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;
944 case EL_SPACESHIP_RIGHT:
945 case EL_SPACESHIP_UP:
946 case EL_SPACESHIP_LEFT:
947 case EL_SPACESHIP_DOWN:
949 case EL_BD_BUTTERFLY_RIGHT:
950 case EL_BD_BUTTERFLY_UP:
951 case EL_BD_BUTTERFLY_LEFT:
952 case EL_BD_BUTTERFLY_DOWN:
953 case EL_BD_BUTTERFLY:
954 case EL_BD_FIREFLY_RIGHT:
955 case EL_BD_FIREFLY_UP:
956 case EL_BD_FIREFLY_LEFT:
957 case EL_BD_FIREFLY_DOWN:
959 case EL_PACMAN_RIGHT:
983 if (y == lev_fieldy - 1)
985 Feld[x][y] = EL_AMOEBA_GROWING;
986 Store[x][y] = EL_AMOEBA_WET;
990 case EL_DYNAMITE_ACTIVE:
991 case EL_SP_DISK_RED_ACTIVE:
992 case EL_DYNABOMB_PLAYER_1_ACTIVE:
993 case EL_DYNABOMB_PLAYER_2_ACTIVE:
994 case EL_DYNABOMB_PLAYER_3_ACTIVE:
995 case EL_DYNABOMB_PLAYER_4_ACTIVE:
999 case EL_EM_DYNAMITE_ACTIVE:
1000 MovDelay[x][y] = 32;
1004 local_player->lights_still_needed++;
1008 local_player->friends_still_needed++;
1013 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1016 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1017 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1018 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1019 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1020 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1021 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1022 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1023 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1024 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1025 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1026 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1027 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1030 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1031 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1032 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1034 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1036 game.belt_dir[belt_nr] = belt_dir;
1037 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1039 else /* more than one switch -- set it like the first switch */
1041 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1046 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1048 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1051 case EL_LIGHT_SWITCH_ACTIVE:
1053 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1056 case EL_EMC_MAGIC_BALL:
1057 if (game.ball_state)
1058 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1061 case EL_EMC_MAGIC_BALL_SWITCH:
1062 if (game.ball_state)
1063 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1068 if (IS_CUSTOM_ELEMENT(element))
1070 if (CAN_MOVE(element))
1073 #if USE_NEW_CUSTOM_VALUE
1074 if (!element_info[element].use_last_ce_value || init_game)
1075 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1079 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1082 else if (IS_GROUP_ELEMENT(element))
1084 struct ElementGroupInfo *group = element_info[element].group;
1085 int last_anim_random_frame = gfx.anim_random_frame;
1088 if (group->choice_mode == ANIM_RANDOM)
1089 gfx.anim_random_frame = RND(group->num_elements_resolved);
1091 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1092 group->choice_mode, 0,
1095 if (group->choice_mode == ANIM_RANDOM)
1096 gfx.anim_random_frame = last_anim_random_frame;
1098 group->choice_pos++;
1100 Feld[x][y] = group->element_resolved[element_pos];
1102 InitField(x, y, init_game);
1109 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1114 #if USE_NEW_CUSTOM_VALUE
1117 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1119 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1127 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1129 InitField(x, y, init_game);
1131 /* not needed to call InitMovDir() -- already done by InitField()! */
1132 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1133 CAN_MOVE(Feld[x][y]))
1137 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1139 int old_element = Feld[x][y];
1141 InitField(x, y, init_game);
1143 /* not needed to call InitMovDir() -- already done by InitField()! */
1144 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1145 CAN_MOVE(old_element) &&
1146 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1149 /* this case is in fact a combination of not less than three bugs:
1150 first, it calls InitMovDir() for elements that can move, although this is
1151 already done by InitField(); then, it checks the element that was at this
1152 field _before_ the call to InitField() (which can change it); lastly, it
1153 was not called for "mole with direction" elements, which were treated as
1154 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1158 inline void DrawGameValue_Emeralds(int value)
1160 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1163 inline void DrawGameValue_Dynamite(int value)
1165 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1168 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1172 /* currently only 4 of 8 possible keys are displayed */
1173 for (i = 0; i < STD_NUM_KEYS; i++)
1176 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1177 el2edimg(EL_KEY_1 + i));
1179 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1180 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1181 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1185 inline void DrawGameValue_Score(int value)
1187 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1190 inline void DrawGameValue_Time(int value)
1193 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1195 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1198 inline void DrawGameValue_Level(int value)
1201 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1204 /* misuse area for displaying emeralds to draw bigger level number */
1205 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1206 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1208 /* now copy it to the area for displaying level number */
1209 BlitBitmap(drawto, drawto,
1210 DX_EMERALDS, DY_EMERALDS + 1,
1211 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1212 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1213 DX_LEVEL - 1, DY_LEVEL + 1);
1215 /* restore the area for displaying emeralds */
1216 DrawGameValue_Emeralds(local_player->gems_still_needed);
1218 /* yes, this is all really ugly :-) */
1222 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1225 int key[MAX_NUM_KEYS];
1228 for (i = 0; i < MAX_NUM_KEYS; i++)
1229 key[i] = key_bits & (1 << i);
1231 DrawGameValue_Level(level_nr);
1233 DrawGameValue_Emeralds(emeralds);
1234 DrawGameValue_Dynamite(dynamite);
1235 DrawGameValue_Score(score);
1236 DrawGameValue_Time(time);
1238 DrawGameValue_Keys(key);
1241 void DrawGameDoorValues()
1245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1247 DrawGameDoorValues_EM();
1252 DrawGameValue_Level(level_nr);
1254 DrawGameValue_Emeralds(local_player->gems_still_needed);
1255 DrawGameValue_Dynamite(local_player->inventory_size);
1256 DrawGameValue_Score(local_player->score);
1257 DrawGameValue_Time(TimeLeft);
1259 for (i = 0; i < MAX_PLAYERS; i++)
1260 DrawGameValue_Keys(stored_player[i].key);
1264 static void resolve_group_element(int group_element, int recursion_depth)
1266 static int group_nr;
1267 static struct ElementGroupInfo *group;
1268 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1271 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1273 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1274 group_element - EL_GROUP_START + 1);
1276 /* replace element which caused too deep recursion by question mark */
1277 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1282 if (recursion_depth == 0) /* initialization */
1284 group = element_info[group_element].group;
1285 group_nr = group_element - EL_GROUP_START;
1287 group->num_elements_resolved = 0;
1288 group->choice_pos = 0;
1291 for (i = 0; i < actual_group->num_elements; i++)
1293 int element = actual_group->element[i];
1295 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1298 if (IS_GROUP_ELEMENT(element))
1299 resolve_group_element(element, recursion_depth + 1);
1302 group->element_resolved[group->num_elements_resolved++] = element;
1303 element_info[element].in_group[group_nr] = TRUE;
1310 =============================================================================
1312 -----------------------------------------------------------------------------
1313 initialize game engine due to level / tape version number
1314 =============================================================================
1317 static void InitGameEngine()
1319 int i, j, k, l, x, y;
1321 /* set game engine from tape file when re-playing, else from level file */
1322 game.engine_version = (tape.playing ? tape.engine_version :
1323 level.game_version);
1325 /* ---------------------------------------------------------------------- */
1326 /* set flags for bugs and changes according to active game engine version */
1327 /* ---------------------------------------------------------------------- */
1330 Summary of bugfix/change:
1331 Fixed handling for custom elements that change when pushed by the player.
1333 Fixed/changed in version:
1337 Before 3.1.0, custom elements that "change when pushing" changed directly
1338 after the player started pushing them (until then handled in "DigField()").
1339 Since 3.1.0, these custom elements are not changed until the "pushing"
1340 move of the element is finished (now handled in "ContinueMoving()").
1342 Affected levels/tapes:
1343 The first condition is generally needed for all levels/tapes before version
1344 3.1.0, which might use the old behaviour before it was changed; known tapes
1345 that are affected are some tapes from the level set "Walpurgis Gardens" by
1347 The second condition is an exception from the above case and is needed for
1348 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1349 above (including some development versions of 3.1.0), but before it was
1350 known that this change would break tapes like the above and was fixed in
1351 3.1.1, so that the changed behaviour was active although the engine version
1352 while recording maybe was before 3.1.0. There is at least one tape that is
1353 affected by this exception, which is the tape for the one-level set "Bug
1354 Machine" by Juergen Bonhagen.
1357 game.use_change_when_pushing_bug =
1358 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1360 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1361 tape.game_version < VERSION_IDENT(3,1,1,0)));
1364 Summary of bugfix/change:
1365 Fixed handling for blocking the field the player leaves when moving.
1367 Fixed/changed in version:
1371 Before 3.1.1, when "block last field when moving" was enabled, the field
1372 the player is leaving when moving was blocked for the time of the move,
1373 and was directly unblocked afterwards. This resulted in the last field
1374 being blocked for exactly one less than the number of frames of one player
1375 move. Additionally, even when blocking was disabled, the last field was
1376 blocked for exactly one frame.
1377 Since 3.1.1, due to changes in player movement handling, the last field
1378 is not blocked at all when blocking is disabled. When blocking is enabled,
1379 the last field is blocked for exactly the number of frames of one player
1380 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1381 last field is blocked for exactly one more than the number of frames of
1384 Affected levels/tapes:
1385 (!!! yet to be determined -- probably many !!!)
1388 game.use_block_last_field_bug =
1389 (game.engine_version < VERSION_IDENT(3,1,1,0));
1392 Summary of bugfix/change:
1393 Changed behaviour of CE changes with multiple changes per single frame.
1395 Fixed/changed in version:
1399 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1400 This resulted in race conditions where CEs seem to behave strange in some
1401 situations (where triggered CE changes were just skipped because there was
1402 already a CE change on that tile in the playfield in that engine frame).
1403 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1404 (The number of changes per frame must be limited in any case, because else
1405 it is easily possible to define CE changes that would result in an infinite
1406 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1407 should be set large enough so that it would only be reached in cases where
1408 the corresponding CE change conditions run into a loop. Therefore, it seems
1409 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1410 maximal number of change pages for custom elements.)
1412 Affected levels/tapes:
1416 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1417 game.max_num_changes_per_frame = 1;
1419 game.max_num_changes_per_frame =
1420 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1423 /* ---------------------------------------------------------------------- */
1425 /* default scan direction: scan playfield from top/left to bottom/right */
1426 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1428 /* dynamically adjust element properties according to game engine version */
1429 InitElementPropertiesEngine(game.engine_version);
1432 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1433 printf(" tape version == %06d [%s] [file: %06d]\n",
1434 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1436 printf(" => game.engine_version == %06d\n", game.engine_version);
1440 /* ---------- recursively resolve group elements ------------------------- */
1442 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1443 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1444 element_info[i].in_group[j] = FALSE;
1446 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1447 resolve_group_element(EL_GROUP_START + i, 0);
1450 /* ---------- initialize player's initial move delay --------------------- */
1453 /* dynamically adjust player properties according to level information */
1454 game.initial_move_delay_value =
1455 get_move_delay_from_stepsize(level.initial_player_stepsize);
1457 /* dynamically adjust player properties according to level information */
1458 game.initial_move_delay_value =
1459 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1462 /* dynamically adjust player properties according to game engine version */
1463 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1464 game.initial_move_delay_value : 0);
1466 /* ---------- initialize player's initial push delay --------------------- */
1468 /* dynamically adjust player properties according to game engine version */
1469 game.initial_push_delay_value =
1470 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1472 /* ---------- initialize changing elements ------------------------------- */
1474 /* initialize changing elements information */
1475 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1477 struct ElementInfo *ei = &element_info[i];
1479 /* this pointer might have been changed in the level editor */
1480 ei->change = &ei->change_page[0];
1482 if (!IS_CUSTOM_ELEMENT(i))
1484 ei->change->target_element = EL_EMPTY_SPACE;
1485 ei->change->delay_fixed = 0;
1486 ei->change->delay_random = 0;
1487 ei->change->delay_frames = 1;
1490 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1492 ei->has_change_event[j] = FALSE;
1494 ei->event_page_nr[j] = 0;
1495 ei->event_page[j] = &ei->change_page[0];
1499 /* add changing elements from pre-defined list */
1500 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1502 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1503 struct ElementInfo *ei = &element_info[ch_delay->element];
1505 ei->change->target_element = ch_delay->target_element;
1506 ei->change->delay_fixed = ch_delay->change_delay;
1508 ei->change->pre_change_function = ch_delay->pre_change_function;
1509 ei->change->change_function = ch_delay->change_function;
1510 ei->change->post_change_function = ch_delay->post_change_function;
1512 ei->change->can_change = TRUE;
1513 ei->change->can_change_or_has_action = TRUE;
1515 ei->has_change_event[CE_DELAY] = TRUE;
1517 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1518 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1521 /* ---------- initialize internal run-time variables ------------- */
1523 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1525 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1527 for (j = 0; j < ei->num_change_pages; j++)
1529 ei->change_page[j].can_change_or_has_action =
1530 (ei->change_page[j].can_change |
1531 ei->change_page[j].has_action);
1535 /* add change events from custom element configuration */
1536 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1538 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1540 for (j = 0; j < ei->num_change_pages; j++)
1542 if (!ei->change_page[j].can_change_or_has_action)
1545 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1547 /* only add event page for the first page found with this event */
1548 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1550 ei->has_change_event[k] = TRUE;
1552 ei->event_page_nr[k] = j;
1553 ei->event_page[k] = &ei->change_page[j];
1559 /* ---------- initialize run-time trigger player and element ------------- */
1561 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1563 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1565 for (j = 0; j < ei->num_change_pages; j++)
1567 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1568 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1569 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1570 ei->change_page[j].actual_trigger_ce_value = 0;
1574 /* ---------- initialize trigger events ---------------------------------- */
1576 /* initialize trigger events information */
1577 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1578 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1579 trigger_events[i][j] = FALSE;
1581 /* add trigger events from element change event properties */
1582 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1584 struct ElementInfo *ei = &element_info[i];
1586 for (j = 0; j < ei->num_change_pages; j++)
1588 if (!ei->change_page[j].can_change_or_has_action)
1591 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1593 int trigger_element = ei->change_page[j].trigger_element;
1595 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1597 if (ei->change_page[j].has_event[k])
1599 if (IS_GROUP_ELEMENT(trigger_element))
1601 struct ElementGroupInfo *group =
1602 element_info[trigger_element].group;
1604 for (l = 0; l < group->num_elements_resolved; l++)
1605 trigger_events[group->element_resolved[l]][k] = TRUE;
1608 trigger_events[trigger_element][k] = TRUE;
1615 /* ---------- initialize push delay -------------------------------------- */
1617 /* initialize push delay values to default */
1618 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 if (!IS_CUSTOM_ELEMENT(i))
1622 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1623 element_info[i].push_delay_random = game.default_push_delay_random;
1627 /* set push delay value for certain elements from pre-defined list */
1628 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1630 int e = push_delay_list[i].element;
1632 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1633 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1636 /* set push delay value for Supaplex elements for newer engine versions */
1637 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1639 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1641 if (IS_SP_ELEMENT(i))
1643 /* set SP push delay to just enough to push under a falling zonk */
1644 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1646 element_info[i].push_delay_fixed = delay;
1647 element_info[i].push_delay_random = 0;
1652 /* ---------- initialize move stepsize ----------------------------------- */
1654 /* initialize move stepsize values to default */
1655 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1656 if (!IS_CUSTOM_ELEMENT(i))
1657 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1659 /* set move stepsize value for certain elements from pre-defined list */
1660 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1662 int e = move_stepsize_list[i].element;
1664 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1667 /* ---------- initialize collect score ----------------------------------- */
1669 /* initialize collect score values for custom elements from initial value */
1670 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1671 if (IS_CUSTOM_ELEMENT(i))
1672 element_info[i].collect_score = element_info[i].collect_score_initial;
1674 /* ---------- initialize collect count ----------------------------------- */
1676 /* initialize collect count values for non-custom elements */
1677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1678 if (!IS_CUSTOM_ELEMENT(i))
1679 element_info[i].collect_count_initial = 0;
1681 /* add collect count values for all elements from pre-defined list */
1682 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1683 element_info[collect_count_list[i].element].collect_count_initial =
1684 collect_count_list[i].count;
1686 /* ---------- initialize access direction -------------------------------- */
1688 /* initialize access direction values to default (access from every side) */
1689 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1690 if (!IS_CUSTOM_ELEMENT(i))
1691 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1693 /* set access direction value for certain elements from pre-defined list */
1694 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1695 element_info[access_direction_list[i].element].access_direction =
1696 access_direction_list[i].direction;
1698 /* ---------- initialize explosion content ------------------------------- */
1699 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1701 if (IS_CUSTOM_ELEMENT(i))
1704 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1706 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1708 element_info[i].content.e[x][y] =
1709 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1710 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1711 i == EL_PLAYER_3 ? EL_EMERALD :
1712 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1713 i == EL_MOLE ? EL_EMERALD_RED :
1714 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1715 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1716 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1717 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1718 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1719 i == EL_WALL_EMERALD ? EL_EMERALD :
1720 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1721 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1722 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1723 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1724 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1725 i == EL_WALL_PEARL ? EL_PEARL :
1726 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1732 int get_num_special_action(int element, int action_first, int action_last)
1734 int num_special_action = 0;
1737 for (i = action_first; i <= action_last; i++)
1739 boolean found = FALSE;
1741 for (j = 0; j < NUM_DIRECTIONS; j++)
1742 if (el_act_dir2img(element, i, j) !=
1743 el_act_dir2img(element, ACTION_DEFAULT, j))
1747 num_special_action++;
1752 return num_special_action;
1756 =============================================================================
1758 -----------------------------------------------------------------------------
1759 initialize and start new game
1760 =============================================================================
1765 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1766 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1767 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1772 /* don't play tapes over network */
1773 network_playing = (options.network && !tape.playing);
1775 for (i = 0; i < MAX_PLAYERS; i++)
1777 struct PlayerInfo *player = &stored_player[i];
1779 player->index_nr = i;
1780 player->index_bit = (1 << i);
1781 player->element_nr = EL_PLAYER_1 + i;
1783 player->present = FALSE;
1784 player->active = FALSE;
1787 player->effective_action = 0;
1788 player->programmed_action = 0;
1791 player->gems_still_needed = level.gems_needed;
1792 player->sokobanfields_still_needed = 0;
1793 player->lights_still_needed = 0;
1794 player->friends_still_needed = 0;
1796 for (j = 0; j < MAX_NUM_KEYS; j++)
1797 player->key[j] = FALSE;
1799 player->dynabomb_count = 0;
1800 player->dynabomb_size = 1;
1801 player->dynabombs_left = 0;
1802 player->dynabomb_xl = FALSE;
1804 player->MovDir = MV_NONE;
1807 player->GfxDir = MV_NONE;
1808 player->GfxAction = ACTION_DEFAULT;
1810 player->StepFrame = 0;
1812 player->use_murphy = FALSE;
1813 player->artwork_element =
1814 (level.use_artwork_element[i] ? level.artwork_element[i] :
1815 player->element_nr);
1817 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1818 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1820 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1822 player->actual_frame_counter = 0;
1824 player->step_counter = 0;
1826 player->last_move_dir = MV_NONE;
1828 player->is_waiting = FALSE;
1829 player->is_moving = FALSE;
1830 player->is_auto_moving = FALSE;
1831 player->is_digging = FALSE;
1832 player->is_snapping = FALSE;
1833 player->is_collecting = FALSE;
1834 player->is_pushing = FALSE;
1835 player->is_switching = FALSE;
1836 player->is_dropping = FALSE;
1837 player->is_dropping_pressed = FALSE;
1839 player->is_bored = FALSE;
1840 player->is_sleeping = FALSE;
1842 player->frame_counter_bored = -1;
1843 player->frame_counter_sleeping = -1;
1845 player->anim_delay_counter = 0;
1846 player->post_delay_counter = 0;
1848 player->action_waiting = ACTION_DEFAULT;
1849 player->last_action_waiting = ACTION_DEFAULT;
1850 player->special_action_bored = ACTION_DEFAULT;
1851 player->special_action_sleeping = ACTION_DEFAULT;
1853 /* set number of special actions for bored and sleeping animation */
1854 player->num_special_action_bored =
1855 get_num_special_action(player->artwork_element,
1856 ACTION_BORING_1, ACTION_BORING_LAST);
1857 player->num_special_action_sleeping =
1858 get_num_special_action(player->artwork_element,
1859 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1861 player->switch_x = -1;
1862 player->switch_y = -1;
1864 player->drop_x = -1;
1865 player->drop_y = -1;
1867 player->show_envelope = 0;
1870 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1872 player->move_delay = game.initial_move_delay;
1873 player->move_delay_value = game.initial_move_delay_value;
1875 player->move_delay_value_next = -1;
1877 player->move_delay_reset_counter = 0;
1879 player->cannot_move = FALSE;
1882 player->push_delay = -1; /* initialized when pushing starts */
1883 player->push_delay_value = game.initial_push_delay_value;
1885 player->drop_delay = 0;
1886 player->drop_pressed_delay = 0;
1888 player->last_jx = player->last_jy = 0;
1889 player->jx = player->jy = 0;
1891 player->shield_normal_time_left = 0;
1892 player->shield_deadly_time_left = 0;
1894 player->inventory_infinite_element = EL_UNDEFINED;
1895 player->inventory_size = 0;
1897 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1898 SnapField(player, 0, 0);
1900 player->LevelSolved = FALSE;
1901 player->GameOver = FALSE;
1904 network_player_action_received = FALSE;
1906 #if defined(NETWORK_AVALIABLE)
1907 /* initial null action */
1908 if (network_playing)
1909 SendToServer_MovePlayer(MV_NONE);
1918 TimeLeft = level.time;
1921 ScreenMovDir = MV_NONE;
1925 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1927 AllPlayersGone = FALSE;
1929 game.yamyam_content_nr = 0;
1930 game.magic_wall_active = FALSE;
1931 game.magic_wall_time_left = 0;
1932 game.light_time_left = 0;
1933 game.timegate_time_left = 0;
1934 game.switchgate_pos = 0;
1935 game.wind_direction = level.wind_direction_initial;
1936 game.gravity = level.initial_gravity;
1937 game.explosions_delayed = TRUE;
1939 game.lenses_time_left = 0;
1940 game.magnify_time_left = 0;
1942 game.ball_state = level.ball_state_initial;
1943 game.ball_content_nr = 0;
1945 game.envelope_active = FALSE;
1947 game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
1949 for (i = 0; i < NUM_BELTS; i++)
1951 game.belt_dir[i] = MV_NONE;
1952 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1955 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1956 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1959 SCAN_PLAYFIELD(x, y)
1961 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1964 Feld[x][y] = level.field[x][y];
1965 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1966 ChangeDelay[x][y] = 0;
1967 ChangePage[x][y] = -1;
1968 #if USE_NEW_CUSTOM_VALUE
1969 CustomValue[x][y] = 0; /* initialized in InitField() */
1971 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1973 WasJustMoving[x][y] = 0;
1974 WasJustFalling[x][y] = 0;
1975 CheckCollision[x][y] = 0;
1977 Pushed[x][y] = FALSE;
1979 ChangeCount[x][y] = 0;
1980 ChangeEvent[x][y] = -1;
1982 ExplodePhase[x][y] = 0;
1983 ExplodeDelay[x][y] = 0;
1984 ExplodeField[x][y] = EX_TYPE_NONE;
1986 RunnerVisit[x][y] = 0;
1987 PlayerVisit[x][y] = 0;
1990 GfxRandom[x][y] = INIT_GFX_RANDOM();
1991 GfxElement[x][y] = EL_UNDEFINED;
1992 GfxAction[x][y] = ACTION_DEFAULT;
1993 GfxDir[x][y] = MV_NONE;
1997 SCAN_PLAYFIELD(x, y)
1999 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2002 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2004 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2006 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2009 InitField(x, y, TRUE);
2014 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2015 emulate_sb ? EMU_SOKOBAN :
2016 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2018 #if USE_NEW_ALL_SLIPPERY
2019 /* initialize type of slippery elements */
2020 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2022 if (!IS_CUSTOM_ELEMENT(i))
2024 /* default: elements slip down either to the left or right randomly */
2025 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2027 /* SP style elements prefer to slip down on the left side */
2028 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2029 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2031 /* BD style elements prefer to slip down on the left side */
2032 if (game.emulation == EMU_BOULDERDASH)
2033 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2038 /* initialize explosion and ignition delay */
2039 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2041 if (!IS_CUSTOM_ELEMENT(i))
2044 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2045 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2046 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2047 int last_phase = (num_phase + 1) * delay;
2048 int half_phase = (num_phase / 2) * delay;
2050 element_info[i].explosion_delay = last_phase - 1;
2051 element_info[i].ignition_delay = half_phase;
2053 if (i == EL_BLACK_ORB)
2054 element_info[i].ignition_delay = 1;
2058 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2059 element_info[i].explosion_delay = 1;
2061 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2062 element_info[i].ignition_delay = 1;
2066 /* correct non-moving belts to start moving left */
2067 for (i = 0; i < NUM_BELTS; i++)
2068 if (game.belt_dir[i] == MV_NONE)
2069 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2071 /* check if any connected player was not found in playfield */
2072 for (i = 0; i < MAX_PLAYERS; i++)
2074 struct PlayerInfo *player = &stored_player[i];
2076 if (player->connected && !player->present)
2078 for (j = 0; j < MAX_PLAYERS; j++)
2080 struct PlayerInfo *some_player = &stored_player[j];
2081 int jx = some_player->jx, jy = some_player->jy;
2083 /* assign first free player found that is present in the playfield */
2084 if (some_player->present && !some_player->connected)
2086 player->present = TRUE;
2087 player->active = TRUE;
2089 some_player->present = FALSE;
2090 some_player->active = FALSE;
2093 player->element_nr = some_player->element_nr;
2096 player->artwork_element = some_player->artwork_element;
2098 player->block_last_field = some_player->block_last_field;
2099 player->block_delay_adjustment = some_player->block_delay_adjustment;
2101 StorePlayer[jx][jy] = player->element_nr;
2102 player->jx = player->last_jx = jx;
2103 player->jy = player->last_jy = jy;
2113 /* when playing a tape, eliminate all players who do not participate */
2115 for (i = 0; i < MAX_PLAYERS; i++)
2117 if (stored_player[i].active && !tape.player_participates[i])
2119 struct PlayerInfo *player = &stored_player[i];
2120 int jx = player->jx, jy = player->jy;
2122 player->active = FALSE;
2123 StorePlayer[jx][jy] = 0;
2124 Feld[jx][jy] = EL_EMPTY;
2128 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2130 /* when in single player mode, eliminate all but the first active player */
2132 for (i = 0; i < MAX_PLAYERS; i++)
2134 if (stored_player[i].active)
2136 for (j = i + 1; j < MAX_PLAYERS; j++)
2138 if (stored_player[j].active)
2140 struct PlayerInfo *player = &stored_player[j];
2141 int jx = player->jx, jy = player->jy;
2143 player->active = FALSE;
2144 player->present = FALSE;
2146 StorePlayer[jx][jy] = 0;
2147 Feld[jx][jy] = EL_EMPTY;
2154 /* when recording the game, store which players take part in the game */
2157 for (i = 0; i < MAX_PLAYERS; i++)
2158 if (stored_player[i].active)
2159 tape.player_participates[i] = TRUE;
2164 for (i = 0; i < MAX_PLAYERS; i++)
2166 struct PlayerInfo *player = &stored_player[i];
2168 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2173 if (local_player == player)
2174 printf("Player %d is local player.\n", i+1);
2178 if (BorderElement == EL_EMPTY)
2181 SBX_Right = lev_fieldx - SCR_FIELDX;
2183 SBY_Lower = lev_fieldy - SCR_FIELDY;
2188 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2190 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2193 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2194 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2196 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2197 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2199 /* if local player not found, look for custom element that might create
2200 the player (make some assumptions about the right custom element) */
2201 if (!local_player->present)
2203 int start_x = 0, start_y = 0;
2204 int found_rating = 0;
2205 int found_element = EL_UNDEFINED;
2206 int player_nr = local_player->index_nr;
2209 SCAN_PLAYFIELD(x, y)
2211 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2214 int element = Feld[x][y];
2219 if (level.use_start_element[player_nr] &&
2220 level.start_element[player_nr] == element &&
2227 found_element = element;
2230 if (!IS_CUSTOM_ELEMENT(element))
2233 if (CAN_CHANGE(element))
2235 for (i = 0; i < element_info[element].num_change_pages; i++)
2237 /* check for player created from custom element as single target */
2238 content = element_info[element].change_page[i].target_element;
2239 is_player = ELEM_IS_PLAYER(content);
2241 if (is_player && (found_rating < 3 || element < found_element))
2247 found_element = element;
2252 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2254 /* check for player created from custom element as explosion content */
2255 content = element_info[element].content.e[xx][yy];
2256 is_player = ELEM_IS_PLAYER(content);
2258 if (is_player && (found_rating < 2 || element < found_element))
2260 start_x = x + xx - 1;
2261 start_y = y + yy - 1;
2264 found_element = element;
2267 if (!CAN_CHANGE(element))
2270 for (i = 0; i < element_info[element].num_change_pages; i++)
2272 /* check for player created from custom element as extended target */
2274 element_info[element].change_page[i].target_content.e[xx][yy];
2276 is_player = ELEM_IS_PLAYER(content);
2278 if (is_player && (found_rating < 1 || element < found_element))
2280 start_x = x + xx - 1;
2281 start_y = y + yy - 1;
2284 found_element = element;
2290 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2291 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2294 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2295 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2300 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2301 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2302 local_player->jx - MIDPOSX);
2304 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2305 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2306 local_player->jy - MIDPOSY);
2309 if (!game.restart_level)
2310 CloseDoor(DOOR_CLOSE_1);
2312 /* !!! FIX THIS (START) !!! */
2313 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2315 InitGameEngine_EM();
2322 /* after drawing the level, correct some elements */
2323 if (game.timegate_time_left == 0)
2324 CloseAllOpenTimegates();
2326 if (setup.soft_scrolling)
2327 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2329 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2332 /* !!! FIX THIS (END) !!! */
2334 if (!game.restart_level)
2336 /* copy default game door content to main double buffer */
2337 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2338 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2341 DrawGameDoorValues();
2343 if (!game.restart_level)
2347 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2348 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2349 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2353 /* copy actual game door content to door double buffer for OpenDoor() */
2354 BlitBitmap(drawto, bitmap_db_door,
2355 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2357 OpenDoor(DOOR_OPEN_ALL);
2359 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2361 if (setup.sound_music)
2364 KeyboardAutoRepeatOffUnlessAutoplay();
2368 for (i = 0; i < MAX_PLAYERS; i++)
2369 printf("Player %d %sactive.\n",
2370 i + 1, (stored_player[i].active ? "" : "not "));
2374 game.restart_level = FALSE;
2377 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2379 /* this is used for non-R'n'D game engines to update certain engine values */
2381 /* needed to determine if sounds are played within the visible screen area */
2382 scroll_x = actual_scroll_x;
2383 scroll_y = actual_scroll_y;
2386 void InitMovDir(int x, int y)
2388 int i, element = Feld[x][y];
2389 static int xy[4][2] =
2396 static int direction[3][4] =
2398 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2399 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2400 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2409 Feld[x][y] = EL_BUG;
2410 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2413 case EL_SPACESHIP_RIGHT:
2414 case EL_SPACESHIP_UP:
2415 case EL_SPACESHIP_LEFT:
2416 case EL_SPACESHIP_DOWN:
2417 Feld[x][y] = EL_SPACESHIP;
2418 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2421 case EL_BD_BUTTERFLY_RIGHT:
2422 case EL_BD_BUTTERFLY_UP:
2423 case EL_BD_BUTTERFLY_LEFT:
2424 case EL_BD_BUTTERFLY_DOWN:
2425 Feld[x][y] = EL_BD_BUTTERFLY;
2426 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2429 case EL_BD_FIREFLY_RIGHT:
2430 case EL_BD_FIREFLY_UP:
2431 case EL_BD_FIREFLY_LEFT:
2432 case EL_BD_FIREFLY_DOWN:
2433 Feld[x][y] = EL_BD_FIREFLY;
2434 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2437 case EL_PACMAN_RIGHT:
2439 case EL_PACMAN_LEFT:
2440 case EL_PACMAN_DOWN:
2441 Feld[x][y] = EL_PACMAN;
2442 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2445 case EL_SP_SNIKSNAK:
2446 MovDir[x][y] = MV_UP;
2449 case EL_SP_ELECTRON:
2450 MovDir[x][y] = MV_LEFT;
2457 Feld[x][y] = EL_MOLE;
2458 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2462 if (IS_CUSTOM_ELEMENT(element))
2464 struct ElementInfo *ei = &element_info[element];
2465 int move_direction_initial = ei->move_direction_initial;
2466 int move_pattern = ei->move_pattern;
2468 if (move_direction_initial == MV_START_PREVIOUS)
2470 if (MovDir[x][y] != MV_NONE)
2473 move_direction_initial = MV_START_AUTOMATIC;
2476 if (move_direction_initial == MV_START_RANDOM)
2477 MovDir[x][y] = 1 << RND(4);
2478 else if (move_direction_initial & MV_ANY_DIRECTION)
2479 MovDir[x][y] = move_direction_initial;
2480 else if (move_pattern == MV_ALL_DIRECTIONS ||
2481 move_pattern == MV_TURNING_LEFT ||
2482 move_pattern == MV_TURNING_RIGHT ||
2483 move_pattern == MV_TURNING_LEFT_RIGHT ||
2484 move_pattern == MV_TURNING_RIGHT_LEFT ||
2485 move_pattern == MV_TURNING_RANDOM)
2486 MovDir[x][y] = 1 << RND(4);
2487 else if (move_pattern == MV_HORIZONTAL)
2488 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2489 else if (move_pattern == MV_VERTICAL)
2490 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2491 else if (move_pattern & MV_ANY_DIRECTION)
2492 MovDir[x][y] = element_info[element].move_pattern;
2493 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2494 move_pattern == MV_ALONG_RIGHT_SIDE)
2496 /* use random direction as default start direction */
2497 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2498 MovDir[x][y] = 1 << RND(4);
2500 for (i = 0; i < NUM_DIRECTIONS; i++)
2502 int x1 = x + xy[i][0];
2503 int y1 = y + xy[i][1];
2505 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2507 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2508 MovDir[x][y] = direction[0][i];
2510 MovDir[x][y] = direction[1][i];
2519 MovDir[x][y] = 1 << RND(4);
2521 if (element != EL_BUG &&
2522 element != EL_SPACESHIP &&
2523 element != EL_BD_BUTTERFLY &&
2524 element != EL_BD_FIREFLY)
2527 for (i = 0; i < NUM_DIRECTIONS; i++)
2529 int x1 = x + xy[i][0];
2530 int y1 = y + xy[i][1];
2532 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2534 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2536 MovDir[x][y] = direction[0][i];
2539 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2540 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2542 MovDir[x][y] = direction[1][i];
2551 GfxDir[x][y] = MovDir[x][y];
2554 void InitAmoebaNr(int x, int y)
2557 int group_nr = AmoebeNachbarNr(x, y);
2561 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2563 if (AmoebaCnt[i] == 0)
2571 AmoebaNr[x][y] = group_nr;
2572 AmoebaCnt[group_nr]++;
2573 AmoebaCnt2[group_nr]++;
2579 boolean raise_level = FALSE;
2581 if (local_player->MovPos)
2584 if (tape.auto_play) /* tape might already be stopped here */
2585 tape.auto_play_level_solved = TRUE;
2587 local_player->LevelSolved = FALSE;
2589 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2593 if (!tape.playing && setup.sound_loops)
2594 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2595 SND_CTRL_PLAY_LOOP);
2597 while (TimeLeft > 0)
2599 if (!tape.playing && !setup.sound_loops)
2600 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2602 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2605 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2610 RaiseScore(level.score[SC_TIME_BONUS]);
2613 DrawGameValue_Time(TimeLeft);
2621 if (!tape.playing && setup.sound_loops)
2622 StopSound(SND_GAME_LEVELTIME_BONUS);
2624 else if (level.time == 0) /* level without time limit */
2626 if (!tape.playing && setup.sound_loops)
2627 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2628 SND_CTRL_PLAY_LOOP);
2630 while (TimePlayed < 999)
2632 if (!tape.playing && !setup.sound_loops)
2633 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2635 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2638 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2643 RaiseScore(level.score[SC_TIME_BONUS]);
2646 DrawGameValue_Time(TimePlayed);
2654 if (!tape.playing && setup.sound_loops)
2655 StopSound(SND_GAME_LEVELTIME_BONUS);
2658 /* close exit door after last player */
2659 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2660 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2661 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2663 int element = Feld[ExitX][ExitY];
2665 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2666 EL_SP_EXIT_CLOSING);
2668 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2671 /* player disappears */
2672 if (ExitX >= 0 && ExitY >= 0)
2673 DrawLevelField(ExitX, ExitY);
2679 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2681 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2687 CloseDoor(DOOR_CLOSE_1);
2692 SaveTape(tape.level_nr); /* Ask to save tape */
2695 if (level_nr == leveldir_current->handicap_level)
2697 leveldir_current->handicap_level++;
2698 SaveLevelSetup_SeriesInfo();
2701 if (level_editor_test_game)
2702 local_player->score = -1; /* no highscore when playing from editor */
2703 else if (level_nr < leveldir_current->last_level)
2704 raise_level = TRUE; /* advance to next level */
2706 if ((hi_pos = NewHiScore()) >= 0)
2708 game_status = GAME_MODE_SCORES;
2709 DrawHallOfFame(hi_pos);
2718 game_status = GAME_MODE_MAIN;
2735 LoadScore(level_nr);
2737 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2738 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2741 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2743 if (local_player->score > highscore[k].Score)
2745 /* player has made it to the hall of fame */
2747 if (k < MAX_SCORE_ENTRIES - 1)
2749 int m = MAX_SCORE_ENTRIES - 1;
2752 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2753 if (!strcmp(setup.player_name, highscore[l].Name))
2755 if (m == k) /* player's new highscore overwrites his old one */
2759 for (l = m; l > k; l--)
2761 strcpy(highscore[l].Name, highscore[l - 1].Name);
2762 highscore[l].Score = highscore[l - 1].Score;
2769 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2770 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2771 highscore[k].Score = local_player->score;
2777 else if (!strncmp(setup.player_name, highscore[k].Name,
2778 MAX_PLAYER_NAME_LEN))
2779 break; /* player already there with a higher score */
2785 SaveScore(level_nr);
2790 inline static int getElementMoveStepsize(int x, int y)
2792 int element = Feld[x][y];
2793 int direction = MovDir[x][y];
2794 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2795 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2796 int horiz_move = (dx != 0);
2797 int sign = (horiz_move ? dx : dy);
2798 int step = sign * element_info[element].move_stepsize;
2800 /* special values for move stepsize for spring and things on conveyor belt */
2804 if (element == EL_SPRING)
2805 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2806 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2807 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2808 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2810 if (CAN_FALL(element) &&
2811 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2812 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2813 else if (element == EL_SPRING)
2814 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2821 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2823 if (player->GfxAction != action || player->GfxDir != dir)
2826 printf("Player frame reset! (%d => %d, %d => %d)\n",
2827 player->GfxAction, action, player->GfxDir, dir);
2830 player->GfxAction = action;
2831 player->GfxDir = dir;
2833 player->StepFrame = 0;
2837 static void ResetRandomAnimationValue(int x, int y)
2839 GfxRandom[x][y] = INIT_GFX_RANDOM();
2842 static void ResetGfxAnimation(int x, int y)
2845 int element, graphic;
2849 GfxAction[x][y] = ACTION_DEFAULT;
2850 GfxDir[x][y] = MovDir[x][y];
2853 element = Feld[x][y];
2854 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2856 if (graphic_info[graphic].anim_global_sync)
2857 GfxFrame[x][y] = FrameCounter;
2858 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2859 GfxFrame[x][y] = CustomValue[x][y];
2860 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2861 GfxFrame[x][y] = element_info[element].collect_score;
2865 void InitMovingField(int x, int y, int direction)
2867 int element = Feld[x][y];
2871 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2872 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2876 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2877 ResetGfxAnimation(x, y);
2879 MovDir[x][y] = direction;
2880 GfxDir[x][y] = direction;
2881 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2882 ACTION_FALLING : ACTION_MOVING);
2885 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2887 if (graphic_info[graphic].anim_global_sync)
2888 GfxFrame[x][y] = FrameCounter;
2889 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2890 GfxFrame[x][y] = CustomValue[x][y];
2891 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2892 GfxFrame[x][y] = element_info[element].collect_score;
2895 /* this is needed for CEs with property "can move" / "not moving" */
2897 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2899 if (Feld[newx][newy] == EL_EMPTY)
2900 Feld[newx][newy] = EL_BLOCKED;
2902 MovDir[newx][newy] = MovDir[x][y];
2904 #if USE_NEW_CUSTOM_VALUE
2905 CustomValue[newx][newy] = CustomValue[x][y];
2908 GfxFrame[newx][newy] = GfxFrame[x][y];
2909 GfxRandom[newx][newy] = GfxRandom[x][y];
2910 GfxAction[newx][newy] = GfxAction[x][y];
2911 GfxDir[newx][newy] = GfxDir[x][y];
2915 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2917 int direction = MovDir[x][y];
2919 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2920 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2922 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2923 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2930 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2932 int oldx = x, oldy = y;
2933 int direction = MovDir[x][y];
2935 if (direction == MV_LEFT)
2937 else if (direction == MV_RIGHT)
2939 else if (direction == MV_UP)
2941 else if (direction == MV_DOWN)
2944 *comes_from_x = oldx;
2945 *comes_from_y = oldy;
2948 int MovingOrBlocked2Element(int x, int y)
2950 int element = Feld[x][y];
2952 if (element == EL_BLOCKED)
2956 Blocked2Moving(x, y, &oldx, &oldy);
2957 return Feld[oldx][oldy];
2963 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2965 /* like MovingOrBlocked2Element(), but if element is moving
2966 and (x,y) is the field the moving element is just leaving,
2967 return EL_BLOCKED instead of the element value */
2968 int element = Feld[x][y];
2970 if (IS_MOVING(x, y))
2972 if (element == EL_BLOCKED)
2976 Blocked2Moving(x, y, &oldx, &oldy);
2977 return Feld[oldx][oldy];
2986 static void RemoveField(int x, int y)
2988 Feld[x][y] = EL_EMPTY;
2994 #if USE_NEW_CUSTOM_VALUE
2995 CustomValue[x][y] = 0;
2999 ChangeDelay[x][y] = 0;
3000 ChangePage[x][y] = -1;
3001 Pushed[x][y] = FALSE;
3004 ExplodeField[x][y] = EX_TYPE_NONE;
3007 GfxElement[x][y] = EL_UNDEFINED;
3008 GfxAction[x][y] = ACTION_DEFAULT;
3009 GfxDir[x][y] = MV_NONE;
3012 void RemoveMovingField(int x, int y)
3014 int oldx = x, oldy = y, newx = x, newy = y;
3015 int element = Feld[x][y];
3016 int next_element = EL_UNDEFINED;
3018 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3021 if (IS_MOVING(x, y))
3023 Moving2Blocked(x, y, &newx, &newy);
3025 if (Feld[newx][newy] != EL_BLOCKED)
3027 /* element is moving, but target field is not free (blocked), but
3028 already occupied by something different (example: acid pool);
3029 in this case, only remove the moving field, but not the target */
3031 RemoveField(oldx, oldy);
3033 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3035 DrawLevelField(oldx, oldy);
3040 else if (element == EL_BLOCKED)
3042 Blocked2Moving(x, y, &oldx, &oldy);
3043 if (!IS_MOVING(oldx, oldy))
3047 if (element == EL_BLOCKED &&
3048 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3049 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3050 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3051 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3052 next_element = get_next_element(Feld[oldx][oldy]);
3054 RemoveField(oldx, oldy);
3055 RemoveField(newx, newy);
3057 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3059 if (next_element != EL_UNDEFINED)
3060 Feld[oldx][oldy] = next_element;
3062 DrawLevelField(oldx, oldy);
3063 DrawLevelField(newx, newy);
3066 void DrawDynamite(int x, int y)
3068 int sx = SCREENX(x), sy = SCREENY(y);
3069 int graphic = el2img(Feld[x][y]);
3072 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3075 if (IS_WALKABLE_INSIDE(Back[x][y]))
3079 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3080 else if (Store[x][y])
3081 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3083 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3085 if (Back[x][y] || Store[x][y])
3086 DrawGraphicThruMask(sx, sy, graphic, frame);
3088 DrawGraphic(sx, sy, graphic, frame);
3091 void CheckDynamite(int x, int y)
3093 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3097 if (MovDelay[x][y] != 0)
3100 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3106 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3111 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3113 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3114 boolean no_delay = (tape.warp_forward);
3115 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3116 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3117 int jx = player->jx;
3118 int jy = player->jy;
3120 if (quick_relocation)
3122 int offset = (setup.scroll_delay ? 3 : 0);
3124 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3126 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3127 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3128 player->jx - MIDPOSX);
3130 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3131 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3132 player->jy - MIDPOSY);
3136 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3137 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3138 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3140 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3141 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3142 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3144 /* don't scroll over playfield boundaries */
3145 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3146 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3148 /* don't scroll over playfield boundaries */
3149 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3150 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3153 RedrawPlayfield(TRUE, 0,0,0,0);
3157 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3158 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3159 player->jx - MIDPOSX);
3161 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3162 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3163 player->jy - MIDPOSY);
3165 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3167 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3170 int fx = FX, fy = FY;
3172 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3173 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3175 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3181 fx += dx * TILEX / 2;
3182 fy += dy * TILEY / 2;
3184 ScrollLevel(dx, dy);
3187 /* scroll in two steps of half tile size to make things smoother */
3188 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3190 Delay(wait_delay_value);
3192 /* scroll second step to align at full tile size */
3194 Delay(wait_delay_value);
3199 Delay(wait_delay_value);
3203 void RelocatePlayer(int jx, int jy, int el_player_raw)
3205 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3206 int player_nr = GET_PLAYER_NR(el_player);
3207 struct PlayerInfo *player = &stored_player[player_nr];
3208 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3209 boolean no_delay = (tape.warp_forward);
3210 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3211 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3212 int old_jx = player->jx;
3213 int old_jy = player->jy;
3214 int old_element = Feld[old_jx][old_jy];
3215 int element = Feld[jx][jy];
3216 boolean player_relocated = (old_jx != jx || old_jy != jy);
3218 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3219 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3220 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3221 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3222 int leave_side_horiz = move_dir_horiz;
3223 int leave_side_vert = move_dir_vert;
3224 int enter_side = enter_side_horiz | enter_side_vert;
3225 int leave_side = leave_side_horiz | leave_side_vert;
3227 if (player->GameOver) /* do not reanimate dead player */
3230 if (!player_relocated) /* no need to relocate the player */
3233 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3235 RemoveField(jx, jy); /* temporarily remove newly placed player */
3236 DrawLevelField(jx, jy);
3239 if (player->present)
3241 while (player->MovPos)
3243 ScrollPlayer(player, SCROLL_GO_ON);
3244 ScrollScreen(NULL, SCROLL_GO_ON);
3246 AdvanceFrameAndPlayerCounters(player->index_nr);
3251 Delay(wait_delay_value);
3254 DrawPlayer(player); /* needed here only to cleanup last field */
3255 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3257 player->is_moving = FALSE;
3260 if (IS_CUSTOM_ELEMENT(old_element))
3261 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3263 player->index_bit, leave_side);
3265 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3267 player->index_bit, leave_side);
3269 Feld[jx][jy] = el_player;
3270 InitPlayerField(jx, jy, el_player, TRUE);
3272 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3274 Feld[jx][jy] = element;
3275 InitField(jx, jy, FALSE);
3279 /* only visually relocate centered player */
3280 if (player->index_nr == game.centered_player_nr)
3281 DrawRelocatePlayer(player, level.instant_relocation);
3283 if (player == local_player) /* only visually relocate local player */
3284 DrawRelocatePlayer(player, level.instant_relocation);
3287 TestIfPlayerTouchesBadThing(jx, jy);
3288 TestIfPlayerTouchesCustomElement(jx, jy);
3290 if (IS_CUSTOM_ELEMENT(element))
3291 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3292 player->index_bit, enter_side);
3294 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3295 player->index_bit, enter_side);
3298 void Explode(int ex, int ey, int phase, int mode)
3304 /* !!! eliminate this variable !!! */
3305 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3307 if (game.explosions_delayed)
3309 ExplodeField[ex][ey] = mode;
3313 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3315 int center_element = Feld[ex][ey];
3316 int artwork_element, explosion_element; /* set these values later */
3319 /* --- This is only really needed (and now handled) in "Impact()". --- */
3320 /* do not explode moving elements that left the explode field in time */
3321 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3322 center_element == EL_EMPTY &&
3323 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3328 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3329 if (mode == EX_TYPE_NORMAL ||
3330 mode == EX_TYPE_CENTER ||
3331 mode == EX_TYPE_CROSS)
3332 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3335 /* remove things displayed in background while burning dynamite */
3336 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3339 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3341 /* put moving element to center field (and let it explode there) */
3342 center_element = MovingOrBlocked2Element(ex, ey);
3343 RemoveMovingField(ex, ey);
3344 Feld[ex][ey] = center_element;
3347 /* now "center_element" is finally determined -- set related values now */
3348 artwork_element = center_element; /* for custom player artwork */
3349 explosion_element = center_element; /* for custom player artwork */
3351 if (IS_PLAYER(ex, ey))
3353 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3355 artwork_element = stored_player[player_nr].artwork_element;
3357 if (level.use_explosion_element[player_nr])
3359 explosion_element = level.explosion_element[player_nr];
3360 artwork_element = explosion_element;
3365 if (mode == EX_TYPE_NORMAL ||
3366 mode == EX_TYPE_CENTER ||
3367 mode == EX_TYPE_CROSS)
3368 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3372 last_phase = element_info[explosion_element].explosion_delay + 1;
3374 last_phase = element_info[center_element].explosion_delay + 1;
3377 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3379 int xx = x - ex + 1;
3380 int yy = y - ey + 1;
3383 if (!IN_LEV_FIELD(x, y) ||
3384 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3385 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3388 element = Feld[x][y];
3390 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3392 element = MovingOrBlocked2Element(x, y);
3394 if (!IS_EXPLOSION_PROOF(element))
3395 RemoveMovingField(x, y);
3398 /* indestructible elements can only explode in center (but not flames) */
3399 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3400 mode == EX_TYPE_BORDER)) ||
3401 element == EL_FLAMES)
3404 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3405 behaviour, for example when touching a yamyam that explodes to rocks
3406 with active deadly shield, a rock is created under the player !!! */
3407 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3409 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3410 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3411 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3413 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3416 if (IS_ACTIVE_BOMB(element))
3418 /* re-activate things under the bomb like gate or penguin */
3419 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3426 /* save walkable background elements while explosion on same tile */
3427 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3428 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3429 Back[x][y] = element;
3431 /* ignite explodable elements reached by other explosion */
3432 if (element == EL_EXPLOSION)
3433 element = Store2[x][y];
3435 if (AmoebaNr[x][y] &&
3436 (element == EL_AMOEBA_FULL ||
3437 element == EL_BD_AMOEBA ||
3438 element == EL_AMOEBA_GROWING))
3440 AmoebaCnt[AmoebaNr[x][y]]--;
3441 AmoebaCnt2[AmoebaNr[x][y]]--;
3446 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3449 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3451 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3453 switch(StorePlayer[ex][ey])
3456 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3459 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3462 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3466 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3471 if (PLAYERINFO(ex, ey)->use_murphy)
3472 Store[x][y] = EL_EMPTY;
3475 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3476 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3477 else if (ELEM_IS_PLAYER(center_element))
3478 Store[x][y] = EL_EMPTY;
3479 else if (center_element == EL_YAMYAM)
3480 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3481 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3482 Store[x][y] = element_info[center_element].content.e[xx][yy];
3484 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3485 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3486 otherwise) -- FIX THIS !!! */
3487 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3488 Store[x][y] = element_info[element].content.e[1][1];
3490 else if (!CAN_EXPLODE(element))
3491 Store[x][y] = element_info[element].content.e[1][1];
3494 Store[x][y] = EL_EMPTY;
3496 else if (center_element == EL_MOLE)
3497 Store[x][y] = EL_EMERALD_RED;
3498 else if (center_element == EL_PENGUIN)
3499 Store[x][y] = EL_EMERALD_PURPLE;
3500 else if (center_element == EL_BUG)
3501 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3502 else if (center_element == EL_BD_BUTTERFLY)
3503 Store[x][y] = EL_BD_DIAMOND;
3504 else if (center_element == EL_SP_ELECTRON)
3505 Store[x][y] = EL_SP_INFOTRON;
3506 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3507 Store[x][y] = level.amoeba_content;
3508 else if (center_element == EL_YAMYAM)
3509 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3510 else if (IS_CUSTOM_ELEMENT(center_element) &&
3511 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3512 Store[x][y] = element_info[center_element].content.e[xx][yy];
3513 else if (element == EL_WALL_EMERALD)
3514 Store[x][y] = EL_EMERALD;
3515 else if (element == EL_WALL_DIAMOND)
3516 Store[x][y] = EL_DIAMOND;
3517 else if (element == EL_WALL_BD_DIAMOND)
3518 Store[x][y] = EL_BD_DIAMOND;
3519 else if (element == EL_WALL_EMERALD_YELLOW)
3520 Store[x][y] = EL_EMERALD_YELLOW;
3521 else if (element == EL_WALL_EMERALD_RED)
3522 Store[x][y] = EL_EMERALD_RED;
3523 else if (element == EL_WALL_EMERALD_PURPLE)
3524 Store[x][y] = EL_EMERALD_PURPLE;
3525 else if (element == EL_WALL_PEARL)
3526 Store[x][y] = EL_PEARL;
3527 else if (element == EL_WALL_CRYSTAL)
3528 Store[x][y] = EL_CRYSTAL;
3529 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3530 Store[x][y] = element_info[element].content.e[1][1];
3532 Store[x][y] = EL_EMPTY;
3535 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3536 center_element == EL_AMOEBA_TO_DIAMOND)
3537 Store2[x][y] = element;
3539 Feld[x][y] = EL_EXPLOSION;
3540 GfxElement[x][y] = artwork_element;
3543 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3544 x, y, artwork_element, EL_NAME(artwork_element));
3547 ExplodePhase[x][y] = 1;
3548 ExplodeDelay[x][y] = last_phase;
3553 if (center_element == EL_YAMYAM)
3554 game.yamyam_content_nr =
3555 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3567 GfxFrame[x][y] = 0; /* restart explosion animation */
3569 last_phase = ExplodeDelay[x][y];
3571 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3575 /* activate this even in non-DEBUG version until cause for crash in
3576 getGraphicAnimationFrame() (see below) is found and eliminated */
3582 /* this can happen if the player leaves an explosion just in time */
3583 if (GfxElement[x][y] == EL_UNDEFINED)
3584 GfxElement[x][y] = EL_EMPTY;
3586 if (GfxElement[x][y] == EL_UNDEFINED)
3589 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3590 printf("Explode(): This should never happen!\n");
3593 GfxElement[x][y] = EL_EMPTY;
3599 border_element = Store2[x][y];
3600 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3601 border_element = StorePlayer[x][y];
3603 if (phase == element_info[border_element].ignition_delay ||
3604 phase == last_phase)
3606 boolean border_explosion = FALSE;
3608 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3609 !PLAYER_EXPLOSION_PROTECTED(x, y))
3611 KillPlayerUnlessExplosionProtected(x, y);
3612 border_explosion = TRUE;
3614 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3616 Feld[x][y] = Store2[x][y];
3619 border_explosion = TRUE;
3621 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3623 AmoebeUmwandeln(x, y);
3625 border_explosion = TRUE;
3628 /* if an element just explodes due to another explosion (chain-reaction),
3629 do not immediately end the new explosion when it was the last frame of
3630 the explosion (as it would be done in the following "if"-statement!) */
3631 if (border_explosion && phase == last_phase)
3635 if (phase == last_phase)
3639 element = Feld[x][y] = Store[x][y];
3640 Store[x][y] = Store2[x][y] = 0;
3641 GfxElement[x][y] = EL_UNDEFINED;
3643 /* player can escape from explosions and might therefore be still alive */
3644 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3645 element <= EL_PLAYER_IS_EXPLODING_4)
3647 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3648 int explosion_element = EL_PLAYER_1 + player_nr;
3649 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3650 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3652 if (level.use_explosion_element[player_nr])
3653 explosion_element = level.explosion_element[player_nr];
3655 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3656 element_info[explosion_element].content.e[xx][yy]);
3659 /* restore probably existing indestructible background element */
3660 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3661 element = Feld[x][y] = Back[x][y];
3664 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3665 GfxDir[x][y] = MV_NONE;
3666 ChangeDelay[x][y] = 0;
3667 ChangePage[x][y] = -1;
3669 #if USE_NEW_CUSTOM_VALUE
3670 CustomValue[x][y] = 0;
3673 InitField_WithBug2(x, y, FALSE);
3675 DrawLevelField(x, y);
3677 TestIfElementTouchesCustomElement(x, y);
3679 if (GFX_CRUMBLED(element))
3680 DrawLevelFieldCrumbledSandNeighbours(x, y);
3682 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3683 StorePlayer[x][y] = 0;
3685 if (ELEM_IS_PLAYER(element))
3686 RelocatePlayer(x, y, element);
3688 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3690 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3691 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3694 DrawLevelFieldCrumbledSand(x, y);
3696 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3698 DrawLevelElement(x, y, Back[x][y]);
3699 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3701 else if (IS_WALKABLE_UNDER(Back[x][y]))
3703 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3704 DrawLevelElementThruMask(x, y, Back[x][y]);
3706 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3707 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3711 void DynaExplode(int ex, int ey)
3714 int dynabomb_element = Feld[ex][ey];
3715 int dynabomb_size = 1;
3716 boolean dynabomb_xl = FALSE;
3717 struct PlayerInfo *player;
3718 static int xy[4][2] =
3726 if (IS_ACTIVE_BOMB(dynabomb_element))
3728 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3729 dynabomb_size = player->dynabomb_size;
3730 dynabomb_xl = player->dynabomb_xl;
3731 player->dynabombs_left++;
3734 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3736 for (i = 0; i < NUM_DIRECTIONS; i++)
3738 for (j = 1; j <= dynabomb_size; j++)
3740 int x = ex + j * xy[i][0];
3741 int y = ey + j * xy[i][1];
3744 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3747 element = Feld[x][y];
3749 /* do not restart explosions of fields with active bombs */
3750 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3753 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3755 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3756 !IS_DIGGABLE(element) && !dynabomb_xl)
3762 void Bang(int x, int y)
3764 int element = MovingOrBlocked2Element(x, y);
3765 int explosion_type = EX_TYPE_NORMAL;
3767 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3769 struct PlayerInfo *player = PLAYERINFO(x, y);
3771 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3772 player->element_nr);
3774 if (level.use_explosion_element[player->index_nr])
3776 int explosion_element = level.explosion_element[player->index_nr];
3778 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3779 explosion_type = EX_TYPE_CROSS;
3780 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3781 explosion_type = EX_TYPE_CENTER;
3789 case EL_BD_BUTTERFLY:
3792 case EL_DARK_YAMYAM:
3796 RaiseScoreElement(element);
3799 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3800 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3801 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3802 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3803 case EL_DYNABOMB_INCREASE_NUMBER:
3804 case EL_DYNABOMB_INCREASE_SIZE:
3805 case EL_DYNABOMB_INCREASE_POWER:
3806 explosion_type = EX_TYPE_DYNA;
3811 case EL_LAMP_ACTIVE:
3812 case EL_AMOEBA_TO_DIAMOND:
3813 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3814 explosion_type = EX_TYPE_CENTER;
3818 if (element_info[element].explosion_type == EXPLODES_CROSS)
3819 explosion_type = EX_TYPE_CROSS;
3820 else if (element_info[element].explosion_type == EXPLODES_1X1)
3821 explosion_type = EX_TYPE_CENTER;
3825 if (explosion_type == EX_TYPE_DYNA)
3828 Explode(x, y, EX_PHASE_START, explosion_type);
3830 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3833 void SplashAcid(int x, int y)
3835 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3836 (!IN_LEV_FIELD(x - 1, y - 2) ||
3837 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3838 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3840 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3841 (!IN_LEV_FIELD(x + 1, y - 2) ||
3842 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3843 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3845 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3848 static void InitBeltMovement()
3850 static int belt_base_element[4] =
3852 EL_CONVEYOR_BELT_1_LEFT,
3853 EL_CONVEYOR_BELT_2_LEFT,
3854 EL_CONVEYOR_BELT_3_LEFT,
3855 EL_CONVEYOR_BELT_4_LEFT
3857 static int belt_base_active_element[4] =
3859 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3860 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3861 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3862 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3867 /* set frame order for belt animation graphic according to belt direction */
3868 for (i = 0; i < NUM_BELTS; i++)
3872 for (j = 0; j < NUM_BELT_PARTS; j++)
3874 int element = belt_base_active_element[belt_nr] + j;
3875 int graphic = el2img(element);
3877 if (game.belt_dir[i] == MV_LEFT)
3878 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3880 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3885 SCAN_PLAYFIELD(x, y)
3887 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3890 int element = Feld[x][y];
3892 for (i = 0; i < NUM_BELTS; i++)
3894 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3896 int e_belt_nr = getBeltNrFromBeltElement(element);
3899 if (e_belt_nr == belt_nr)
3901 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3903 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3910 static void ToggleBeltSwitch(int x, int y)
3912 static int belt_base_element[4] =
3914 EL_CONVEYOR_BELT_1_LEFT,
3915 EL_CONVEYOR_BELT_2_LEFT,
3916 EL_CONVEYOR_BELT_3_LEFT,
3917 EL_CONVEYOR_BELT_4_LEFT
3919 static int belt_base_active_element[4] =
3921 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3922 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3923 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3924 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3926 static int belt_base_switch_element[4] =
3928 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3929 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3930 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3931 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3933 static int belt_move_dir[4] =
3941 int element = Feld[x][y];
3942 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3943 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3944 int belt_dir = belt_move_dir[belt_dir_nr];
3947 if (!IS_BELT_SWITCH(element))
3950 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3951 game.belt_dir[belt_nr] = belt_dir;
3953 if (belt_dir_nr == 3)
3956 /* set frame order for belt animation graphic according to belt direction */
3957 for (i = 0; i < NUM_BELT_PARTS; i++)
3959 int element = belt_base_active_element[belt_nr] + i;
3960 int graphic = el2img(element);
3962 if (belt_dir == MV_LEFT)
3963 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3965 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3969 SCAN_PLAYFIELD(xx, yy)
3971 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3974 int element = Feld[xx][yy];
3976 if (IS_BELT_SWITCH(element))
3978 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3980 if (e_belt_nr == belt_nr)
3982 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3983 DrawLevelField(xx, yy);
3986 else if (IS_BELT(element) && belt_dir != MV_NONE)
3988 int e_belt_nr = getBeltNrFromBeltElement(element);
3990 if (e_belt_nr == belt_nr)
3992 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3994 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3995 DrawLevelField(xx, yy);
3998 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4000 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4002 if (e_belt_nr == belt_nr)
4004 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4006 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4007 DrawLevelField(xx, yy);
4013 static void ToggleSwitchgateSwitch(int x, int y)
4017 game.switchgate_pos = !game.switchgate_pos;
4020 SCAN_PLAYFIELD(xx, yy)
4022 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4025 int element = Feld[xx][yy];
4027 if (element == EL_SWITCHGATE_SWITCH_UP ||
4028 element == EL_SWITCHGATE_SWITCH_DOWN)
4030 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4031 DrawLevelField(xx, yy);
4033 else if (element == EL_SWITCHGATE_OPEN ||
4034 element == EL_SWITCHGATE_OPENING)
4036 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4038 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4040 else if (element == EL_SWITCHGATE_CLOSED ||
4041 element == EL_SWITCHGATE_CLOSING)
4043 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4045 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4050 static int getInvisibleActiveFromInvisibleElement(int element)
4052 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4053 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4054 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4058 static int getInvisibleFromInvisibleActiveElement(int element)
4060 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4061 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4062 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4066 static void RedrawAllLightSwitchesAndInvisibleElements()
4071 SCAN_PLAYFIELD(x, y)
4073 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4076 int element = Feld[x][y];
4078 if (element == EL_LIGHT_SWITCH &&
4079 game.light_time_left > 0)
4081 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4082 DrawLevelField(x, y);
4084 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4085 game.light_time_left == 0)
4087 Feld[x][y] = EL_LIGHT_SWITCH;
4088 DrawLevelField(x, y);
4090 else if (element == EL_EMC_DRIPPER &&
4091 game.light_time_left > 0)
4093 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4094 DrawLevelField(x, y);
4096 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4097 game.light_time_left == 0)
4099 Feld[x][y] = EL_EMC_DRIPPER;
4100 DrawLevelField(x, y);
4102 else if (element == EL_INVISIBLE_STEELWALL ||
4103 element == EL_INVISIBLE_WALL ||
4104 element == EL_INVISIBLE_SAND)
4106 if (game.light_time_left > 0)
4107 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4109 DrawLevelField(x, y);
4111 /* uncrumble neighbour fields, if needed */
4112 if (element == EL_INVISIBLE_SAND)
4113 DrawLevelFieldCrumbledSandNeighbours(x, y);
4115 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4116 element == EL_INVISIBLE_WALL_ACTIVE ||
4117 element == EL_INVISIBLE_SAND_ACTIVE)
4119 if (game.light_time_left == 0)
4120 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4122 DrawLevelField(x, y);
4124 /* re-crumble neighbour fields, if needed */
4125 if (element == EL_INVISIBLE_SAND)
4126 DrawLevelFieldCrumbledSandNeighbours(x, y);
4131 static void RedrawAllInvisibleElementsForLenses()
4136 SCAN_PLAYFIELD(x, y)
4138 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4141 int element = Feld[x][y];
4143 if (element == EL_EMC_DRIPPER &&
4144 game.lenses_time_left > 0)
4146 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4147 DrawLevelField(x, y);
4149 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4150 game.lenses_time_left == 0)
4152 Feld[x][y] = EL_EMC_DRIPPER;
4153 DrawLevelField(x, y);
4155 else if (element == EL_INVISIBLE_STEELWALL ||
4156 element == EL_INVISIBLE_WALL ||
4157 element == EL_INVISIBLE_SAND)
4159 if (game.lenses_time_left > 0)
4160 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4162 DrawLevelField(x, y);
4164 /* uncrumble neighbour fields, if needed */
4165 if (element == EL_INVISIBLE_SAND)
4166 DrawLevelFieldCrumbledSandNeighbours(x, y);
4168 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4169 element == EL_INVISIBLE_WALL_ACTIVE ||
4170 element == EL_INVISIBLE_SAND_ACTIVE)
4172 if (game.lenses_time_left == 0)
4173 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4175 DrawLevelField(x, y);
4177 /* re-crumble neighbour fields, if needed */
4178 if (element == EL_INVISIBLE_SAND)
4179 DrawLevelFieldCrumbledSandNeighbours(x, y);
4184 static void RedrawAllInvisibleElementsForMagnifier()
4189 SCAN_PLAYFIELD(x, y)
4191 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4194 int element = Feld[x][y];
4196 if (element == EL_EMC_FAKE_GRASS &&
4197 game.magnify_time_left > 0)
4199 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4200 DrawLevelField(x, y);
4202 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4203 game.magnify_time_left == 0)
4205 Feld[x][y] = EL_EMC_FAKE_GRASS;
4206 DrawLevelField(x, y);
4208 else if (IS_GATE_GRAY(element) &&
4209 game.magnify_time_left > 0)
4211 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4212 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4213 IS_EM_GATE_GRAY(element) ?
4214 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4215 IS_EMC_GATE_GRAY(element) ?
4216 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4218 DrawLevelField(x, y);
4220 else if (IS_GATE_GRAY_ACTIVE(element) &&
4221 game.magnify_time_left == 0)
4223 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4224 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4225 IS_EM_GATE_GRAY_ACTIVE(element) ?
4226 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4227 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4228 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4230 DrawLevelField(x, y);
4235 static void ToggleLightSwitch(int x, int y)
4237 int element = Feld[x][y];
4239 game.light_time_left =
4240 (element == EL_LIGHT_SWITCH ?
4241 level.time_light * FRAMES_PER_SECOND : 0);
4243 RedrawAllLightSwitchesAndInvisibleElements();
4246 static void ActivateTimegateSwitch(int x, int y)
4250 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4253 SCAN_PLAYFIELD(xx, yy)
4255 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4258 int element = Feld[xx][yy];
4260 if (element == EL_TIMEGATE_CLOSED ||
4261 element == EL_TIMEGATE_CLOSING)
4263 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4264 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4268 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4270 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4271 DrawLevelField(xx, yy);
4277 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4280 void Impact(int x, int y)
4282 boolean last_line = (y == lev_fieldy - 1);
4283 boolean object_hit = FALSE;
4284 boolean impact = (last_line || object_hit);
4285 int element = Feld[x][y];
4286 int smashed = EL_STEELWALL;
4288 if (!last_line) /* check if element below was hit */
4290 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4293 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4294 MovDir[x][y + 1] != MV_DOWN ||
4295 MovPos[x][y + 1] <= TILEY / 2));
4297 /* do not smash moving elements that left the smashed field in time */
4298 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4299 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4302 #if USE_QUICKSAND_IMPACT_BUGFIX
4303 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4305 RemoveMovingField(x, y + 1);
4306 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4307 Feld[x][y + 2] = EL_ROCK;
4308 DrawLevelField(x, y + 2);
4315 smashed = MovingOrBlocked2Element(x, y + 1);
4317 impact = (last_line || object_hit);
4320 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4322 SplashAcid(x, y + 1);
4326 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4327 /* only reset graphic animation if graphic really changes after impact */
4329 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4331 ResetGfxAnimation(x, y);
4332 DrawLevelField(x, y);
4335 if (impact && CAN_EXPLODE_IMPACT(element))
4340 else if (impact && element == EL_PEARL)
4342 ResetGfxAnimation(x, y);
4344 Feld[x][y] = EL_PEARL_BREAKING;
4345 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4348 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4350 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4355 if (impact && element == EL_AMOEBA_DROP)
4357 if (object_hit && IS_PLAYER(x, y + 1))
4358 KillPlayerUnlessEnemyProtected(x, y + 1);
4359 else if (object_hit && smashed == EL_PENGUIN)
4363 Feld[x][y] = EL_AMOEBA_GROWING;
4364 Store[x][y] = EL_AMOEBA_WET;
4366 ResetRandomAnimationValue(x, y);
4371 if (object_hit) /* check which object was hit */
4373 if (CAN_PASS_MAGIC_WALL(element) &&
4374 (smashed == EL_MAGIC_WALL ||
4375 smashed == EL_BD_MAGIC_WALL))
4378 int activated_magic_wall =
4379 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4380 EL_BD_MAGIC_WALL_ACTIVE);
4382 /* activate magic wall / mill */
4384 SCAN_PLAYFIELD(xx, yy)
4386 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4388 if (Feld[xx][yy] == smashed)
4389 Feld[xx][yy] = activated_magic_wall;
4391 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4392 game.magic_wall_active = TRUE;
4394 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4395 SND_MAGIC_WALL_ACTIVATING :
4396 SND_BD_MAGIC_WALL_ACTIVATING));
4399 if (IS_PLAYER(x, y + 1))
4401 if (CAN_SMASH_PLAYER(element))
4403 KillPlayerUnlessEnemyProtected(x, y + 1);
4407 else if (smashed == EL_PENGUIN)
4409 if (CAN_SMASH_PLAYER(element))
4415 else if (element == EL_BD_DIAMOND)
4417 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4423 else if (((element == EL_SP_INFOTRON ||
4424 element == EL_SP_ZONK) &&
4425 (smashed == EL_SP_SNIKSNAK ||
4426 smashed == EL_SP_ELECTRON ||
4427 smashed == EL_SP_DISK_ORANGE)) ||
4428 (element == EL_SP_INFOTRON &&
4429 smashed == EL_SP_DISK_YELLOW))
4434 else if (CAN_SMASH_EVERYTHING(element))
4436 if (IS_CLASSIC_ENEMY(smashed) ||
4437 CAN_EXPLODE_SMASHED(smashed))
4442 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4444 if (smashed == EL_LAMP ||
4445 smashed == EL_LAMP_ACTIVE)
4450 else if (smashed == EL_NUT)
4452 Feld[x][y + 1] = EL_NUT_BREAKING;
4453 PlayLevelSound(x, y, SND_NUT_BREAKING);
4454 RaiseScoreElement(EL_NUT);
4457 else if (smashed == EL_PEARL)
4459 ResetGfxAnimation(x, y);
4461 Feld[x][y + 1] = EL_PEARL_BREAKING;
4462 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4465 else if (smashed == EL_DIAMOND)
4467 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4468 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4471 else if (IS_BELT_SWITCH(smashed))
4473 ToggleBeltSwitch(x, y + 1);
4475 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4476 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4478 ToggleSwitchgateSwitch(x, y + 1);
4480 else if (smashed == EL_LIGHT_SWITCH ||
4481 smashed == EL_LIGHT_SWITCH_ACTIVE)
4483 ToggleLightSwitch(x, y + 1);
4488 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4491 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4493 CheckElementChangeBySide(x, y + 1, smashed, element,
4494 CE_SWITCHED, CH_SIDE_TOP);
4495 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4501 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4506 /* play sound of magic wall / mill */
4508 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4509 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4511 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4512 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4513 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4514 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4519 /* play sound of object that hits the ground */
4520 if (last_line || object_hit)
4521 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4524 inline static void TurnRoundExt(int x, int y)
4536 { 0, 0 }, { 0, 0 }, { 0, 0 },
4541 int left, right, back;
4545 { MV_DOWN, MV_UP, MV_RIGHT },
4546 { MV_UP, MV_DOWN, MV_LEFT },
4548 { MV_LEFT, MV_RIGHT, MV_DOWN },
4552 { MV_RIGHT, MV_LEFT, MV_UP }
4555 int element = Feld[x][y];
4556 int move_pattern = element_info[element].move_pattern;
4558 int old_move_dir = MovDir[x][y];
4559 int left_dir = turn[old_move_dir].left;
4560 int right_dir = turn[old_move_dir].right;
4561 int back_dir = turn[old_move_dir].back;
4563 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4564 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4565 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4566 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4568 int left_x = x + left_dx, left_y = y + left_dy;
4569 int right_x = x + right_dx, right_y = y + right_dy;
4570 int move_x = x + move_dx, move_y = y + move_dy;
4574 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4576 TestIfBadThingTouchesOtherBadThing(x, y);
4578 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4579 MovDir[x][y] = right_dir;
4580 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4581 MovDir[x][y] = left_dir;
4583 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4585 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4588 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4590 TestIfBadThingTouchesOtherBadThing(x, y);
4592 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4593 MovDir[x][y] = left_dir;
4594 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4595 MovDir[x][y] = right_dir;
4597 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4599 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4602 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4604 TestIfBadThingTouchesOtherBadThing(x, y);
4606 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4607 MovDir[x][y] = left_dir;
4608 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4609 MovDir[x][y] = right_dir;
4611 if (MovDir[x][y] != old_move_dir)
4614 else if (element == EL_YAMYAM)
4616 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4617 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4619 if (can_turn_left && can_turn_right)
4620 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4621 else if (can_turn_left)
4622 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4623 else if (can_turn_right)
4624 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4626 MovDir[x][y] = back_dir;
4628 MovDelay[x][y] = 16 + 16 * RND(3);
4630 else if (element == EL_DARK_YAMYAM)
4632 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4634 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4637 if (can_turn_left && can_turn_right)
4638 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4639 else if (can_turn_left)
4640 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4641 else if (can_turn_right)
4642 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4644 MovDir[x][y] = back_dir;
4646 MovDelay[x][y] = 16 + 16 * RND(3);
4648 else if (element == EL_PACMAN)
4650 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4651 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4653 if (can_turn_left && can_turn_right)
4654 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4655 else if (can_turn_left)
4656 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4657 else if (can_turn_right)
4658 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4660 MovDir[x][y] = back_dir;
4662 MovDelay[x][y] = 6 + RND(40);
4664 else if (element == EL_PIG)
4666 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4667 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4668 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4669 boolean should_turn_left, should_turn_right, should_move_on;
4671 int rnd = RND(rnd_value);
4673 should_turn_left = (can_turn_left &&
4675 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4676 y + back_dy + left_dy)));
4677 should_turn_right = (can_turn_right &&
4679 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4680 y + back_dy + right_dy)));
4681 should_move_on = (can_move_on &&
4684 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4685 y + move_dy + left_dy) ||
4686 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4687 y + move_dy + right_dy)));
4689 if (should_turn_left || should_turn_right || should_move_on)
4691 if (should_turn_left && should_turn_right && should_move_on)
4692 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4693 rnd < 2 * rnd_value / 3 ? right_dir :
4695 else if (should_turn_left && should_turn_right)
4696 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4697 else if (should_turn_left && should_move_on)
4698 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4699 else if (should_turn_right && should_move_on)
4700 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4701 else if (should_turn_left)
4702 MovDir[x][y] = left_dir;
4703 else if (should_turn_right)
4704 MovDir[x][y] = right_dir;
4705 else if (should_move_on)
4706 MovDir[x][y] = old_move_dir;
4708 else if (can_move_on && rnd > rnd_value / 8)
4709 MovDir[x][y] = old_move_dir;
4710 else if (can_turn_left && can_turn_right)
4711 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4712 else if (can_turn_left && rnd > rnd_value / 8)
4713 MovDir[x][y] = left_dir;
4714 else if (can_turn_right && rnd > rnd_value/8)
4715 MovDir[x][y] = right_dir;
4717 MovDir[x][y] = back_dir;
4719 xx = x + move_xy[MovDir[x][y]].dx;
4720 yy = y + move_xy[MovDir[x][y]].dy;
4722 if (!IN_LEV_FIELD(xx, yy) ||
4723 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4724 MovDir[x][y] = old_move_dir;
4728 else if (element == EL_DRAGON)
4730 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4731 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4732 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4734 int rnd = RND(rnd_value);
4736 if (can_move_on && rnd > rnd_value / 8)
4737 MovDir[x][y] = old_move_dir;
4738 else if (can_turn_left && can_turn_right)
4739 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4740 else if (can_turn_left && rnd > rnd_value / 8)
4741 MovDir[x][y] = left_dir;
4742 else if (can_turn_right && rnd > rnd_value / 8)
4743 MovDir[x][y] = right_dir;
4745 MovDir[x][y] = back_dir;
4747 xx = x + move_xy[MovDir[x][y]].dx;
4748 yy = y + move_xy[MovDir[x][y]].dy;
4750 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4751 MovDir[x][y] = old_move_dir;
4755 else if (element == EL_MOLE)
4757 boolean can_move_on =
4758 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4759 IS_AMOEBOID(Feld[move_x][move_y]) ||
4760 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4763 boolean can_turn_left =
4764 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4765 IS_AMOEBOID(Feld[left_x][left_y])));
4767 boolean can_turn_right =
4768 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4769 IS_AMOEBOID(Feld[right_x][right_y])));
4771 if (can_turn_left && can_turn_right)
4772 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4773 else if (can_turn_left)
4774 MovDir[x][y] = left_dir;
4776 MovDir[x][y] = right_dir;
4779 if (MovDir[x][y] != old_move_dir)
4782 else if (element == EL_BALLOON)
4784 MovDir[x][y] = game.wind_direction;
4787 else if (element == EL_SPRING)
4789 #if USE_NEW_SPRING_BUMPER
4790 if (MovDir[x][y] & MV_HORIZONTAL)
4792 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4793 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4795 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4796 ResetGfxAnimation(move_x, move_y);
4797 DrawLevelField(move_x, move_y);
4799 MovDir[x][y] = back_dir;
4801 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4802 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4803 MovDir[x][y] = MV_NONE;
4806 if (MovDir[x][y] & MV_HORIZONTAL &&
4807 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4808 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4809 MovDir[x][y] = MV_NONE;
4814 else if (element == EL_ROBOT ||
4815 element == EL_SATELLITE ||
4816 element == EL_PENGUIN ||
4817 element == EL_EMC_ANDROID)
4819 int attr_x = -1, attr_y = -1;
4830 for (i = 0; i < MAX_PLAYERS; i++)
4832 struct PlayerInfo *player = &stored_player[i];
4833 int jx = player->jx, jy = player->jy;
4835 if (!player->active)
4839 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4847 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4848 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4849 game.engine_version < VERSION_IDENT(3,1,0,0)))
4855 if (element == EL_PENGUIN)
4858 static int xy[4][2] =
4866 for (i = 0; i < NUM_DIRECTIONS; i++)
4868 int ex = x + xy[i][0];
4869 int ey = y + xy[i][1];
4871 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4880 MovDir[x][y] = MV_NONE;
4882 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4883 else if (attr_x > x)
4884 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4886 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4887 else if (attr_y > y)
4888 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4890 if (element == EL_ROBOT)
4894 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4895 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4896 Moving2Blocked(x, y, &newx, &newy);
4898 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4899 MovDelay[x][y] = 8 + 8 * !RND(3);
4901 MovDelay[x][y] = 16;
4903 else if (element == EL_PENGUIN)
4909 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4911 boolean first_horiz = RND(2);
4912 int new_move_dir = MovDir[x][y];
4915 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4916 Moving2Blocked(x, y, &newx, &newy);
4918 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4922 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4923 Moving2Blocked(x, y, &newx, &newy);
4925 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4928 MovDir[x][y] = old_move_dir;
4932 else if (element == EL_SATELLITE)
4938 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4940 boolean first_horiz = RND(2);
4941 int new_move_dir = MovDir[x][y];
4944 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4945 Moving2Blocked(x, y, &newx, &newy);
4947 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4951 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4952 Moving2Blocked(x, y, &newx, &newy);
4954 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4957 MovDir[x][y] = old_move_dir;
4961 else if (element == EL_EMC_ANDROID)
4963 static int check_pos[16] =
4965 -1, /* 0 => (invalid) */
4966 7, /* 1 => MV_LEFT */
4967 3, /* 2 => MV_RIGHT */
4968 -1, /* 3 => (invalid) */
4970 0, /* 5 => MV_LEFT | MV_UP */
4971 2, /* 6 => MV_RIGHT | MV_UP */
4972 -1, /* 7 => (invalid) */
4973 5, /* 8 => MV_DOWN */
4974 6, /* 9 => MV_LEFT | MV_DOWN */
4975 4, /* 10 => MV_RIGHT | MV_DOWN */
4976 -1, /* 11 => (invalid) */
4977 -1, /* 12 => (invalid) */
4978 -1, /* 13 => (invalid) */
4979 -1, /* 14 => (invalid) */
4980 -1, /* 15 => (invalid) */
4988 { -1, -1, MV_LEFT | MV_UP },
4990 { +1, -1, MV_RIGHT | MV_UP },
4991 { +1, 0, MV_RIGHT },
4992 { +1, +1, MV_RIGHT | MV_DOWN },
4994 { -1, +1, MV_LEFT | MV_DOWN },
4997 int start_pos, check_order;
4998 boolean can_clone = FALSE;
5001 /* check if there is any free field around current position */
5002 for (i = 0; i < 8; i++)
5004 int newx = x + check_xy[i].dx;
5005 int newy = y + check_xy[i].dy;
5007 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5015 if (can_clone) /* randomly find an element to clone */
5019 start_pos = check_pos[RND(8)];
5020 check_order = (RND(2) ? -1 : +1);
5022 for (i = 0; i < 8; i++)
5024 int pos_raw = start_pos + i * check_order;
5025 int pos = (pos_raw + 8) % 8;
5026 int newx = x + check_xy[pos].dx;
5027 int newy = y + check_xy[pos].dy;
5029 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5031 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5032 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5034 Store[x][y] = Feld[newx][newy];
5043 if (can_clone) /* randomly find a direction to move */
5047 start_pos = check_pos[RND(8)];
5048 check_order = (RND(2) ? -1 : +1);
5050 for (i = 0; i < 8; i++)
5052 int pos_raw = start_pos + i * check_order;
5053 int pos = (pos_raw + 8) % 8;
5054 int newx = x + check_xy[pos].dx;
5055 int newy = y + check_xy[pos].dy;
5056 int new_move_dir = check_xy[pos].dir;
5058 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5060 MovDir[x][y] = new_move_dir;
5061 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5070 if (can_clone) /* cloning and moving successful */
5073 /* cannot clone -- try to move towards player */
5075 start_pos = check_pos[MovDir[x][y] & 0x0f];
5076 check_order = (RND(2) ? -1 : +1);
5078 for (i = 0; i < 3; i++)
5080 /* first check start_pos, then previous/next or (next/previous) pos */
5081 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5082 int pos = (pos_raw + 8) % 8;
5083 int newx = x + check_xy[pos].dx;
5084 int newy = y + check_xy[pos].dy;
5085 int new_move_dir = check_xy[pos].dir;
5087 if (IS_PLAYER(newx, newy))
5090 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5092 MovDir[x][y] = new_move_dir;
5093 MovDelay[x][y] = level.android_move_time * 8 + 1;
5100 else if (move_pattern == MV_TURNING_LEFT ||
5101 move_pattern == MV_TURNING_RIGHT ||
5102 move_pattern == MV_TURNING_LEFT_RIGHT ||
5103 move_pattern == MV_TURNING_RIGHT_LEFT ||
5104 move_pattern == MV_TURNING_RANDOM ||
5105 move_pattern == MV_ALL_DIRECTIONS)
5107 boolean can_turn_left =
5108 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5109 boolean can_turn_right =
5110 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5112 if (element_info[element].move_stepsize == 0) /* "not moving" */
5115 if (move_pattern == MV_TURNING_LEFT)
5116 MovDir[x][y] = left_dir;
5117 else if (move_pattern == MV_TURNING_RIGHT)
5118 MovDir[x][y] = right_dir;
5119 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5120 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5121 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5122 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5123 else if (move_pattern == MV_TURNING_RANDOM)
5124 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5125 can_turn_right && !can_turn_left ? right_dir :
5126 RND(2) ? left_dir : right_dir);
5127 else if (can_turn_left && can_turn_right)
5128 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5129 else if (can_turn_left)
5130 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5131 else if (can_turn_right)
5132 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5134 MovDir[x][y] = back_dir;
5136 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5138 else if (move_pattern == MV_HORIZONTAL ||
5139 move_pattern == MV_VERTICAL)
5141 if (move_pattern & old_move_dir)
5142 MovDir[x][y] = back_dir;
5143 else if (move_pattern == MV_HORIZONTAL)
5144 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5145 else if (move_pattern == MV_VERTICAL)
5146 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5148 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5150 else if (move_pattern & MV_ANY_DIRECTION)
5152 MovDir[x][y] = move_pattern;
5153 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5155 else if (move_pattern & MV_WIND_DIRECTION)
5157 MovDir[x][y] = game.wind_direction;
5158 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5160 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5162 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5163 MovDir[x][y] = left_dir;
5164 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5165 MovDir[x][y] = right_dir;
5167 if (MovDir[x][y] != old_move_dir)
5168 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5170 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5172 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5173 MovDir[x][y] = right_dir;
5174 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5175 MovDir[x][y] = left_dir;
5177 if (MovDir[x][y] != old_move_dir)
5178 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5180 else if (move_pattern == MV_TOWARDS_PLAYER ||
5181 move_pattern == MV_AWAY_FROM_PLAYER)
5183 int attr_x = -1, attr_y = -1;
5185 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5196 for (i = 0; i < MAX_PLAYERS; i++)
5198 struct PlayerInfo *player = &stored_player[i];
5199 int jx = player->jx, jy = player->jy;
5201 if (!player->active)
5205 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5213 MovDir[x][y] = MV_NONE;
5215 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5216 else if (attr_x > x)
5217 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5219 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5220 else if (attr_y > y)
5221 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5223 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5225 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5227 boolean first_horiz = RND(2);
5228 int new_move_dir = MovDir[x][y];
5230 if (element_info[element].move_stepsize == 0) /* "not moving" */
5232 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5233 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5239 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5240 Moving2Blocked(x, y, &newx, &newy);
5242 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5246 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5247 Moving2Blocked(x, y, &newx, &newy);
5249 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5252 MovDir[x][y] = old_move_dir;
5255 else if (move_pattern == MV_WHEN_PUSHED ||
5256 move_pattern == MV_WHEN_DROPPED)
5258 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5259 MovDir[x][y] = MV_NONE;
5263 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5265 static int test_xy[7][2] =
5275 static int test_dir[7] =
5285 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5286 int move_preference = -1000000; /* start with very low preference */
5287 int new_move_dir = MV_NONE;
5288 int start_test = RND(4);
5291 for (i = 0; i < NUM_DIRECTIONS; i++)
5293 int move_dir = test_dir[start_test + i];
5294 int move_dir_preference;
5296 xx = x + test_xy[start_test + i][0];
5297 yy = y + test_xy[start_test + i][1];
5299 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5300 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5302 new_move_dir = move_dir;
5307 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5310 move_dir_preference = -1 * RunnerVisit[xx][yy];
5311 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5312 move_dir_preference = PlayerVisit[xx][yy];
5314 if (move_dir_preference > move_preference)
5316 /* prefer field that has not been visited for the longest time */
5317 move_preference = move_dir_preference;
5318 new_move_dir = move_dir;
5320 else if (move_dir_preference == move_preference &&
5321 move_dir == old_move_dir)
5323 /* prefer last direction when all directions are preferred equally */
5324 move_preference = move_dir_preference;
5325 new_move_dir = move_dir;
5329 MovDir[x][y] = new_move_dir;
5330 if (old_move_dir != new_move_dir)
5331 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5335 static void TurnRound(int x, int y)
5337 int direction = MovDir[x][y];
5339 int element, graphic;
5344 GfxDir[x][y] = MovDir[x][y];
5346 if (direction != MovDir[x][y])
5350 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5353 element = Feld[x][y];
5354 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5356 if (graphic_info[graphic].anim_global_sync)
5357 GfxFrame[x][y] = FrameCounter;
5358 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5359 GfxFrame[x][y] = CustomValue[x][y];
5360 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5361 GfxFrame[x][y] = element_info[element].collect_score;
5365 static boolean JustBeingPushed(int x, int y)
5369 for (i = 0; i < MAX_PLAYERS; i++)
5371 struct PlayerInfo *player = &stored_player[i];
5373 if (player->active && player->is_pushing && player->MovPos)
5375 int next_jx = player->jx + (player->jx - player->last_jx);
5376 int next_jy = player->jy + (player->jy - player->last_jy);
5378 if (x == next_jx && y == next_jy)
5386 void StartMoving(int x, int y)
5388 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5389 int element = Feld[x][y];
5394 if (MovDelay[x][y] == 0)
5395 GfxAction[x][y] = ACTION_DEFAULT;
5397 if (CAN_FALL(element) && y < lev_fieldy - 1)
5399 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5400 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5401 if (JustBeingPushed(x, y))
5404 if (element == EL_QUICKSAND_FULL)
5406 if (IS_FREE(x, y + 1))
5408 InitMovingField(x, y, MV_DOWN);
5409 started_moving = TRUE;
5411 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5412 Store[x][y] = EL_ROCK;
5414 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5416 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5418 if (!MovDelay[x][y])
5419 MovDelay[x][y] = TILEY + 1;
5428 Feld[x][y] = EL_QUICKSAND_EMPTY;
5429 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5430 Store[x][y + 1] = Store[x][y];
5433 PlayLevelSoundAction(x, y, ACTION_FILLING);
5436 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5437 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5439 InitMovingField(x, y, MV_DOWN);
5440 started_moving = TRUE;
5442 Feld[x][y] = EL_QUICKSAND_FILLING;
5443 Store[x][y] = element;
5445 PlayLevelSoundAction(x, y, ACTION_FILLING);
5447 else if (element == EL_MAGIC_WALL_FULL)
5449 if (IS_FREE(x, y + 1))
5451 InitMovingField(x, y, MV_DOWN);
5452 started_moving = TRUE;
5454 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5455 Store[x][y] = EL_CHANGED(Store[x][y]);
5457 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5459 if (!MovDelay[x][y])
5460 MovDelay[x][y] = TILEY/4 + 1;
5469 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5470 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5471 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5475 else if (element == EL_BD_MAGIC_WALL_FULL)
5477 if (IS_FREE(x, y + 1))
5479 InitMovingField(x, y, MV_DOWN);
5480 started_moving = TRUE;
5482 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5483 Store[x][y] = EL_CHANGED2(Store[x][y]);
5485 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5487 if (!MovDelay[x][y])
5488 MovDelay[x][y] = TILEY/4 + 1;
5497 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5498 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5499 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5503 else if (CAN_PASS_MAGIC_WALL(element) &&
5504 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5505 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5507 InitMovingField(x, y, MV_DOWN);
5508 started_moving = TRUE;
5511 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5512 EL_BD_MAGIC_WALL_FILLING);
5513 Store[x][y] = element;
5515 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5517 SplashAcid(x, y + 1);
5519 InitMovingField(x, y, MV_DOWN);
5520 started_moving = TRUE;
5522 Store[x][y] = EL_ACID;
5524 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5525 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5527 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5528 CAN_FALL(element) && WasJustFalling[x][y] &&
5529 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5531 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5532 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5533 (Feld[x][y + 1] == EL_BLOCKED)))
5535 /* this is needed for a special case not covered by calling "Impact()"
5536 from "ContinueMoving()": if an element moves to a tile directly below
5537 another element which was just falling on that tile (which was empty
5538 in the previous frame), the falling element above would just stop
5539 instead of smashing the element below (in previous version, the above
5540 element was just checked for "moving" instead of "falling", resulting
5541 in incorrect smashes caused by horizontal movement of the above
5542 element; also, the case of the player being the element to smash was
5543 simply not covered here... :-/ ) */
5545 CheckCollision[x][y] = 0;
5549 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5551 if (MovDir[x][y] == MV_NONE)
5553 InitMovingField(x, y, MV_DOWN);
5554 started_moving = TRUE;
5557 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5559 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5560 MovDir[x][y] = MV_DOWN;
5562 InitMovingField(x, y, MV_DOWN);
5563 started_moving = TRUE;
5565 else if (element == EL_AMOEBA_DROP)
5567 Feld[x][y] = EL_AMOEBA_GROWING;
5568 Store[x][y] = EL_AMOEBA_WET;
5570 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5571 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5572 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5573 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5575 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5576 (IS_FREE(x - 1, y + 1) ||
5577 Feld[x - 1][y + 1] == EL_ACID));
5578 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5579 (IS_FREE(x + 1, y + 1) ||
5580 Feld[x + 1][y + 1] == EL_ACID));
5581 boolean can_fall_any = (can_fall_left || can_fall_right);
5582 boolean can_fall_both = (can_fall_left && can_fall_right);
5583 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5585 #if USE_NEW_ALL_SLIPPERY
5586 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5588 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5589 can_fall_right = FALSE;
5590 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5591 can_fall_left = FALSE;
5592 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5593 can_fall_right = FALSE;
5594 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5595 can_fall_left = FALSE;
5597 can_fall_any = (can_fall_left || can_fall_right);
5598 can_fall_both = FALSE;
5601 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5603 if (slippery_type == SLIPPERY_ONLY_LEFT)
5604 can_fall_right = FALSE;
5605 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5606 can_fall_left = FALSE;
5607 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5608 can_fall_right = FALSE;
5609 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5610 can_fall_left = FALSE;
5612 can_fall_any = (can_fall_left || can_fall_right);
5613 can_fall_both = (can_fall_left && can_fall_right);
5617 #if USE_NEW_ALL_SLIPPERY
5619 #if USE_NEW_SP_SLIPPERY
5620 /* !!! better use the same properties as for custom elements here !!! */
5621 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5622 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5624 can_fall_right = FALSE; /* slip down on left side */
5625 can_fall_both = FALSE;
5630 #if USE_NEW_ALL_SLIPPERY
5633 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5634 can_fall_right = FALSE; /* slip down on left side */
5636 can_fall_left = !(can_fall_right = RND(2));
5638 can_fall_both = FALSE;
5643 if (game.emulation == EMU_BOULDERDASH ||
5644 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5645 can_fall_right = FALSE; /* slip down on left side */
5647 can_fall_left = !(can_fall_right = RND(2));
5649 can_fall_both = FALSE;
5655 /* if not determined otherwise, prefer left side for slipping down */
5656 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5657 started_moving = TRUE;
5661 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5663 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5666 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5667 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5668 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5669 int belt_dir = game.belt_dir[belt_nr];
5671 if ((belt_dir == MV_LEFT && left_is_free) ||
5672 (belt_dir == MV_RIGHT && right_is_free))
5674 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5676 InitMovingField(x, y, belt_dir);
5677 started_moving = TRUE;
5679 Pushed[x][y] = TRUE;
5680 Pushed[nextx][y] = TRUE;
5682 GfxAction[x][y] = ACTION_DEFAULT;
5686 MovDir[x][y] = 0; /* if element was moving, stop it */
5691 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5693 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5695 if (CAN_MOVE(element) && !started_moving)
5698 int move_pattern = element_info[element].move_pattern;
5703 if (MovDir[x][y] == MV_NONE)
5705 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5706 x, y, element, element_info[element].token_name);
5707 printf("StartMoving(): This should never happen!\n");
5712 Moving2Blocked(x, y, &newx, &newy);
5714 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5717 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5718 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5720 WasJustMoving[x][y] = 0;
5721 CheckCollision[x][y] = 0;
5723 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5725 if (Feld[x][y] != element) /* element has changed */
5729 if (!MovDelay[x][y]) /* start new movement phase */
5731 /* all objects that can change their move direction after each step
5732 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5734 if (element != EL_YAMYAM &&
5735 element != EL_DARK_YAMYAM &&
5736 element != EL_PACMAN &&
5737 !(move_pattern & MV_ANY_DIRECTION) &&
5738 move_pattern != MV_TURNING_LEFT &&
5739 move_pattern != MV_TURNING_RIGHT &&
5740 move_pattern != MV_TURNING_LEFT_RIGHT &&
5741 move_pattern != MV_TURNING_RIGHT_LEFT &&
5742 move_pattern != MV_TURNING_RANDOM)
5746 if (MovDelay[x][y] && (element == EL_BUG ||
5747 element == EL_SPACESHIP ||
5748 element == EL_SP_SNIKSNAK ||
5749 element == EL_SP_ELECTRON ||
5750 element == EL_MOLE))
5751 DrawLevelField(x, y);
5755 if (MovDelay[x][y]) /* wait some time before next movement */
5759 if (element == EL_ROBOT ||
5760 element == EL_YAMYAM ||
5761 element == EL_DARK_YAMYAM)
5763 DrawLevelElementAnimationIfNeeded(x, y, element);
5764 PlayLevelSoundAction(x, y, ACTION_WAITING);
5766 else if (element == EL_SP_ELECTRON)
5767 DrawLevelElementAnimationIfNeeded(x, y, element);
5768 else if (element == EL_DRAGON)
5771 int dir = MovDir[x][y];
5772 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5773 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5774 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5775 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5776 dir == MV_UP ? IMG_FLAMES_1_UP :
5777 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5778 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5780 GfxAction[x][y] = ACTION_ATTACKING;
5782 if (IS_PLAYER(x, y))
5783 DrawPlayerField(x, y);
5785 DrawLevelField(x, y);
5787 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5789 for (i = 1; i <= 3; i++)
5791 int xx = x + i * dx;
5792 int yy = y + i * dy;
5793 int sx = SCREENX(xx);
5794 int sy = SCREENY(yy);
5795 int flame_graphic = graphic + (i - 1);
5797 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5802 int flamed = MovingOrBlocked2Element(xx, yy);
5806 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5808 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5809 RemoveMovingField(xx, yy);
5811 RemoveField(xx, yy);
5813 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5816 RemoveMovingField(xx, yy);
5819 ChangeDelay[xx][yy] = 0;
5821 Feld[xx][yy] = EL_FLAMES;
5823 if (IN_SCR_FIELD(sx, sy))
5825 DrawLevelFieldCrumbledSand(xx, yy);
5826 DrawGraphic(sx, sy, flame_graphic, frame);
5831 if (Feld[xx][yy] == EL_FLAMES)
5832 Feld[xx][yy] = EL_EMPTY;
5833 DrawLevelField(xx, yy);
5838 if (MovDelay[x][y]) /* element still has to wait some time */
5840 PlayLevelSoundAction(x, y, ACTION_WAITING);
5846 /* now make next step */
5848 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5850 if (DONT_COLLIDE_WITH(element) &&
5851 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5852 !PLAYER_ENEMY_PROTECTED(newx, newy))
5854 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5859 else if (CAN_MOVE_INTO_ACID(element) &&
5860 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5861 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5862 (MovDir[x][y] == MV_DOWN ||
5863 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5865 SplashAcid(newx, newy);
5866 Store[x][y] = EL_ACID;
5868 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5870 if (Feld[newx][newy] == EL_EXIT_OPEN)
5873 DrawLevelField(x, y);
5875 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5876 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5877 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5879 local_player->friends_still_needed--;
5880 if (!local_player->friends_still_needed &&
5881 !local_player->GameOver && AllPlayersGone)
5882 local_player->LevelSolved = local_player->GameOver = TRUE;
5886 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5888 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5889 DrawLevelField(newx, newy);
5891 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5893 else if (!IS_FREE(newx, newy))
5895 GfxAction[x][y] = ACTION_WAITING;
5897 if (IS_PLAYER(x, y))
5898 DrawPlayerField(x, y);
5900 DrawLevelField(x, y);
5905 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5907 if (IS_FOOD_PIG(Feld[newx][newy]))
5909 if (IS_MOVING(newx, newy))
5910 RemoveMovingField(newx, newy);
5913 Feld[newx][newy] = EL_EMPTY;
5914 DrawLevelField(newx, newy);
5917 PlayLevelSound(x, y, SND_PIG_DIGGING);
5919 else if (!IS_FREE(newx, newy))
5921 if (IS_PLAYER(x, y))
5922 DrawPlayerField(x, y);
5924 DrawLevelField(x, y);
5929 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5931 if (Store[x][y] != EL_EMPTY)
5933 boolean can_clone = FALSE;
5936 /* check if element to clone is still there */
5937 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5939 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5947 /* cannot clone or target field not free anymore -- do not clone */
5948 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5949 Store[x][y] = EL_EMPTY;
5952 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5954 if (IS_MV_DIAGONAL(MovDir[x][y]))
5956 int diagonal_move_dir = MovDir[x][y];
5957 int stored = Store[x][y];
5958 int change_delay = 8;
5961 /* android is moving diagonally */
5963 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5965 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5966 GfxElement[x][y] = EL_EMC_ANDROID;
5967 GfxAction[x][y] = ACTION_SHRINKING;
5968 GfxDir[x][y] = diagonal_move_dir;
5969 ChangeDelay[x][y] = change_delay;
5971 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5974 DrawLevelGraphicAnimation(x, y, graphic);
5975 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5977 if (Feld[newx][newy] == EL_ACID)
5979 SplashAcid(newx, newy);
5984 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5986 Store[newx][newy] = EL_EMC_ANDROID;
5987 GfxElement[newx][newy] = EL_EMC_ANDROID;
5988 GfxAction[newx][newy] = ACTION_GROWING;
5989 GfxDir[newx][newy] = diagonal_move_dir;
5990 ChangeDelay[newx][newy] = change_delay;
5992 graphic = el_act_dir2img(GfxElement[newx][newy],
5993 GfxAction[newx][newy], GfxDir[newx][newy]);
5995 DrawLevelGraphicAnimation(newx, newy, graphic);
5996 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6002 Feld[newx][newy] = EL_EMPTY;
6003 DrawLevelField(newx, newy);
6005 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6008 else if (!IS_FREE(newx, newy))
6011 if (IS_PLAYER(x, y))
6012 DrawPlayerField(x, y);
6014 DrawLevelField(x, y);
6020 else if (IS_CUSTOM_ELEMENT(element) &&
6021 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6023 int new_element = Feld[newx][newy];
6025 if (!IS_FREE(newx, newy))
6027 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6028 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6031 /* no element can dig solid indestructible elements */
6032 if (IS_INDESTRUCTIBLE(new_element) &&
6033 !IS_DIGGABLE(new_element) &&
6034 !IS_COLLECTIBLE(new_element))
6037 if (AmoebaNr[newx][newy] &&
6038 (new_element == EL_AMOEBA_FULL ||
6039 new_element == EL_BD_AMOEBA ||
6040 new_element == EL_AMOEBA_GROWING))
6042 AmoebaCnt[AmoebaNr[newx][newy]]--;
6043 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6046 if (IS_MOVING(newx, newy))
6047 RemoveMovingField(newx, newy);
6050 RemoveField(newx, newy);
6051 DrawLevelField(newx, newy);
6054 /* if digged element was about to explode, prevent the explosion */
6055 ExplodeField[newx][newy] = EX_TYPE_NONE;
6057 PlayLevelSoundAction(x, y, action);
6060 Store[newx][newy] = EL_EMPTY;
6062 /* this makes it possible to leave the removed element again */
6063 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6064 Store[newx][newy] = new_element;
6066 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6068 int move_leave_element = element_info[element].move_leave_element;
6070 /* this makes it possible to leave the removed element again */
6071 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6072 new_element : move_leave_element);
6076 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6078 RunnerVisit[x][y] = FrameCounter;
6079 PlayerVisit[x][y] /= 8; /* expire player visit path */
6082 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6084 if (!IS_FREE(newx, newy))
6086 if (IS_PLAYER(x, y))
6087 DrawPlayerField(x, y);
6089 DrawLevelField(x, y);
6095 boolean wanna_flame = !RND(10);
6096 int dx = newx - x, dy = newy - y;
6097 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6098 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6099 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6100 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6101 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6102 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6105 IS_CLASSIC_ENEMY(element1) ||
6106 IS_CLASSIC_ENEMY(element2)) &&
6107 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6108 element1 != EL_FLAMES && element2 != EL_FLAMES)
6110 ResetGfxAnimation(x, y);
6111 GfxAction[x][y] = ACTION_ATTACKING;
6113 if (IS_PLAYER(x, y))
6114 DrawPlayerField(x, y);
6116 DrawLevelField(x, y);
6118 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6120 MovDelay[x][y] = 50;
6124 RemoveField(newx, newy);
6126 Feld[newx][newy] = EL_FLAMES;
6127 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6130 RemoveField(newx1, newy1);
6132 Feld[newx1][newy1] = EL_FLAMES;
6134 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6137 RemoveField(newx2, newy2);
6139 Feld[newx2][newy2] = EL_FLAMES;
6146 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6147 Feld[newx][newy] == EL_DIAMOND)
6149 if (IS_MOVING(newx, newy))
6150 RemoveMovingField(newx, newy);
6153 Feld[newx][newy] = EL_EMPTY;
6154 DrawLevelField(newx, newy);
6157 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6159 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6160 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6162 if (AmoebaNr[newx][newy])
6164 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6165 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6166 Feld[newx][newy] == EL_BD_AMOEBA)
6167 AmoebaCnt[AmoebaNr[newx][newy]]--;
6172 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6174 RemoveMovingField(newx, newy);
6177 if (IS_MOVING(newx, newy))
6179 RemoveMovingField(newx, newy);
6184 Feld[newx][newy] = EL_EMPTY;
6185 DrawLevelField(newx, newy);
6188 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6190 else if ((element == EL_PACMAN || element == EL_MOLE)
6191 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6193 if (AmoebaNr[newx][newy])
6195 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6196 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6197 Feld[newx][newy] == EL_BD_AMOEBA)
6198 AmoebaCnt[AmoebaNr[newx][newy]]--;
6201 if (element == EL_MOLE)
6203 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6204 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6206 ResetGfxAnimation(x, y);
6207 GfxAction[x][y] = ACTION_DIGGING;
6208 DrawLevelField(x, y);
6210 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6212 return; /* wait for shrinking amoeba */
6214 else /* element == EL_PACMAN */
6216 Feld[newx][newy] = EL_EMPTY;
6217 DrawLevelField(newx, newy);
6218 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6221 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6222 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6223 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6225 /* wait for shrinking amoeba to completely disappear */
6228 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6230 /* object was running against a wall */
6235 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6236 if (move_pattern & MV_ANY_DIRECTION &&
6237 move_pattern == MovDir[x][y])
6239 int blocking_element =
6240 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6242 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6245 element = Feld[x][y]; /* element might have changed */
6249 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6250 DrawLevelElementAnimation(x, y, element);
6252 if (DONT_TOUCH(element))
6253 TestIfBadThingTouchesPlayer(x, y);
6258 InitMovingField(x, y, MovDir[x][y]);
6260 PlayLevelSoundAction(x, y, ACTION_MOVING);
6264 ContinueMoving(x, y);
6267 void ContinueMoving(int x, int y)
6269 int element = Feld[x][y];
6270 struct ElementInfo *ei = &element_info[element];
6271 int direction = MovDir[x][y];
6272 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6273 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6274 int newx = x + dx, newy = y + dy;
6275 int stored = Store[x][y];
6276 int stored_new = Store[newx][newy];
6277 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6278 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6279 boolean last_line = (newy == lev_fieldy - 1);
6281 MovPos[x][y] += getElementMoveStepsize(x, y);
6283 if (pushed_by_player) /* special case: moving object pushed by player */
6284 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6286 if (ABS(MovPos[x][y]) < TILEX)
6288 DrawLevelField(x, y);
6290 return; /* element is still moving */
6293 /* element reached destination field */
6295 Feld[x][y] = EL_EMPTY;
6296 Feld[newx][newy] = element;
6297 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6299 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6301 element = Feld[newx][newy] = EL_ACID;
6303 else if (element == EL_MOLE)
6305 Feld[x][y] = EL_SAND;
6307 DrawLevelFieldCrumbledSandNeighbours(x, y);
6309 else if (element == EL_QUICKSAND_FILLING)
6311 element = Feld[newx][newy] = get_next_element(element);
6312 Store[newx][newy] = Store[x][y];
6314 else if (element == EL_QUICKSAND_EMPTYING)
6316 Feld[x][y] = get_next_element(element);
6317 element = Feld[newx][newy] = Store[x][y];
6319 else if (element == EL_MAGIC_WALL_FILLING)
6321 element = Feld[newx][newy] = get_next_element(element);
6322 if (!game.magic_wall_active)
6323 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6324 Store[newx][newy] = Store[x][y];
6326 else if (element == EL_MAGIC_WALL_EMPTYING)
6328 Feld[x][y] = get_next_element(element);
6329 if (!game.magic_wall_active)
6330 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6331 element = Feld[newx][newy] = Store[x][y];
6333 #if USE_NEW_CUSTOM_VALUE
6334 InitField(newx, newy, FALSE);
6337 else if (element == EL_BD_MAGIC_WALL_FILLING)
6339 element = Feld[newx][newy] = get_next_element(element);
6340 if (!game.magic_wall_active)
6341 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6342 Store[newx][newy] = Store[x][y];
6344 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6346 Feld[x][y] = get_next_element(element);
6347 if (!game.magic_wall_active)
6348 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6349 element = Feld[newx][newy] = Store[x][y];
6351 #if USE_NEW_CUSTOM_VALUE
6352 InitField(newx, newy, FALSE);
6355 else if (element == EL_AMOEBA_DROPPING)
6357 Feld[x][y] = get_next_element(element);
6358 element = Feld[newx][newy] = Store[x][y];
6360 else if (element == EL_SOKOBAN_OBJECT)
6363 Feld[x][y] = Back[x][y];
6365 if (Back[newx][newy])
6366 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6368 Back[x][y] = Back[newx][newy] = 0;
6371 Store[x][y] = EL_EMPTY;
6376 MovDelay[newx][newy] = 0;
6379 if (CAN_CHANGE_OR_HAS_ACTION(element))
6381 if (CAN_CHANGE(element))
6384 /* copy element change control values to new field */
6385 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6386 ChangePage[newx][newy] = ChangePage[x][y];
6387 ChangeCount[newx][newy] = ChangeCount[x][y];
6388 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6391 #if USE_NEW_CUSTOM_VALUE
6392 CustomValue[newx][newy] = CustomValue[x][y];
6398 #if USE_NEW_CUSTOM_VALUE
6399 CustomValue[newx][newy] = CustomValue[x][y];
6403 ChangeDelay[x][y] = 0;
6404 ChangePage[x][y] = -1;
6405 ChangeCount[x][y] = 0;
6406 ChangeEvent[x][y] = -1;
6408 #if USE_NEW_CUSTOM_VALUE
6409 CustomValue[x][y] = 0;
6412 /* copy animation control values to new field */
6413 GfxFrame[newx][newy] = GfxFrame[x][y];
6414 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6415 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6416 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6418 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6420 /* some elements can leave other elements behind after moving */
6422 if (ei->move_leave_element != EL_EMPTY &&
6423 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6424 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6426 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6427 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6428 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6431 int move_leave_element = ei->move_leave_element;
6435 /* this makes it possible to leave the removed element again */
6436 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6437 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6439 /* this makes it possible to leave the removed element again */
6440 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6441 move_leave_element = stored;
6444 /* this makes it possible to leave the removed element again */
6445 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6446 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6447 move_leave_element = stored;
6450 Feld[x][y] = move_leave_element;
6452 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6453 MovDir[x][y] = direction;
6455 InitField(x, y, FALSE);
6457 if (GFX_CRUMBLED(Feld[x][y]))
6458 DrawLevelFieldCrumbledSandNeighbours(x, y);
6460 if (ELEM_IS_PLAYER(move_leave_element))
6461 RelocatePlayer(x, y, move_leave_element);
6464 /* do this after checking for left-behind element */
6465 ResetGfxAnimation(x, y); /* reset animation values for old field */
6467 if (!CAN_MOVE(element) ||
6468 (CAN_FALL(element) && direction == MV_DOWN &&
6469 (element == EL_SPRING ||
6470 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6471 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6472 GfxDir[x][y] = MovDir[newx][newy] = 0;
6474 DrawLevelField(x, y);
6475 DrawLevelField(newx, newy);
6477 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6479 /* prevent pushed element from moving on in pushed direction */
6480 if (pushed_by_player && CAN_MOVE(element) &&
6481 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6482 !(element_info[element].move_pattern & direction))
6483 TurnRound(newx, newy);
6485 /* prevent elements on conveyor belt from moving on in last direction */
6486 if (pushed_by_conveyor && CAN_FALL(element) &&
6487 direction & MV_HORIZONTAL)
6488 MovDir[newx][newy] = 0;
6490 if (!pushed_by_player)
6492 int nextx = newx + dx, nexty = newy + dy;
6493 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6495 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6497 if (CAN_FALL(element) && direction == MV_DOWN)
6498 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6500 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6501 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6504 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6506 TestIfBadThingTouchesPlayer(newx, newy);
6507 TestIfBadThingTouchesFriend(newx, newy);
6509 if (!IS_CUSTOM_ELEMENT(element))
6510 TestIfBadThingTouchesOtherBadThing(newx, newy);
6512 else if (element == EL_PENGUIN)
6513 TestIfFriendTouchesBadThing(newx, newy);
6515 /* give the player one last chance (one more frame) to move away */
6516 if (CAN_FALL(element) && direction == MV_DOWN &&
6517 (last_line || (!IS_FREE(x, newy + 1) &&
6518 (!IS_PLAYER(x, newy + 1) ||
6519 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6522 if (pushed_by_player && !game.use_change_when_pushing_bug)
6524 int push_side = MV_DIR_OPPOSITE(direction);
6525 struct PlayerInfo *player = PLAYERINFO(x, y);
6527 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6528 player->index_bit, push_side);
6529 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6530 player->index_bit, push_side);
6533 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6534 MovDelay[newx][newy] = 1;
6536 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6538 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6541 if (ChangePage[newx][newy] != -1) /* delayed change */
6543 int page = ChangePage[newx][newy];
6544 struct ElementChangeInfo *change = &ei->change_page[page];
6546 ChangePage[newx][newy] = -1;
6548 if (change->can_change)
6550 if (ChangeElement(newx, newy, element, page))
6552 if (change->post_change_function)
6553 change->post_change_function(newx, newy);
6557 if (change->has_action)
6558 ExecuteCustomElementAction(newx, newy, element, page);
6562 TestIfElementHitsCustomElement(newx, newy, direction);
6563 TestIfPlayerTouchesCustomElement(newx, newy);
6564 TestIfElementTouchesCustomElement(newx, newy);
6567 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6568 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6569 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6570 MV_DIR_OPPOSITE(direction));
6574 int AmoebeNachbarNr(int ax, int ay)
6577 int element = Feld[ax][ay];
6579 static int xy[4][2] =
6587 for (i = 0; i < NUM_DIRECTIONS; i++)
6589 int x = ax + xy[i][0];
6590 int y = ay + xy[i][1];
6592 if (!IN_LEV_FIELD(x, y))
6595 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6596 group_nr = AmoebaNr[x][y];
6602 void AmoebenVereinigen(int ax, int ay)
6604 int i, x, y, xx, yy;
6605 int new_group_nr = AmoebaNr[ax][ay];
6606 static int xy[4][2] =
6614 if (new_group_nr == 0)
6617 for (i = 0; i < NUM_DIRECTIONS; i++)
6622 if (!IN_LEV_FIELD(x, y))
6625 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6626 Feld[x][y] == EL_BD_AMOEBA ||
6627 Feld[x][y] == EL_AMOEBA_DEAD) &&
6628 AmoebaNr[x][y] != new_group_nr)
6630 int old_group_nr = AmoebaNr[x][y];
6632 if (old_group_nr == 0)
6635 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6636 AmoebaCnt[old_group_nr] = 0;
6637 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6638 AmoebaCnt2[old_group_nr] = 0;
6641 SCAN_PLAYFIELD(xx, yy)
6643 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6646 if (AmoebaNr[xx][yy] == old_group_nr)
6647 AmoebaNr[xx][yy] = new_group_nr;
6653 void AmoebeUmwandeln(int ax, int ay)
6657 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6659 int group_nr = AmoebaNr[ax][ay];
6664 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6665 printf("AmoebeUmwandeln(): This should never happen!\n");
6671 SCAN_PLAYFIELD(x, y)
6673 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6676 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6679 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6683 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6684 SND_AMOEBA_TURNING_TO_GEM :
6685 SND_AMOEBA_TURNING_TO_ROCK));
6690 static int xy[4][2] =
6698 for (i = 0; i < NUM_DIRECTIONS; i++)
6703 if (!IN_LEV_FIELD(x, y))
6706 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6708 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6709 SND_AMOEBA_TURNING_TO_GEM :
6710 SND_AMOEBA_TURNING_TO_ROCK));
6717 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6720 int group_nr = AmoebaNr[ax][ay];
6721 boolean done = FALSE;
6726 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6727 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6733 SCAN_PLAYFIELD(x, y)
6735 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6738 if (AmoebaNr[x][y] == group_nr &&
6739 (Feld[x][y] == EL_AMOEBA_DEAD ||
6740 Feld[x][y] == EL_BD_AMOEBA ||
6741 Feld[x][y] == EL_AMOEBA_GROWING))
6744 Feld[x][y] = new_element;
6745 InitField(x, y, FALSE);
6746 DrawLevelField(x, y);
6752 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6753 SND_BD_AMOEBA_TURNING_TO_ROCK :
6754 SND_BD_AMOEBA_TURNING_TO_GEM));
6757 void AmoebeWaechst(int x, int y)
6759 static unsigned long sound_delay = 0;
6760 static unsigned long sound_delay_value = 0;
6762 if (!MovDelay[x][y]) /* start new growing cycle */
6766 if (DelayReached(&sound_delay, sound_delay_value))
6768 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6769 sound_delay_value = 30;
6773 if (MovDelay[x][y]) /* wait some time before growing bigger */
6776 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6778 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6779 6 - MovDelay[x][y]);
6781 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6784 if (!MovDelay[x][y])
6786 Feld[x][y] = Store[x][y];
6788 DrawLevelField(x, y);
6793 void AmoebaDisappearing(int x, int y)
6795 static unsigned long sound_delay = 0;
6796 static unsigned long sound_delay_value = 0;
6798 if (!MovDelay[x][y]) /* start new shrinking cycle */
6802 if (DelayReached(&sound_delay, sound_delay_value))
6803 sound_delay_value = 30;
6806 if (MovDelay[x][y]) /* wait some time before shrinking */
6809 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6811 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6812 6 - MovDelay[x][y]);
6814 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6817 if (!MovDelay[x][y])
6819 Feld[x][y] = EL_EMPTY;
6820 DrawLevelField(x, y);
6822 /* don't let mole enter this field in this cycle;
6823 (give priority to objects falling to this field from above) */
6829 void AmoebeAbleger(int ax, int ay)
6832 int element = Feld[ax][ay];
6833 int graphic = el2img(element);
6834 int newax = ax, neway = ay;
6835 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6836 static int xy[4][2] =
6844 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6846 Feld[ax][ay] = EL_AMOEBA_DEAD;
6847 DrawLevelField(ax, ay);
6851 if (IS_ANIMATED(graphic))
6852 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6854 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6855 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6857 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6860 if (MovDelay[ax][ay])
6864 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6867 int x = ax + xy[start][0];
6868 int y = ay + xy[start][1];
6870 if (!IN_LEV_FIELD(x, y))
6873 if (IS_FREE(x, y) ||
6874 CAN_GROW_INTO(Feld[x][y]) ||
6875 Feld[x][y] == EL_QUICKSAND_EMPTY)
6881 if (newax == ax && neway == ay)
6884 else /* normal or "filled" (BD style) amoeba */
6887 boolean waiting_for_player = FALSE;
6889 for (i = 0; i < NUM_DIRECTIONS; i++)
6891 int j = (start + i) % 4;
6892 int x = ax + xy[j][0];
6893 int y = ay + xy[j][1];
6895 if (!IN_LEV_FIELD(x, y))
6898 if (IS_FREE(x, y) ||
6899 CAN_GROW_INTO(Feld[x][y]) ||
6900 Feld[x][y] == EL_QUICKSAND_EMPTY)
6906 else if (IS_PLAYER(x, y))
6907 waiting_for_player = TRUE;
6910 if (newax == ax && neway == ay) /* amoeba cannot grow */
6912 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6914 Feld[ax][ay] = EL_AMOEBA_DEAD;
6915 DrawLevelField(ax, ay);
6916 AmoebaCnt[AmoebaNr[ax][ay]]--;
6918 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6920 if (element == EL_AMOEBA_FULL)
6921 AmoebeUmwandeln(ax, ay);
6922 else if (element == EL_BD_AMOEBA)
6923 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6928 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6930 /* amoeba gets larger by growing in some direction */
6932 int new_group_nr = AmoebaNr[ax][ay];
6935 if (new_group_nr == 0)
6937 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6938 printf("AmoebeAbleger(): This should never happen!\n");
6943 AmoebaNr[newax][neway] = new_group_nr;
6944 AmoebaCnt[new_group_nr]++;
6945 AmoebaCnt2[new_group_nr]++;
6947 /* if amoeba touches other amoeba(s) after growing, unify them */
6948 AmoebenVereinigen(newax, neway);
6950 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6952 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6958 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6959 (neway == lev_fieldy - 1 && newax != ax))
6961 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6962 Store[newax][neway] = element;
6964 else if (neway == ay || element == EL_EMC_DRIPPER)
6966 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6968 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6972 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6973 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6974 Store[ax][ay] = EL_AMOEBA_DROP;
6975 ContinueMoving(ax, ay);
6979 DrawLevelField(newax, neway);
6982 void Life(int ax, int ay)
6986 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6989 int element = Feld[ax][ay];
6990 int graphic = el2img(element);
6991 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6993 boolean changed = FALSE;
6995 if (IS_ANIMATED(graphic))
6996 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7001 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7002 MovDelay[ax][ay] = life_time;
7004 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7007 if (MovDelay[ax][ay])
7011 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7013 int xx = ax+x1, yy = ay+y1;
7016 if (!IN_LEV_FIELD(xx, yy))
7019 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7021 int x = xx+x2, y = yy+y2;
7023 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7026 if (((Feld[x][y] == element ||
7027 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7029 (IS_FREE(x, y) && Stop[x][y]))
7033 if (xx == ax && yy == ay) /* field in the middle */
7035 if (nachbarn < life_parameter[0] ||
7036 nachbarn > life_parameter[1])
7038 Feld[xx][yy] = EL_EMPTY;
7040 DrawLevelField(xx, yy);
7041 Stop[xx][yy] = TRUE;
7045 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7046 { /* free border field */
7047 if (nachbarn >= life_parameter[2] &&
7048 nachbarn <= life_parameter[3])
7050 Feld[xx][yy] = element;
7051 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7053 DrawLevelField(xx, yy);
7054 Stop[xx][yy] = TRUE;
7061 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7062 SND_GAME_OF_LIFE_GROWING);
7065 static void InitRobotWheel(int x, int y)
7067 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7070 static void RunRobotWheel(int x, int y)
7072 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7075 static void StopRobotWheel(int x, int y)
7077 if (ZX == x && ZY == y)
7081 static void InitTimegateWheel(int x, int y)
7083 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7086 static void RunTimegateWheel(int x, int y)
7088 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7091 static void InitMagicBallDelay(int x, int y)
7094 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7096 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7100 static void ActivateMagicBall(int bx, int by)
7104 if (level.ball_random)
7106 int pos_border = RND(8); /* select one of the eight border elements */
7107 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7108 int xx = pos_content % 3;
7109 int yy = pos_content / 3;
7114 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7115 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7119 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7121 int xx = x - bx + 1;
7122 int yy = y - by + 1;
7124 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7125 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7129 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7132 static void InitDiagonalMovingElement(int x, int y)
7135 MovDelay[x][y] = level.android_move_time;
7139 void CheckExit(int x, int y)
7141 if (local_player->gems_still_needed > 0 ||
7142 local_player->sokobanfields_still_needed > 0 ||
7143 local_player->lights_still_needed > 0)
7145 int element = Feld[x][y];
7146 int graphic = el2img(element);
7148 if (IS_ANIMATED(graphic))
7149 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7154 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7157 Feld[x][y] = EL_EXIT_OPENING;
7159 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7162 void CheckExitSP(int x, int y)
7164 if (local_player->gems_still_needed > 0)
7166 int element = Feld[x][y];
7167 int graphic = el2img(element);
7169 if (IS_ANIMATED(graphic))
7170 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7175 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7178 Feld[x][y] = EL_SP_EXIT_OPENING;
7180 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7183 static void CloseAllOpenTimegates()
7188 SCAN_PLAYFIELD(x, y)
7190 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7193 int element = Feld[x][y];
7195 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7197 Feld[x][y] = EL_TIMEGATE_CLOSING;
7199 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7204 void EdelsteinFunkeln(int x, int y)
7206 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7209 if (Feld[x][y] == EL_BD_DIAMOND)
7212 if (MovDelay[x][y] == 0) /* next animation frame */
7213 MovDelay[x][y] = 11 * !SimpleRND(500);
7215 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7219 if (setup.direct_draw && MovDelay[x][y])
7220 SetDrawtoField(DRAW_BUFFERED);
7222 DrawLevelElementAnimation(x, y, Feld[x][y]);
7224 if (MovDelay[x][y] != 0)
7226 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7227 10 - MovDelay[x][y]);
7229 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7231 if (setup.direct_draw)
7235 dest_x = FX + SCREENX(x) * TILEX;
7236 dest_y = FY + SCREENY(y) * TILEY;
7238 BlitBitmap(drawto_field, window,
7239 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7240 SetDrawtoField(DRAW_DIRECT);
7246 void MauerWaechst(int x, int y)
7250 if (!MovDelay[x][y]) /* next animation frame */
7251 MovDelay[x][y] = 3 * delay;
7253 if (MovDelay[x][y]) /* wait some time before next frame */
7257 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7259 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7260 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7262 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7265 if (!MovDelay[x][y])
7267 if (MovDir[x][y] == MV_LEFT)
7269 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7270 DrawLevelField(x - 1, y);
7272 else if (MovDir[x][y] == MV_RIGHT)
7274 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7275 DrawLevelField(x + 1, y);
7277 else if (MovDir[x][y] == MV_UP)
7279 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7280 DrawLevelField(x, y - 1);
7284 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7285 DrawLevelField(x, y + 1);
7288 Feld[x][y] = Store[x][y];
7290 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7291 DrawLevelField(x, y);
7296 void MauerAbleger(int ax, int ay)
7298 int element = Feld[ax][ay];
7299 int graphic = el2img(element);
7300 boolean oben_frei = FALSE, unten_frei = FALSE;
7301 boolean links_frei = FALSE, rechts_frei = FALSE;
7302 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7303 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7304 boolean new_wall = FALSE;
7306 if (IS_ANIMATED(graphic))
7307 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7309 if (!MovDelay[ax][ay]) /* start building new wall */
7310 MovDelay[ax][ay] = 6;
7312 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7315 if (MovDelay[ax][ay])
7319 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7321 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7323 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7325 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7328 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7329 element == EL_EXPANDABLE_WALL_ANY)
7333 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7334 Store[ax][ay-1] = element;
7335 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7336 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7337 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7338 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7343 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7344 Store[ax][ay+1] = element;
7345 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7346 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7347 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7348 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7353 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7354 element == EL_EXPANDABLE_WALL_ANY ||
7355 element == EL_EXPANDABLE_WALL)
7359 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7360 Store[ax-1][ay] = element;
7361 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7362 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7363 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7364 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7370 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7371 Store[ax+1][ay] = element;
7372 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7373 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7374 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7375 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7380 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7381 DrawLevelField(ax, ay);
7383 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7385 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7386 unten_massiv = TRUE;
7387 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7388 links_massiv = TRUE;
7389 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7390 rechts_massiv = TRUE;
7392 if (((oben_massiv && unten_massiv) ||
7393 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7394 element == EL_EXPANDABLE_WALL) &&
7395 ((links_massiv && rechts_massiv) ||
7396 element == EL_EXPANDABLE_WALL_VERTICAL))
7397 Feld[ax][ay] = EL_WALL;
7400 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7403 void CheckForDragon(int x, int y)
7406 boolean dragon_found = FALSE;
7407 static int xy[4][2] =
7415 for (i = 0; i < NUM_DIRECTIONS; i++)
7417 for (j = 0; j < 4; j++)
7419 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7421 if (IN_LEV_FIELD(xx, yy) &&
7422 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7424 if (Feld[xx][yy] == EL_DRAGON)
7425 dragon_found = TRUE;
7434 for (i = 0; i < NUM_DIRECTIONS; i++)
7436 for (j = 0; j < 3; j++)
7438 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7440 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7442 Feld[xx][yy] = EL_EMPTY;
7443 DrawLevelField(xx, yy);
7452 static void InitBuggyBase(int x, int y)
7454 int element = Feld[x][y];
7455 int activating_delay = FRAMES_PER_SECOND / 4;
7458 (element == EL_SP_BUGGY_BASE ?
7459 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7460 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7462 element == EL_SP_BUGGY_BASE_ACTIVE ?
7463 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7466 static void WarnBuggyBase(int x, int y)
7469 static int xy[4][2] =
7477 for (i = 0; i < NUM_DIRECTIONS; i++)
7479 int xx = x + xy[i][0];
7480 int yy = y + xy[i][1];
7482 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7484 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7491 static void InitTrap(int x, int y)
7493 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7496 static void ActivateTrap(int x, int y)
7498 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7501 static void ChangeActiveTrap(int x, int y)
7503 int graphic = IMG_TRAP_ACTIVE;
7505 /* if new animation frame was drawn, correct crumbled sand border */
7506 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7507 DrawLevelFieldCrumbledSand(x, y);
7510 static int getSpecialActionElement(int element, int number, int base_element)
7512 return (element != EL_EMPTY ? element :
7513 number != -1 ? base_element + number - 1 :
7517 static int getModifiedActionNumber(int value_old, int operator, int operand,
7518 int value_min, int value_max)
7520 int value_new = (operator == CA_MODE_SET ? operand :
7521 operator == CA_MODE_ADD ? value_old + operand :
7522 operator == CA_MODE_SUBTRACT ? value_old - operand :
7523 operator == CA_MODE_MULTIPLY ? value_old * operand :
7524 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7525 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7528 return (value_new < value_min ? value_min :
7529 value_new > value_max ? value_max :
7533 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7535 struct ElementInfo *ei = &element_info[element];
7536 struct ElementChangeInfo *change = &ei->change_page[page];
7537 int action_type = change->action_type;
7538 int action_mode = change->action_mode;
7539 int action_arg = change->action_arg;
7542 if (!change->has_action)
7545 /* ---------- determine action paramater values -------------------------- */
7547 int level_time_value =
7548 (level.time > 0 ? TimeLeft :
7551 int action_arg_element =
7552 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7553 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7554 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7557 int action_arg_direction =
7558 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7559 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7560 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7561 change->actual_trigger_side :
7562 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7563 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7566 int action_arg_number_min =
7567 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7570 int action_arg_number_max =
7571 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7572 action_type == CA_SET_LEVEL_GEMS ? 999 :
7573 action_type == CA_SET_LEVEL_TIME ? 9999 :
7574 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7575 action_type == CA_SET_CE_SCORE ? 9999 :
7576 action_type == CA_SET_CE_VALUE ? 9999 :
7579 int action_arg_number_reset =
7580 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7581 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7582 action_type == CA_SET_LEVEL_TIME ? level.time :
7583 action_type == CA_SET_LEVEL_SCORE ? 0 :
7584 action_type == CA_SET_CE_SCORE ? 0 :
7586 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7588 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7592 int action_arg_number =
7593 (action_arg <= CA_ARG_MAX ? action_arg :
7594 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7595 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7596 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7597 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7598 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7599 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7600 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7601 #if USE_NEW_CUSTOM_VALUE
7602 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7604 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7606 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7607 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7608 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7609 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7610 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7611 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7612 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7613 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7616 int action_arg_number_old =
7617 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7618 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7619 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7620 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7621 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7624 int action_arg_number_new =
7625 getModifiedActionNumber(action_arg_number_old,
7626 action_mode, action_arg_number,
7627 action_arg_number_min, action_arg_number_max);
7629 int trigger_player_bits =
7630 (change->actual_trigger_player >= EL_PLAYER_1 &&
7631 change->actual_trigger_player <= EL_PLAYER_4 ?
7632 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7635 int action_arg_player_bits =
7636 (action_arg >= CA_ARG_PLAYER_1 &&
7637 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7638 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7641 /* ---------- execute action -------------------------------------------- */
7650 /* ---------- level actions ------------------------------------------- */
7652 case CA_RESTART_LEVEL:
7654 game.restart_level = TRUE;
7659 case CA_SHOW_ENVELOPE:
7661 int element = getSpecialActionElement(action_arg_element,
7662 action_arg_number, EL_ENVELOPE_1);
7664 if (IS_ENVELOPE(element))
7665 local_player->show_envelope = element;
7670 case CA_SET_LEVEL_TIME:
7672 if (level.time > 0) /* only modify limited time value */
7674 TimeLeft = action_arg_number_new;
7676 DrawGameValue_Time(TimeLeft);
7678 if (!TimeLeft && setup.time_limit)
7679 for (i = 0; i < MAX_PLAYERS; i++)
7680 KillPlayer(&stored_player[i]);
7686 case CA_SET_LEVEL_SCORE:
7688 local_player->score = action_arg_number_new;
7690 DrawGameValue_Score(local_player->score);
7695 case CA_SET_LEVEL_GEMS:
7697 local_player->gems_still_needed = action_arg_number_new;
7699 DrawGameValue_Emeralds(local_player->gems_still_needed);
7704 case CA_SET_LEVEL_GRAVITY:
7706 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7707 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7708 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7713 case CA_SET_LEVEL_WIND:
7715 game.wind_direction = action_arg_direction;
7720 /* ---------- player actions ------------------------------------------ */
7722 case CA_MOVE_PLAYER:
7724 /* automatically move to the next field in specified direction */
7725 for (i = 0; i < MAX_PLAYERS; i++)
7726 if (trigger_player_bits & (1 << i))
7727 stored_player[i].programmed_action = action_arg_direction;
7732 case CA_EXIT_PLAYER:
7734 for (i = 0; i < MAX_PLAYERS; i++)
7735 if (action_arg_player_bits & (1 << i))
7736 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7741 case CA_KILL_PLAYER:
7743 for (i = 0; i < MAX_PLAYERS; i++)
7744 if (action_arg_player_bits & (1 << i))
7745 KillPlayer(&stored_player[i]);
7750 case CA_SET_PLAYER_KEYS:
7752 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7753 int element = getSpecialActionElement(action_arg_element,
7754 action_arg_number, EL_KEY_1);
7756 if (IS_KEY(element))
7758 for (i = 0; i < MAX_PLAYERS; i++)
7760 if (trigger_player_bits & (1 << i))
7762 stored_player[i].key[KEY_NR(element)] = key_state;
7764 DrawGameValue_Keys(stored_player[i].key);
7766 redraw_mask |= REDRAW_DOOR_1;
7774 case CA_SET_PLAYER_SPEED:
7776 for (i = 0; i < MAX_PLAYERS; i++)
7778 if (trigger_player_bits & (1 << i))
7780 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7782 if (action_arg == CA_ARG_SPEED_FASTER &&
7783 stored_player[i].cannot_move)
7785 action_arg_number = STEPSIZE_VERY_SLOW;
7787 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7788 action_arg == CA_ARG_SPEED_FASTER)
7790 action_arg_number = 2;
7791 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7796 getModifiedActionNumber(move_stepsize,
7799 action_arg_number_min,
7800 action_arg_number_max);
7803 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7805 /* make sure that value is power of 2 */
7806 move_stepsize = (1 << log_2(move_stepsize));
7808 /* do no immediately change -- the player might just be moving */
7809 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7811 stored_player[i].cannot_move =
7812 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7820 case CA_SET_PLAYER_SHIELD:
7822 for (i = 0; i < MAX_PLAYERS; i++)
7824 if (trigger_player_bits & (1 << i))
7826 if (action_arg == CA_ARG_SHIELD_OFF)
7828 stored_player[i].shield_normal_time_left = 0;
7829 stored_player[i].shield_deadly_time_left = 0;
7831 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7833 stored_player[i].shield_normal_time_left = 999999;
7835 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7837 stored_player[i].shield_normal_time_left = 999999;
7838 stored_player[i].shield_deadly_time_left = 999999;
7846 case CA_SET_PLAYER_ARTWORK:
7848 for (i = 0; i < MAX_PLAYERS; i++)
7850 if (trigger_player_bits & (1 << i))
7852 int artwork_element = action_arg_element;
7854 if (action_arg == CA_ARG_ELEMENT_RESET)
7856 (level.use_artwork_element[i] ? level.artwork_element[i] :
7857 stored_player[i].element_nr);
7859 stored_player[i].artwork_element = artwork_element;
7861 SetPlayerWaiting(&stored_player[i], FALSE);
7863 /* set number of special actions for bored and sleeping animation */
7864 stored_player[i].num_special_action_bored =
7865 get_num_special_action(artwork_element,
7866 ACTION_BORING_1, ACTION_BORING_LAST);
7867 stored_player[i].num_special_action_sleeping =
7868 get_num_special_action(artwork_element,
7869 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7876 /* ---------- CE actions ---------------------------------------------- */
7878 case CA_SET_CE_SCORE:
7880 ei->collect_score = action_arg_number_new;
7885 case CA_SET_CE_VALUE:
7887 #if USE_NEW_CUSTOM_VALUE
7888 int last_custom_value = CustomValue[x][y];
7890 CustomValue[x][y] = action_arg_number_new;
7893 printf("::: Count == %d\n", CustomValue[x][y]);
7896 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7899 printf("::: CE_VALUE_GETS_ZERO\n");
7902 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7903 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7906 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7914 /* ---------- engine actions ------------------------------------------ */
7916 case CA_SET_ENGINE_SCAN_MODE:
7918 InitPlayfieldScanMode(action_arg);
7928 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7930 int previous_move_direction = MovDir[x][y];
7931 #if USE_NEW_CUSTOM_VALUE
7932 int last_ce_value = CustomValue[x][y];
7934 boolean add_player = (ELEM_IS_PLAYER(element) &&
7935 IS_WALKABLE(Feld[x][y]));
7937 /* check if element under player changes from accessible to unaccessible
7938 (needed for special case of dropping element which then changes) */
7939 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7940 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7949 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7950 RemoveMovingField(x, y);
7954 Feld[x][y] = element;
7956 ResetGfxAnimation(x, y);
7957 ResetRandomAnimationValue(x, y);
7959 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7960 MovDir[x][y] = previous_move_direction;
7962 #if USE_NEW_CUSTOM_VALUE
7963 if (element_info[Feld[x][y]].use_last_ce_value)
7964 CustomValue[x][y] = last_ce_value;
7967 InitField_WithBug1(x, y, FALSE);
7969 DrawLevelField(x, y);
7971 if (GFX_CRUMBLED(Feld[x][y]))
7972 DrawLevelFieldCrumbledSandNeighbours(x, y);
7975 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7976 if (ELEM_IS_PLAYER(element))
7977 RelocatePlayer(x, y, element);
7980 ChangeCount[x][y]++; /* count number of changes in the same frame */
7982 TestIfBadThingTouchesPlayer(x, y);
7983 TestIfPlayerTouchesCustomElement(x, y);
7984 TestIfElementTouchesCustomElement(x, y);
7987 static void CreateField(int x, int y, int element)
7989 CreateFieldExt(x, y, element, FALSE);
7992 static void CreateElementFromChange(int x, int y, int element)
7994 element = GET_VALID_RUNTIME_ELEMENT(element);
7996 #if USE_STOP_CHANGED_ELEMENTS
7997 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7999 int old_element = Feld[x][y];
8001 /* prevent changed element from moving in same engine frame
8002 unless both old and new element can either fall or move */
8003 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8004 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8009 CreateFieldExt(x, y, element, TRUE);
8012 static boolean ChangeElement(int x, int y, int element, int page)
8014 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8016 int old_element = Feld[x][y];
8018 /* always use default change event to prevent running into a loop */
8019 if (ChangeEvent[x][y] == -1)
8020 ChangeEvent[x][y] = CE_DELAY;
8022 if (ChangeEvent[x][y] == CE_DELAY)
8024 /* reset actual trigger element, trigger player and action element */
8025 change->actual_trigger_element = EL_EMPTY;
8026 change->actual_trigger_player = EL_PLAYER_1;
8027 change->actual_trigger_side = CH_SIDE_NONE;
8028 change->actual_trigger_ce_value = 0;
8031 /* do not change elements more than a specified maximum number of changes */
8032 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8035 ChangeCount[x][y]++; /* count number of changes in the same frame */
8037 if (change->explode)
8044 if (change->use_target_content)
8046 boolean complete_replace = TRUE;
8047 boolean can_replace[3][3];
8050 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8053 boolean is_walkable;
8054 boolean is_diggable;
8055 boolean is_collectible;
8056 boolean is_removable;
8057 boolean is_destructible;
8058 int ex = x + xx - 1;
8059 int ey = y + yy - 1;
8060 int content_element = change->target_content.e[xx][yy];
8063 can_replace[xx][yy] = TRUE;
8065 if (ex == x && ey == y) /* do not check changing element itself */
8068 if (content_element == EL_EMPTY_SPACE)
8070 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8075 if (!IN_LEV_FIELD(ex, ey))
8077 can_replace[xx][yy] = FALSE;
8078 complete_replace = FALSE;
8085 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8086 e = MovingOrBlocked2Element(ex, ey);
8088 is_empty = (IS_FREE(ex, ey) ||
8089 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8091 is_walkable = (is_empty || IS_WALKABLE(e));
8092 is_diggable = (is_empty || IS_DIGGABLE(e));
8093 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8094 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8095 is_removable = (is_diggable || is_collectible);
8097 can_replace[xx][yy] =
8098 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8099 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8100 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8101 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8102 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8103 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8104 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8106 if (!can_replace[xx][yy])
8107 complete_replace = FALSE;
8110 if (!change->only_if_complete || complete_replace)
8112 boolean something_has_changed = FALSE;
8114 if (change->only_if_complete && change->use_random_replace &&
8115 RND(100) < change->random_percentage)
8118 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8120 int ex = x + xx - 1;
8121 int ey = y + yy - 1;
8122 int content_element;
8124 if (can_replace[xx][yy] && (!change->use_random_replace ||
8125 RND(100) < change->random_percentage))
8127 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8128 RemoveMovingField(ex, ey);
8130 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8132 content_element = change->target_content.e[xx][yy];
8133 target_element = GET_TARGET_ELEMENT(content_element, change);
8135 CreateElementFromChange(ex, ey, target_element);
8137 something_has_changed = TRUE;
8139 /* for symmetry reasons, freeze newly created border elements */
8140 if (ex != x || ey != y)
8141 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8145 if (something_has_changed)
8147 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8148 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8154 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8156 if (element == EL_DIAGONAL_GROWING ||
8157 element == EL_DIAGONAL_SHRINKING)
8159 target_element = Store[x][y];
8161 Store[x][y] = EL_EMPTY;
8164 CreateElementFromChange(x, y, target_element);
8166 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8167 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8170 /* this uses direct change before indirect change */
8171 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8176 #if USE_NEW_DELAYED_ACTION
8178 static void HandleElementChange(int x, int y, int page)
8180 int element = MovingOrBlocked2Element(x, y);
8181 struct ElementInfo *ei = &element_info[element];
8182 struct ElementChangeInfo *change = &ei->change_page[page];
8185 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8186 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8189 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8190 x, y, element, element_info[element].token_name);
8191 printf("HandleElementChange(): This should never happen!\n");
8196 /* this can happen with classic bombs on walkable, changing elements */
8197 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8200 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8201 ChangeDelay[x][y] = 0;
8207 if (ChangeDelay[x][y] == 0) /* initialize element change */
8209 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8211 if (change->can_change)
8213 ResetGfxAnimation(x, y);
8214 ResetRandomAnimationValue(x, y);
8216 if (change->pre_change_function)
8217 change->pre_change_function(x, y);
8221 ChangeDelay[x][y]--;
8223 if (ChangeDelay[x][y] != 0) /* continue element change */
8225 if (change->can_change)
8227 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8229 if (IS_ANIMATED(graphic))
8230 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8232 if (change->change_function)
8233 change->change_function(x, y);
8236 else /* finish element change */
8238 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8240 page = ChangePage[x][y];
8241 ChangePage[x][y] = -1;
8243 change = &ei->change_page[page];
8246 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8248 ChangeDelay[x][y] = 1; /* try change after next move step */
8249 ChangePage[x][y] = page; /* remember page to use for change */
8254 if (change->can_change)
8256 if (ChangeElement(x, y, element, page))
8258 if (change->post_change_function)
8259 change->post_change_function(x, y);
8263 if (change->has_action)
8264 ExecuteCustomElementAction(x, y, element, page);
8270 static void HandleElementChange(int x, int y, int page)
8272 int element = MovingOrBlocked2Element(x, y);
8273 struct ElementInfo *ei = &element_info[element];
8274 struct ElementChangeInfo *change = &ei->change_page[page];
8277 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8280 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8281 x, y, element, element_info[element].token_name);
8282 printf("HandleElementChange(): This should never happen!\n");
8287 /* this can happen with classic bombs on walkable, changing elements */
8288 if (!CAN_CHANGE(element))
8291 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8292 ChangeDelay[x][y] = 0;
8298 if (ChangeDelay[x][y] == 0) /* initialize element change */
8300 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8302 ResetGfxAnimation(x, y);
8303 ResetRandomAnimationValue(x, y);
8305 if (change->pre_change_function)
8306 change->pre_change_function(x, y);
8309 ChangeDelay[x][y]--;
8311 if (ChangeDelay[x][y] != 0) /* continue element change */
8313 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8315 if (IS_ANIMATED(graphic))
8316 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8318 if (change->change_function)
8319 change->change_function(x, y);
8321 else /* finish element change */
8323 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8325 page = ChangePage[x][y];
8326 ChangePage[x][y] = -1;
8328 change = &ei->change_page[page];
8331 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8333 ChangeDelay[x][y] = 1; /* try change after next move step */
8334 ChangePage[x][y] = page; /* remember page to use for change */
8339 if (ChangeElement(x, y, element, page))
8341 if (change->post_change_function)
8342 change->post_change_function(x, y);
8349 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8350 int trigger_element,
8356 boolean change_done_any = FALSE;
8357 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8360 if (!(trigger_events[trigger_element][trigger_event]))
8363 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8365 int element = EL_CUSTOM_START + i;
8366 boolean change_done = FALSE;
8369 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8370 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8373 for (p = 0; p < element_info[element].num_change_pages; p++)
8375 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8377 if (change->can_change_or_has_action &&
8378 change->has_event[trigger_event] &&
8379 change->trigger_side & trigger_side &&
8380 change->trigger_player & trigger_player &&
8381 change->trigger_page & trigger_page_bits &&
8382 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8384 change->actual_trigger_element = trigger_element;
8385 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8386 change->actual_trigger_side = trigger_side;
8387 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8389 if ((change->can_change && !change_done) || change->has_action)
8394 SCAN_PLAYFIELD(x, y)
8396 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8399 if (Feld[x][y] == element)
8401 if (change->can_change && !change_done)
8403 ChangeDelay[x][y] = 1;
8404 ChangeEvent[x][y] = trigger_event;
8406 HandleElementChange(x, y, p);
8408 #if USE_NEW_DELAYED_ACTION
8409 else if (change->has_action)
8411 ExecuteCustomElementAction(x, y, element, p);
8412 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8415 if (change->has_action)
8417 ExecuteCustomElementAction(x, y, element, p);
8418 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8424 if (change->can_change)
8427 change_done_any = TRUE;
8434 return change_done_any;
8437 static boolean CheckElementChangeExt(int x, int y,
8439 int trigger_element,
8444 boolean change_done = FALSE;
8447 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8448 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8451 if (Feld[x][y] == EL_BLOCKED)
8453 Blocked2Moving(x, y, &x, &y);
8454 element = Feld[x][y];
8458 /* check if element has already changed */
8459 if (Feld[x][y] != element)
8462 /* check if element has already changed or is about to change after moving */
8463 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8464 Feld[x][y] != element) ||
8466 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8467 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8468 ChangePage[x][y] != -1)))
8472 for (p = 0; p < element_info[element].num_change_pages; p++)
8474 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8476 boolean check_trigger_element =
8477 (trigger_event == CE_TOUCHING_X ||
8478 trigger_event == CE_HITTING_X ||
8479 trigger_event == CE_HIT_BY_X);
8481 if (change->can_change_or_has_action &&
8482 change->has_event[trigger_event] &&
8483 change->trigger_side & trigger_side &&
8484 change->trigger_player & trigger_player &&
8485 (!check_trigger_element ||
8486 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8488 change->actual_trigger_element = trigger_element;
8489 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8490 change->actual_trigger_side = trigger_side;
8491 change->actual_trigger_ce_value = CustomValue[x][y];
8493 /* special case: trigger element not at (x,y) position for some events */
8494 if (check_trigger_element)
8506 { 0, 0 }, { 0, 0 }, { 0, 0 },
8510 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8511 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8513 change->actual_trigger_ce_value = CustomValue[xx][yy];
8516 if (change->can_change && !change_done)
8518 ChangeDelay[x][y] = 1;
8519 ChangeEvent[x][y] = trigger_event;
8521 HandleElementChange(x, y, p);
8525 #if USE_NEW_DELAYED_ACTION
8526 else if (change->has_action)
8528 ExecuteCustomElementAction(x, y, element, p);
8529 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8532 if (change->has_action)
8534 ExecuteCustomElementAction(x, y, element, p);
8535 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8544 static void PlayPlayerSound(struct PlayerInfo *player)
8546 int jx = player->jx, jy = player->jy;
8547 int sound_element = player->artwork_element;
8548 int last_action = player->last_action_waiting;
8549 int action = player->action_waiting;
8551 if (player->is_waiting)
8553 if (action != last_action)
8554 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8556 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8560 if (action != last_action)
8561 StopSound(element_info[sound_element].sound[last_action]);
8563 if (last_action == ACTION_SLEEPING)
8564 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8568 static void PlayAllPlayersSound()
8572 for (i = 0; i < MAX_PLAYERS; i++)
8573 if (stored_player[i].active)
8574 PlayPlayerSound(&stored_player[i]);
8577 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8579 boolean last_waiting = player->is_waiting;
8580 int move_dir = player->MovDir;
8582 player->last_action_waiting = player->action_waiting;
8586 if (!last_waiting) /* not waiting -> waiting */
8588 player->is_waiting = TRUE;
8590 player->frame_counter_bored =
8592 game.player_boring_delay_fixed +
8593 SimpleRND(game.player_boring_delay_random);
8594 player->frame_counter_sleeping =
8596 game.player_sleeping_delay_fixed +
8597 SimpleRND(game.player_sleeping_delay_random);
8599 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8602 if (game.player_sleeping_delay_fixed +
8603 game.player_sleeping_delay_random > 0 &&
8604 player->anim_delay_counter == 0 &&
8605 player->post_delay_counter == 0 &&
8606 FrameCounter >= player->frame_counter_sleeping)
8607 player->is_sleeping = TRUE;
8608 else if (game.player_boring_delay_fixed +
8609 game.player_boring_delay_random > 0 &&
8610 FrameCounter >= player->frame_counter_bored)
8611 player->is_bored = TRUE;
8613 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8614 player->is_bored ? ACTION_BORING :
8617 if (player->is_sleeping)
8619 if (player->num_special_action_sleeping > 0)
8621 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8623 int last_special_action = player->special_action_sleeping;
8624 int num_special_action = player->num_special_action_sleeping;
8625 int special_action =
8626 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8627 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8628 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8629 last_special_action + 1 : ACTION_SLEEPING);
8630 int special_graphic =
8631 el_act_dir2img(player->artwork_element, special_action, move_dir);
8633 player->anim_delay_counter =
8634 graphic_info[special_graphic].anim_delay_fixed +
8635 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8636 player->post_delay_counter =
8637 graphic_info[special_graphic].post_delay_fixed +
8638 SimpleRND(graphic_info[special_graphic].post_delay_random);
8640 player->special_action_sleeping = special_action;
8643 if (player->anim_delay_counter > 0)
8645 player->action_waiting = player->special_action_sleeping;
8646 player->anim_delay_counter--;
8648 else if (player->post_delay_counter > 0)
8650 player->post_delay_counter--;
8654 else if (player->is_bored)
8656 if (player->num_special_action_bored > 0)
8658 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8660 int special_action =
8661 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8662 int special_graphic =
8663 el_act_dir2img(player->artwork_element, special_action, move_dir);
8665 player->anim_delay_counter =
8666 graphic_info[special_graphic].anim_delay_fixed +
8667 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8668 player->post_delay_counter =
8669 graphic_info[special_graphic].post_delay_fixed +
8670 SimpleRND(graphic_info[special_graphic].post_delay_random);
8672 player->special_action_bored = special_action;
8675 if (player->anim_delay_counter > 0)
8677 player->action_waiting = player->special_action_bored;
8678 player->anim_delay_counter--;
8680 else if (player->post_delay_counter > 0)
8682 player->post_delay_counter--;
8687 else if (last_waiting) /* waiting -> not waiting */
8689 player->is_waiting = FALSE;
8690 player->is_bored = FALSE;
8691 player->is_sleeping = FALSE;
8693 player->frame_counter_bored = -1;
8694 player->frame_counter_sleeping = -1;
8696 player->anim_delay_counter = 0;
8697 player->post_delay_counter = 0;
8699 player->action_waiting = ACTION_DEFAULT;
8701 player->special_action_bored = ACTION_DEFAULT;
8702 player->special_action_sleeping = ACTION_DEFAULT;
8706 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8708 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8709 int left = player_action & JOY_LEFT;
8710 int right = player_action & JOY_RIGHT;
8711 int up = player_action & JOY_UP;
8712 int down = player_action & JOY_DOWN;
8713 int button1 = player_action & JOY_BUTTON_1;
8714 int button2 = player_action & JOY_BUTTON_2;
8715 int dx = (left ? -1 : right ? 1 : 0);
8716 int dy = (up ? -1 : down ? 1 : 0);
8718 if (!player->active || tape.pausing)
8724 snapped = SnapField(player, dx, dy);
8728 dropped = DropElement(player);
8730 moved = MovePlayer(player, dx, dy);
8733 if (tape.single_step && tape.recording && !tape.pausing)
8735 if (button1 || (dropped && !moved))
8737 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8738 SnapField(player, 0, 0); /* stop snapping */
8742 SetPlayerWaiting(player, FALSE);
8744 return player_action;
8748 /* no actions for this player (no input at player's configured device) */
8750 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8751 SnapField(player, 0, 0);
8752 CheckGravityMovementWhenNotMoving(player);
8754 if (player->MovPos == 0)
8755 SetPlayerWaiting(player, TRUE);
8757 if (player->MovPos == 0) /* needed for tape.playing */
8758 player->is_moving = FALSE;
8760 player->is_dropping = FALSE;
8761 player->is_dropping_pressed = FALSE;
8762 player->drop_pressed_delay = 0;
8768 void AdvanceFrameAndPlayerCounters(int player_nr)
8772 /* advance frame counters (global frame counter and time frame counter) */
8776 /* advance player counters (counters for move delay, move animation etc.) */
8777 for (i = 0; i < MAX_PLAYERS; i++)
8779 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8780 int move_delay_value = stored_player[i].move_delay_value;
8781 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8783 if (!advance_player_counters) /* not all players may be affected */
8786 #if USE_NEW_PLAYER_ANIM
8787 if (move_frames == 0) /* less than one move per game frame */
8789 int stepsize = TILEX / move_delay_value;
8790 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8791 int count = (stored_player[i].is_moving ?
8792 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8794 if (count % delay == 0)
8799 stored_player[i].Frame += move_frames;
8801 if (stored_player[i].MovPos != 0)
8802 stored_player[i].StepFrame += move_frames;
8804 if (stored_player[i].move_delay > 0)
8805 stored_player[i].move_delay--;
8807 /* due to bugs in previous versions, counter must count up, not down */
8808 if (stored_player[i].push_delay != -1)
8809 stored_player[i].push_delay++;
8811 if (stored_player[i].drop_delay > 0)
8812 stored_player[i].drop_delay--;
8814 if (stored_player[i].is_dropping_pressed)
8815 stored_player[i].drop_pressed_delay++;
8821 static unsigned long game_frame_delay = 0;
8822 unsigned long game_frame_delay_value;
8823 int magic_wall_x = 0, magic_wall_y = 0;
8824 int i, x, y, element, graphic;
8825 byte *recorded_player_action;
8826 byte summarized_player_action = 0;
8827 byte tape_action[MAX_PLAYERS];
8829 if (game_status != GAME_MODE_PLAYING)
8832 game_frame_delay_value =
8833 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8835 if (tape.playing && tape.warp_forward && !tape.pausing)
8836 game_frame_delay_value = 0;
8838 /* ---------- main game synchronization point ---------- */
8840 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8842 InitPlayfieldScanModeVars();
8844 if (ScreenMovPos == 0) /* screen currently aligned at tile position */
8846 struct PlayerInfo *player;
8847 int player_nr = game.centered_player_nr_next;
8849 if (game.centered_player_nr_next == -1)
8850 player_nr = local_player->index_nr;
8852 player = &stored_player[player_nr];
8854 if (!player->active)
8855 game.centered_player_nr_next = game.centered_player_nr;
8857 if (game.centered_player_nr != game.centered_player_nr_next)
8859 DrawRelocatePlayer(player, setup.quick_switch);
8861 game.centered_player_nr = game.centered_player_nr_next;
8865 #if USE_ONE_MORE_CHANGE_PER_FRAME
8866 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8868 SCAN_PLAYFIELD(x, y)
8870 ChangeCount[x][y] = 0;
8871 ChangeEvent[x][y] = -1;
8876 if (network_playing && !network_player_action_received)
8878 /* try to get network player actions in time */
8880 #if defined(NETWORK_AVALIABLE)
8881 /* last chance to get network player actions without main loop delay */
8885 /* game was quit by network peer */
8886 if (game_status != GAME_MODE_PLAYING)
8889 if (!network_player_action_received)
8890 return; /* failed to get network player actions in time */
8896 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8899 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8900 if (recorded_player_action == NULL && tape.pausing)
8904 for (i = 0; i < MAX_PLAYERS; i++)
8906 summarized_player_action |= stored_player[i].action;
8908 if (!network_playing)
8909 stored_player[i].effective_action = stored_player[i].action;
8912 #if defined(NETWORK_AVALIABLE)
8913 if (network_playing)
8914 SendToServer_MovePlayer(summarized_player_action);
8917 if (!options.network && !setup.team_mode)
8918 local_player->effective_action = summarized_player_action;
8920 if (recorded_player_action != NULL)
8921 for (i = 0; i < MAX_PLAYERS; i++)
8922 stored_player[i].effective_action = recorded_player_action[i];
8924 for (i = 0; i < MAX_PLAYERS; i++)
8926 tape_action[i] = stored_player[i].effective_action;
8928 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8929 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8932 /* only save actions from input devices, but not programmed actions */
8934 TapeRecordAction(tape_action);
8936 for (i = 0; i < MAX_PLAYERS; i++)
8938 int actual_player_action = stored_player[i].effective_action;
8941 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8942 - rnd_equinox_tetrachloride 048
8943 - rnd_equinox_tetrachloride_ii 096
8944 - rnd_emanuel_schmieg 002
8945 - doctor_sloan_ww 001, 020
8947 if (stored_player[i].MovPos == 0)
8948 CheckGravityMovement(&stored_player[i]);
8951 /* overwrite programmed action with tape action */
8952 if (stored_player[i].programmed_action)
8953 actual_player_action = stored_player[i].programmed_action;
8956 PlayerActions(&stored_player[i], actual_player_action);
8958 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8960 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8961 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8964 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8967 network_player_action_received = FALSE;
8969 ScrollScreen(NULL, SCROLL_GO_ON);
8971 /* for backwards compatibility, the following code emulates a fixed bug that
8972 occured when pushing elements (causing elements that just made their last
8973 pushing step to already (if possible) make their first falling step in the
8974 same game frame, which is bad); this code is also needed to use the famous
8975 "spring push bug" which is used in older levels and might be wanted to be
8976 used also in newer levels, but in this case the buggy pushing code is only
8977 affecting the "spring" element and no other elements */
8979 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8981 for (i = 0; i < MAX_PLAYERS; i++)
8983 struct PlayerInfo *player = &stored_player[i];
8987 if (player->active && player->is_pushing && player->is_moving &&
8989 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8990 Feld[x][y] == EL_SPRING))
8992 ContinueMoving(x, y);
8994 /* continue moving after pushing (this is actually a bug) */
8995 if (!IS_MOVING(x, y))
9004 SCAN_PLAYFIELD(x, y)
9006 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9009 ChangeCount[x][y] = 0;
9010 ChangeEvent[x][y] = -1;
9012 /* this must be handled before main playfield loop */
9013 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9016 if (MovDelay[x][y] <= 0)
9020 #if USE_NEW_SNAP_DELAY
9021 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9024 if (MovDelay[x][y] <= 0)
9027 DrawLevelField(x, y);
9029 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9035 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9037 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9038 printf("GameActions(): This should never happen!\n");
9040 ChangePage[x][y] = -1;
9045 if (WasJustMoving[x][y] > 0)
9046 WasJustMoving[x][y]--;
9047 if (WasJustFalling[x][y] > 0)
9048 WasJustFalling[x][y]--;
9049 if (CheckCollision[x][y] > 0)
9050 CheckCollision[x][y]--;
9054 /* reset finished pushing action (not done in ContinueMoving() to allow
9055 continuous pushing animation for elements with zero push delay) */
9056 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9058 ResetGfxAnimation(x, y);
9059 DrawLevelField(x, y);
9063 if (IS_BLOCKED(x, y))
9067 Blocked2Moving(x, y, &oldx, &oldy);
9068 if (!IS_MOVING(oldx, oldy))
9070 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9071 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9072 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9073 printf("GameActions(): This should never happen!\n");
9080 SCAN_PLAYFIELD(x, y)
9082 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9085 element = Feld[x][y];
9086 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9089 printf("::: %d,%d\n", x, y);
9091 if (element == EL_ROCK)
9092 printf("::: Yo man! Rocks can fall!\n");
9095 if (graphic_info[graphic].anim_global_sync)
9096 GfxFrame[x][y] = FrameCounter;
9097 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9099 int old_gfx_frame = GfxFrame[x][y];
9101 GfxFrame[x][y] = CustomValue[x][y];
9104 if (GfxFrame[x][y] != old_gfx_frame)
9106 DrawLevelGraphicAnimation(x, y, graphic);
9108 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9110 int old_gfx_frame = GfxFrame[x][y];
9112 GfxFrame[x][y] = element_info[element].collect_score;
9115 if (GfxFrame[x][y] != old_gfx_frame)
9117 DrawLevelGraphicAnimation(x, y, graphic);
9120 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9121 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9122 ResetRandomAnimationValue(x, y);
9124 SetRandomAnimationValue(x, y);
9126 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9128 if (IS_INACTIVE(element))
9130 if (IS_ANIMATED(graphic))
9131 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136 /* this may take place after moving, so 'element' may have changed */
9137 if (IS_CHANGING(x, y) &&
9138 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9140 int page = element_info[element].event_page_nr[CE_DELAY];
9142 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9146 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9150 if (element == EL_CUSTOM_255)
9151 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9155 HandleElementChange(x, y, page);
9157 if (CAN_CHANGE(element))
9158 HandleElementChange(x, y, page);
9160 if (HAS_ACTION(element))
9161 ExecuteCustomElementAction(x, y, element, page);
9166 element = Feld[x][y];
9167 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9170 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9174 element = Feld[x][y];
9175 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9177 if (IS_ANIMATED(graphic) &&
9180 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9182 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9183 EdelsteinFunkeln(x, y);
9185 else if ((element == EL_ACID ||
9186 element == EL_EXIT_OPEN ||
9187 element == EL_SP_EXIT_OPEN ||
9188 element == EL_SP_TERMINAL ||
9189 element == EL_SP_TERMINAL_ACTIVE ||
9190 element == EL_EXTRA_TIME ||
9191 element == EL_SHIELD_NORMAL ||
9192 element == EL_SHIELD_DEADLY) &&
9193 IS_ANIMATED(graphic))
9194 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9195 else if (IS_MOVING(x, y))
9196 ContinueMoving(x, y);
9197 else if (IS_ACTIVE_BOMB(element))
9198 CheckDynamite(x, y);
9199 else if (element == EL_AMOEBA_GROWING)
9200 AmoebeWaechst(x, y);
9201 else if (element == EL_AMOEBA_SHRINKING)
9202 AmoebaDisappearing(x, y);
9204 #if !USE_NEW_AMOEBA_CODE
9205 else if (IS_AMOEBALIVE(element))
9206 AmoebeAbleger(x, y);
9209 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9211 else if (element == EL_EXIT_CLOSED)
9213 else if (element == EL_SP_EXIT_CLOSED)
9215 else if (element == EL_EXPANDABLE_WALL_GROWING)
9217 else if (element == EL_EXPANDABLE_WALL ||
9218 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9219 element == EL_EXPANDABLE_WALL_VERTICAL ||
9220 element == EL_EXPANDABLE_WALL_ANY)
9222 else if (element == EL_FLAMES)
9223 CheckForDragon(x, y);
9224 else if (element == EL_EXPLOSION)
9225 ; /* drawing of correct explosion animation is handled separately */
9226 else if (element == EL_ELEMENT_SNAPPING ||
9227 element == EL_DIAGONAL_SHRINKING ||
9228 element == EL_DIAGONAL_GROWING)
9231 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9233 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9236 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9237 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9240 if (element == EL_CUSTOM_255 ||
9241 element == EL_CUSTOM_256)
9242 DrawLevelGraphicAnimation(x, y, graphic);
9245 if (IS_BELT_ACTIVE(element))
9246 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9248 if (game.magic_wall_active)
9250 int jx = local_player->jx, jy = local_player->jy;
9252 /* play the element sound at the position nearest to the player */
9253 if ((element == EL_MAGIC_WALL_FULL ||
9254 element == EL_MAGIC_WALL_ACTIVE ||
9255 element == EL_MAGIC_WALL_EMPTYING ||
9256 element == EL_BD_MAGIC_WALL_FULL ||
9257 element == EL_BD_MAGIC_WALL_ACTIVE ||
9258 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9259 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9267 #if USE_NEW_AMOEBA_CODE
9268 /* new experimental amoeba growth stuff */
9269 if (!(FrameCounter % 8))
9271 static unsigned long random = 1684108901;
9273 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9275 x = RND(lev_fieldx);
9276 y = RND(lev_fieldy);
9277 element = Feld[x][y];
9279 if (!IS_PLAYER(x,y) &&
9280 (element == EL_EMPTY ||
9281 CAN_GROW_INTO(element) ||
9282 element == EL_QUICKSAND_EMPTY ||
9283 element == EL_ACID_SPLASH_LEFT ||
9284 element == EL_ACID_SPLASH_RIGHT))
9286 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9287 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9288 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9289 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9290 Feld[x][y] = EL_AMOEBA_DROP;
9293 random = random * 129 + 1;
9299 if (game.explosions_delayed)
9302 game.explosions_delayed = FALSE;
9305 SCAN_PLAYFIELD(x, y)
9307 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9310 element = Feld[x][y];
9312 if (ExplodeField[x][y])
9313 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9314 else if (element == EL_EXPLOSION)
9315 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9317 ExplodeField[x][y] = EX_TYPE_NONE;
9320 game.explosions_delayed = TRUE;
9323 if (game.magic_wall_active)
9325 if (!(game.magic_wall_time_left % 4))
9327 int element = Feld[magic_wall_x][magic_wall_y];
9329 if (element == EL_BD_MAGIC_WALL_FULL ||
9330 element == EL_BD_MAGIC_WALL_ACTIVE ||
9331 element == EL_BD_MAGIC_WALL_EMPTYING)
9332 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9334 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9337 if (game.magic_wall_time_left > 0)
9339 game.magic_wall_time_left--;
9340 if (!game.magic_wall_time_left)
9343 SCAN_PLAYFIELD(x, y)
9345 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9348 element = Feld[x][y];
9350 if (element == EL_MAGIC_WALL_ACTIVE ||
9351 element == EL_MAGIC_WALL_FULL)
9353 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9354 DrawLevelField(x, y);
9356 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9357 element == EL_BD_MAGIC_WALL_FULL)
9359 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9360 DrawLevelField(x, y);
9364 game.magic_wall_active = FALSE;
9369 if (game.light_time_left > 0)
9371 game.light_time_left--;
9373 if (game.light_time_left == 0)
9374 RedrawAllLightSwitchesAndInvisibleElements();
9377 if (game.timegate_time_left > 0)
9379 game.timegate_time_left--;
9381 if (game.timegate_time_left == 0)
9382 CloseAllOpenTimegates();
9385 if (game.lenses_time_left > 0)
9387 game.lenses_time_left--;
9389 if (game.lenses_time_left == 0)
9390 RedrawAllInvisibleElementsForLenses();
9393 if (game.magnify_time_left > 0)
9395 game.magnify_time_left--;
9397 if (game.magnify_time_left == 0)
9398 RedrawAllInvisibleElementsForMagnifier();
9401 for (i = 0; i < MAX_PLAYERS; i++)
9403 struct PlayerInfo *player = &stored_player[i];
9405 if (SHIELD_ON(player))
9407 if (player->shield_deadly_time_left)
9408 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9409 else if (player->shield_normal_time_left)
9410 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9414 if (TimeFrames >= FRAMES_PER_SECOND)
9419 for (i = 0; i < MAX_PLAYERS; i++)
9421 struct PlayerInfo *player = &stored_player[i];
9423 if (SHIELD_ON(player))
9425 player->shield_normal_time_left--;
9427 if (player->shield_deadly_time_left > 0)
9428 player->shield_deadly_time_left--;
9432 if (!level.use_step_counter)
9440 if (TimeLeft <= 10 && setup.time_limit)
9441 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9443 DrawGameValue_Time(TimeLeft);
9445 if (!TimeLeft && setup.time_limit)
9446 for (i = 0; i < MAX_PLAYERS; i++)
9447 KillPlayer(&stored_player[i]);
9449 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9450 DrawGameValue_Time(TimePlayed);
9453 if (tape.recording || tape.playing)
9454 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9458 PlayAllPlayersSound();
9460 if (options.debug) /* calculate frames per second */
9462 static unsigned long fps_counter = 0;
9463 static int fps_frames = 0;
9464 unsigned long fps_delay_ms = Counter() - fps_counter;
9468 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9470 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9473 fps_counter = Counter();
9476 redraw_mask |= REDRAW_FPS;
9479 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9481 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9483 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9485 local_player->show_envelope = 0;
9488 /* use random number generator in every frame to make it less predictable */
9489 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9493 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9495 int min_x = x, min_y = y, max_x = x, max_y = y;
9498 for (i = 0; i < MAX_PLAYERS; i++)
9500 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9502 if (!stored_player[i].active || &stored_player[i] == player)
9505 min_x = MIN(min_x, jx);
9506 min_y = MIN(min_y, jy);
9507 max_x = MAX(max_x, jx);
9508 max_y = MAX(max_y, jy);
9511 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9514 static boolean AllPlayersInVisibleScreen()
9518 for (i = 0; i < MAX_PLAYERS; i++)
9520 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9522 if (!stored_player[i].active)
9525 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9532 void ScrollLevel(int dx, int dy)
9534 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9537 BlitBitmap(drawto_field, drawto_field,
9538 FX + TILEX * (dx == -1) - softscroll_offset,
9539 FY + TILEY * (dy == -1) - softscroll_offset,
9540 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9541 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9542 FX + TILEX * (dx == 1) - softscroll_offset,
9543 FY + TILEY * (dy == 1) - softscroll_offset);
9547 x = (dx == 1 ? BX1 : BX2);
9548 for (y = BY1; y <= BY2; y++)
9549 DrawScreenField(x, y);
9554 y = (dy == 1 ? BY1 : BY2);
9555 for (x = BX1; x <= BX2; x++)
9556 DrawScreenField(x, y);
9559 redraw_mask |= REDRAW_FIELD;
9562 static boolean canFallDown(struct PlayerInfo *player)
9564 int jx = player->jx, jy = player->jy;
9566 return (IN_LEV_FIELD(jx, jy + 1) &&
9567 (IS_FREE(jx, jy + 1) ||
9568 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9569 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9570 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9573 static boolean canPassField(int x, int y, int move_dir)
9575 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9576 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9577 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9580 int element = Feld[x][y];
9582 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9583 !CAN_MOVE(element) &&
9584 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9585 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9586 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9589 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9591 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9592 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9593 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9597 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9598 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9599 (IS_DIGGABLE(Feld[newx][newy]) ||
9600 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9601 canPassField(newx, newy, move_dir)));
9604 static void CheckGravityMovement(struct PlayerInfo *player)
9606 if (game.gravity && !player->programmed_action)
9608 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9609 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9610 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9611 int jx = player->jx, jy = player->jy;
9612 boolean player_is_moving_to_valid_field =
9613 (!player_is_snapping &&
9614 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9615 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9616 boolean player_can_fall_down = canFallDown(player);
9618 if (player_can_fall_down &&
9619 !player_is_moving_to_valid_field)
9620 player->programmed_action = MV_DOWN;
9624 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9626 return CheckGravityMovement(player);
9628 if (game.gravity && !player->programmed_action)
9630 int jx = player->jx, jy = player->jy;
9631 boolean field_under_player_is_free =
9632 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9633 boolean player_is_standing_on_valid_field =
9634 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9635 (IS_WALKABLE(Feld[jx][jy]) &&
9636 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9638 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9639 player->programmed_action = MV_DOWN;
9645 -----------------------------------------------------------------------------
9646 dx, dy: direction (non-diagonal) to try to move the player to
9647 real_dx, real_dy: direction as read from input device (can be diagonal)
9650 boolean MovePlayerOneStep(struct PlayerInfo *player,
9651 int dx, int dy, int real_dx, int real_dy)
9653 int jx = player->jx, jy = player->jy;
9654 int new_jx = jx + dx, new_jy = jy + dy;
9655 #if !USE_FIXED_DONT_RUN_INTO
9659 boolean player_can_move = !player->cannot_move;
9661 if (!player->active || (!dx && !dy))
9662 return MP_NO_ACTION;
9664 player->MovDir = (dx < 0 ? MV_LEFT :
9667 dy > 0 ? MV_DOWN : MV_NONE);
9669 if (!IN_LEV_FIELD(new_jx, new_jy))
9670 return MP_NO_ACTION;
9672 if (!player_can_move)
9675 if (player->MovPos == 0)
9677 player->is_moving = FALSE;
9678 player->is_digging = FALSE;
9679 player->is_collecting = FALSE;
9680 player->is_snapping = FALSE;
9681 player->is_pushing = FALSE;
9684 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9685 SnapField(player, 0, 0);
9689 return MP_NO_ACTION;
9694 if (!options.network && game.centered_player_nr == -1 &&
9695 !AllPlayersInSight(player, new_jx, new_jy))
9696 return MP_NO_ACTION;
9698 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9699 return MP_NO_ACTION;
9702 #if !USE_FIXED_DONT_RUN_INTO
9703 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9705 /* (moved to DigField()) */
9706 if (player_can_move && DONT_RUN_INTO(element))
9708 if (element == EL_ACID && dx == 0 && dy == 1)
9710 SplashAcid(new_jx, new_jy);
9711 Feld[jx][jy] = EL_PLAYER_1;
9712 InitMovingField(jx, jy, MV_DOWN);
9713 Store[jx][jy] = EL_ACID;
9714 ContinueMoving(jx, jy);
9718 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9724 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9726 #if USE_FIXED_DONT_RUN_INTO
9727 if (can_move == MP_DONT_RUN_INTO)
9731 if (can_move != MP_MOVING)
9734 #if USE_FIXED_DONT_RUN_INTO
9737 /* check if DigField() has caused relocation of the player */
9738 if (player->jx != jx || player->jy != jy)
9739 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9741 StorePlayer[jx][jy] = 0;
9742 player->last_jx = jx;
9743 player->last_jy = jy;
9744 player->jx = new_jx;
9745 player->jy = new_jy;
9746 StorePlayer[new_jx][new_jy] = player->element_nr;
9748 if (player->move_delay_value_next != -1)
9750 player->move_delay_value = player->move_delay_value_next;
9751 player->move_delay_value_next = -1;
9755 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9757 player->step_counter++;
9759 PlayerVisit[jx][jy] = FrameCounter;
9761 ScrollPlayer(player, SCROLL_INIT);
9766 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9768 int jx = player->jx, jy = player->jy;
9769 int old_jx = jx, old_jy = jy;
9770 int moved = MP_NO_ACTION;
9772 if (!player->active)
9777 if (player->MovPos == 0)
9779 player->is_moving = FALSE;
9780 player->is_digging = FALSE;
9781 player->is_collecting = FALSE;
9782 player->is_snapping = FALSE;
9783 player->is_pushing = FALSE;
9789 if (player->move_delay > 0)
9792 player->move_delay = -1; /* set to "uninitialized" value */
9794 /* store if player is automatically moved to next field */
9795 player->is_auto_moving = (player->programmed_action != MV_NONE);
9797 /* remove the last programmed player action */
9798 player->programmed_action = 0;
9802 /* should only happen if pre-1.2 tape recordings are played */
9803 /* this is only for backward compatibility */
9805 int original_move_delay_value = player->move_delay_value;
9808 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9812 /* scroll remaining steps with finest movement resolution */
9813 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9815 while (player->MovPos)
9817 ScrollPlayer(player, SCROLL_GO_ON);
9818 ScrollScreen(NULL, SCROLL_GO_ON);
9820 AdvanceFrameAndPlayerCounters(player->index_nr);
9826 player->move_delay_value = original_move_delay_value;
9829 if (player->last_move_dir & MV_HORIZONTAL)
9831 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9832 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9836 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9837 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9844 if (moved & MP_MOVING && !ScreenMovPos &&
9845 (player->index_nr == game.centered_player_nr ||
9846 game.centered_player_nr == -1))
9848 if (moved & MP_MOVING && !ScreenMovPos &&
9849 (player == local_player || !options.network))
9852 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9853 int offset = (setup.scroll_delay ? 3 : 0);
9855 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9857 /* actual player has left the screen -- scroll in that direction */
9858 if (jx != old_jx) /* player has moved horizontally */
9859 scroll_x += (jx - old_jx);
9860 else /* player has moved vertically */
9861 scroll_y += (jy - old_jy);
9865 if (jx != old_jx) /* player has moved horizontally */
9867 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9868 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9869 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9871 /* don't scroll over playfield boundaries */
9872 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9873 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9875 /* don't scroll more than one field at a time */
9876 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9878 /* don't scroll against the player's moving direction */
9879 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9880 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9881 scroll_x = old_scroll_x;
9883 else /* player has moved vertically */
9885 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9886 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9887 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9889 /* don't scroll over playfield boundaries */
9890 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9891 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9893 /* don't scroll more than one field at a time */
9894 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9896 /* don't scroll against the player's moving direction */
9897 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9898 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9899 scroll_y = old_scroll_y;
9903 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9906 if (!options.network && game.centered_player_nr == -1 &&
9907 !AllPlayersInVisibleScreen())
9909 scroll_x = old_scroll_x;
9910 scroll_y = old_scroll_y;
9914 if (!options.network && !AllPlayersInVisibleScreen())
9916 scroll_x = old_scroll_x;
9917 scroll_y = old_scroll_y;
9922 ScrollScreen(player, SCROLL_INIT);
9923 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9928 player->StepFrame = 0;
9930 if (moved & MP_MOVING)
9932 if (old_jx != jx && old_jy == jy)
9933 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9934 else if (old_jx == jx && old_jy != jy)
9935 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9937 DrawLevelField(jx, jy); /* for "crumbled sand" */
9939 player->last_move_dir = player->MovDir;
9940 player->is_moving = TRUE;
9941 player->is_snapping = FALSE;
9942 player->is_switching = FALSE;
9943 player->is_dropping = FALSE;
9944 player->is_dropping_pressed = FALSE;
9945 player->drop_pressed_delay = 0;
9949 CheckGravityMovementWhenNotMoving(player);
9951 player->is_moving = FALSE;
9953 /* at this point, the player is allowed to move, but cannot move right now
9954 (e.g. because of something blocking the way) -- ensure that the player
9955 is also allowed to move in the next frame (in old versions before 3.1.1,
9956 the player was forced to wait again for eight frames before next try) */
9958 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9959 player->move_delay = 0; /* allow direct movement in the next frame */
9962 if (player->move_delay == -1) /* not yet initialized by DigField() */
9963 player->move_delay = player->move_delay_value;
9965 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9967 TestIfPlayerTouchesBadThing(jx, jy);
9968 TestIfPlayerTouchesCustomElement(jx, jy);
9971 if (!player->active)
9972 RemovePlayer(player);
9977 void ScrollPlayer(struct PlayerInfo *player, int mode)
9979 int jx = player->jx, jy = player->jy;
9980 int last_jx = player->last_jx, last_jy = player->last_jy;
9981 int move_stepsize = TILEX / player->move_delay_value;
9983 #if USE_NEW_PLAYER_SPEED
9984 if (!player->active)
9987 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9990 if (!player->active || player->MovPos == 0)
9994 if (mode == SCROLL_INIT)
9996 player->actual_frame_counter = FrameCounter;
9997 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9999 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10000 Feld[last_jx][last_jy] == EL_EMPTY)
10002 int last_field_block_delay = 0; /* start with no blocking at all */
10003 int block_delay_adjustment = player->block_delay_adjustment;
10005 /* if player blocks last field, add delay for exactly one move */
10006 if (player->block_last_field)
10008 last_field_block_delay += player->move_delay_value;
10010 /* when blocking enabled, prevent moving up despite gravity */
10011 if (game.gravity && player->MovDir == MV_UP)
10012 block_delay_adjustment = -1;
10015 /* add block delay adjustment (also possible when not blocking) */
10016 last_field_block_delay += block_delay_adjustment;
10018 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10019 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10022 #if USE_NEW_PLAYER_SPEED
10023 if (player->MovPos != 0) /* player has not yet reached destination */
10029 else if (!FrameReached(&player->actual_frame_counter, 1))
10033 printf("::: player->MovPos: %d -> %d\n",
10035 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10038 #if USE_NEW_PLAYER_SPEED
10039 if (player->MovPos != 0)
10041 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10042 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10044 /* before DrawPlayer() to draw correct player graphic for this case */
10045 if (player->MovPos == 0)
10046 CheckGravityMovement(player);
10049 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10050 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10052 /* before DrawPlayer() to draw correct player graphic for this case */
10053 if (player->MovPos == 0)
10054 CheckGravityMovement(player);
10057 if (player->MovPos == 0) /* player reached destination field */
10060 printf("::: player reached destination field\n");
10063 if (player->move_delay_reset_counter > 0)
10065 player->move_delay_reset_counter--;
10067 if (player->move_delay_reset_counter == 0)
10069 /* continue with normal speed after quickly moving through gate */
10070 HALVE_PLAYER_SPEED(player);
10072 /* be able to make the next move without delay */
10073 player->move_delay = 0;
10077 player->last_jx = jx;
10078 player->last_jy = jy;
10080 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10081 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10082 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10084 DrawPlayer(player); /* needed here only to cleanup last field */
10085 RemovePlayer(player);
10087 if (local_player->friends_still_needed == 0 ||
10088 IS_SP_ELEMENT(Feld[jx][jy]))
10089 player->LevelSolved = player->GameOver = TRUE;
10092 /* this breaks one level: "machine", level 000 */
10094 int move_direction = player->MovDir;
10095 int enter_side = MV_DIR_OPPOSITE(move_direction);
10096 int leave_side = move_direction;
10097 int old_jx = last_jx;
10098 int old_jy = last_jy;
10099 int old_element = Feld[old_jx][old_jy];
10100 int new_element = Feld[jx][jy];
10102 if (IS_CUSTOM_ELEMENT(old_element))
10103 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10105 player->index_bit, leave_side);
10107 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10108 CE_PLAYER_LEAVES_X,
10109 player->index_bit, leave_side);
10111 if (IS_CUSTOM_ELEMENT(new_element))
10112 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10113 player->index_bit, enter_side);
10115 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10116 CE_PLAYER_ENTERS_X,
10117 player->index_bit, enter_side);
10119 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10120 CE_MOVE_OF_X, move_direction);
10123 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10125 TestIfPlayerTouchesBadThing(jx, jy);
10126 TestIfPlayerTouchesCustomElement(jx, jy);
10128 /* needed because pushed element has not yet reached its destination,
10129 so it would trigger a change event at its previous field location */
10130 if (!player->is_pushing)
10131 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10133 if (!player->active)
10134 RemovePlayer(player);
10137 if (level.use_step_counter)
10147 if (TimeLeft <= 10 && setup.time_limit)
10148 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10150 DrawGameValue_Time(TimeLeft);
10152 if (!TimeLeft && setup.time_limit)
10153 for (i = 0; i < MAX_PLAYERS; i++)
10154 KillPlayer(&stored_player[i]);
10156 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10157 DrawGameValue_Time(TimePlayed);
10160 if (tape.single_step && tape.recording && !tape.pausing &&
10161 !player->programmed_action)
10162 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10166 void ScrollScreen(struct PlayerInfo *player, int mode)
10168 static unsigned long screen_frame_counter = 0;
10170 if (mode == SCROLL_INIT)
10172 /* set scrolling step size according to actual player's moving speed */
10173 ScrollStepSize = TILEX / player->move_delay_value;
10175 screen_frame_counter = FrameCounter;
10176 ScreenMovDir = player->MovDir;
10177 ScreenMovPos = player->MovPos;
10178 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10181 else if (!FrameReached(&screen_frame_counter, 1))
10186 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10187 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10188 redraw_mask |= REDRAW_FIELD;
10191 ScreenMovDir = MV_NONE;
10194 void TestIfPlayerTouchesCustomElement(int x, int y)
10196 static int xy[4][2] =
10203 static int trigger_sides[4][2] =
10205 /* center side border side */
10206 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10207 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10208 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10209 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10211 static int touch_dir[4] =
10213 MV_LEFT | MV_RIGHT,
10218 int center_element = Feld[x][y]; /* should always be non-moving! */
10221 for (i = 0; i < NUM_DIRECTIONS; i++)
10223 int xx = x + xy[i][0];
10224 int yy = y + xy[i][1];
10225 int center_side = trigger_sides[i][0];
10226 int border_side = trigger_sides[i][1];
10227 int border_element;
10229 if (!IN_LEV_FIELD(xx, yy))
10232 if (IS_PLAYER(x, y))
10234 struct PlayerInfo *player = PLAYERINFO(x, y);
10236 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10237 border_element = Feld[xx][yy]; /* may be moving! */
10238 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10239 border_element = Feld[xx][yy];
10240 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10241 border_element = MovingOrBlocked2Element(xx, yy);
10243 continue; /* center and border element do not touch */
10245 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10246 player->index_bit, border_side);
10247 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10248 CE_PLAYER_TOUCHES_X,
10249 player->index_bit, border_side);
10251 else if (IS_PLAYER(xx, yy))
10253 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10255 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10257 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10258 continue; /* center and border element do not touch */
10261 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10262 player->index_bit, center_side);
10263 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10264 CE_PLAYER_TOUCHES_X,
10265 player->index_bit, center_side);
10271 #if USE_ELEMENT_TOUCHING_BUGFIX
10273 void TestIfElementTouchesCustomElement(int x, int y)
10275 static int xy[4][2] =
10282 static int trigger_sides[4][2] =
10284 /* center side border side */
10285 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10286 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10287 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10288 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10290 static int touch_dir[4] =
10292 MV_LEFT | MV_RIGHT,
10297 boolean change_center_element = FALSE;
10298 int center_element = Feld[x][y]; /* should always be non-moving! */
10299 int border_element_old[NUM_DIRECTIONS];
10302 for (i = 0; i < NUM_DIRECTIONS; i++)
10304 int xx = x + xy[i][0];
10305 int yy = y + xy[i][1];
10306 int border_element;
10308 border_element_old[i] = -1;
10310 if (!IN_LEV_FIELD(xx, yy))
10313 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10314 border_element = Feld[xx][yy]; /* may be moving! */
10315 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10316 border_element = Feld[xx][yy];
10317 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10318 border_element = MovingOrBlocked2Element(xx, yy);
10320 continue; /* center and border element do not touch */
10322 border_element_old[i] = border_element;
10325 for (i = 0; i < NUM_DIRECTIONS; i++)
10327 int xx = x + xy[i][0];
10328 int yy = y + xy[i][1];
10329 int center_side = trigger_sides[i][0];
10330 int border_element = border_element_old[i];
10332 if (border_element == -1)
10335 /* check for change of border element */
10336 CheckElementChangeBySide(xx, yy, border_element, center_element,
10337 CE_TOUCHING_X, center_side);
10340 for (i = 0; i < NUM_DIRECTIONS; i++)
10342 int border_side = trigger_sides[i][1];
10343 int border_element = border_element_old[i];
10345 if (border_element == -1)
10348 /* check for change of center element (but change it only once) */
10349 if (!change_center_element)
10350 change_center_element =
10351 CheckElementChangeBySide(x, y, center_element, border_element,
10352 CE_TOUCHING_X, border_side);
10358 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10360 static int xy[4][2] =
10367 static int trigger_sides[4][2] =
10369 /* center side border side */
10370 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10371 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10372 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10373 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10375 static int touch_dir[4] =
10377 MV_LEFT | MV_RIGHT,
10382 boolean change_center_element = FALSE;
10383 int center_element = Feld[x][y]; /* should always be non-moving! */
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_side = trigger_sides[i][1];
10392 int border_element;
10394 if (!IN_LEV_FIELD(xx, yy))
10397 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10398 border_element = Feld[xx][yy]; /* may be moving! */
10399 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10400 border_element = Feld[xx][yy];
10401 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10402 border_element = MovingOrBlocked2Element(xx, yy);
10404 continue; /* center and border element do not touch */
10406 /* check for change of center element (but change it only once) */
10407 if (!change_center_element)
10408 change_center_element =
10409 CheckElementChangeBySide(x, y, center_element, border_element,
10410 CE_TOUCHING_X, border_side);
10412 /* check for change of border element */
10413 CheckElementChangeBySide(xx, yy, border_element, center_element,
10414 CE_TOUCHING_X, center_side);
10420 void TestIfElementHitsCustomElement(int x, int y, int direction)
10422 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10423 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10424 int hitx = x + dx, hity = y + dy;
10425 int hitting_element = Feld[x][y];
10426 int touched_element;
10428 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10431 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10432 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10434 if (IN_LEV_FIELD(hitx, hity))
10436 int opposite_direction = MV_DIR_OPPOSITE(direction);
10437 int hitting_side = direction;
10438 int touched_side = opposite_direction;
10439 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10440 MovDir[hitx][hity] != direction ||
10441 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10447 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10448 CE_HITTING_X, touched_side);
10450 CheckElementChangeBySide(hitx, hity, touched_element,
10451 hitting_element, CE_HIT_BY_X, hitting_side);
10453 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10454 CE_HIT_BY_SOMETHING, opposite_direction);
10458 /* "hitting something" is also true when hitting the playfield border */
10459 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10460 CE_HITTING_SOMETHING, direction);
10464 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10466 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10467 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10468 int hitx = x + dx, hity = y + dy;
10469 int hitting_element = Feld[x][y];
10470 int touched_element;
10472 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10473 !IS_FREE(hitx, hity) &&
10474 (!IS_MOVING(hitx, hity) ||
10475 MovDir[hitx][hity] != direction ||
10476 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10479 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10483 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10487 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10488 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10490 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10491 EP_CAN_SMASH_EVERYTHING, direction);
10493 if (IN_LEV_FIELD(hitx, hity))
10495 int opposite_direction = MV_DIR_OPPOSITE(direction);
10496 int hitting_side = direction;
10497 int touched_side = opposite_direction;
10499 int touched_element = MovingOrBlocked2Element(hitx, hity);
10502 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10503 MovDir[hitx][hity] != direction ||
10504 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10513 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10514 CE_SMASHED_BY_SOMETHING, opposite_direction);
10516 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10517 CE_OTHER_IS_SMASHING, touched_side);
10519 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10520 CE_OTHER_GETS_SMASHED, hitting_side);
10526 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10528 int i, kill_x = -1, kill_y = -1;
10530 int bad_element = -1;
10531 static int test_xy[4][2] =
10538 static int test_dir[4] =
10546 for (i = 0; i < NUM_DIRECTIONS; i++)
10548 int test_x, test_y, test_move_dir, test_element;
10550 test_x = good_x + test_xy[i][0];
10551 test_y = good_y + test_xy[i][1];
10553 if (!IN_LEV_FIELD(test_x, test_y))
10557 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10559 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10561 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10562 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10564 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10565 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10569 bad_element = test_element;
10575 if (kill_x != -1 || kill_y != -1)
10577 if (IS_PLAYER(good_x, good_y))
10579 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10581 if (player->shield_deadly_time_left > 0 &&
10582 !IS_INDESTRUCTIBLE(bad_element))
10583 Bang(kill_x, kill_y);
10584 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10585 KillPlayer(player);
10588 Bang(good_x, good_y);
10592 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10594 int i, kill_x = -1, kill_y = -1;
10595 int bad_element = Feld[bad_x][bad_y];
10596 static int test_xy[4][2] =
10603 static int touch_dir[4] =
10605 MV_LEFT | MV_RIGHT,
10610 static int test_dir[4] =
10618 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10621 for (i = 0; i < NUM_DIRECTIONS; i++)
10623 int test_x, test_y, test_move_dir, test_element;
10625 test_x = bad_x + test_xy[i][0];
10626 test_y = bad_y + test_xy[i][1];
10627 if (!IN_LEV_FIELD(test_x, test_y))
10631 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10633 test_element = Feld[test_x][test_y];
10635 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10636 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10638 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10639 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10641 /* good thing is player or penguin that does not move away */
10642 if (IS_PLAYER(test_x, test_y))
10644 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10646 if (bad_element == EL_ROBOT && player->is_moving)
10647 continue; /* robot does not kill player if he is moving */
10649 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10651 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10652 continue; /* center and border element do not touch */
10659 else if (test_element == EL_PENGUIN)
10668 if (kill_x != -1 || kill_y != -1)
10670 if (IS_PLAYER(kill_x, kill_y))
10672 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10674 if (player->shield_deadly_time_left > 0 &&
10675 !IS_INDESTRUCTIBLE(bad_element))
10676 Bang(bad_x, bad_y);
10677 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10678 KillPlayer(player);
10681 Bang(kill_x, kill_y);
10685 void TestIfPlayerTouchesBadThing(int x, int y)
10687 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10690 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10692 TestIfGoodThingHitsBadThing(x, y, move_dir);
10695 void TestIfBadThingTouchesPlayer(int x, int y)
10697 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10700 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10702 TestIfBadThingHitsGoodThing(x, y, move_dir);
10705 void TestIfFriendTouchesBadThing(int x, int y)
10707 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10710 void TestIfBadThingTouchesFriend(int x, int y)
10712 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10715 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10717 int i, kill_x = bad_x, kill_y = bad_y;
10718 static int xy[4][2] =
10726 for (i = 0; i < NUM_DIRECTIONS; i++)
10730 x = bad_x + xy[i][0];
10731 y = bad_y + xy[i][1];
10732 if (!IN_LEV_FIELD(x, y))
10735 element = Feld[x][y];
10736 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10737 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10745 if (kill_x != bad_x || kill_y != bad_y)
10746 Bang(bad_x, bad_y);
10749 void KillPlayer(struct PlayerInfo *player)
10751 int jx = player->jx, jy = player->jy;
10753 if (!player->active)
10756 /* remove accessible field at the player's position */
10757 Feld[jx][jy] = EL_EMPTY;
10759 /* deactivate shield (else Bang()/Explode() would not work right) */
10760 player->shield_normal_time_left = 0;
10761 player->shield_deadly_time_left = 0;
10764 BuryPlayer(player);
10767 static void KillPlayerUnlessEnemyProtected(int x, int y)
10769 if (!PLAYER_ENEMY_PROTECTED(x, y))
10770 KillPlayer(PLAYERINFO(x, y));
10773 static void KillPlayerUnlessExplosionProtected(int x, int y)
10775 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10776 KillPlayer(PLAYERINFO(x, y));
10779 void BuryPlayer(struct PlayerInfo *player)
10781 int jx = player->jx, jy = player->jy;
10783 if (!player->active)
10786 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10787 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10789 player->GameOver = TRUE;
10790 RemovePlayer(player);
10793 void RemovePlayer(struct PlayerInfo *player)
10795 int jx = player->jx, jy = player->jy;
10796 int i, found = FALSE;
10798 player->present = FALSE;
10799 player->active = FALSE;
10801 if (!ExplodeField[jx][jy])
10802 StorePlayer[jx][jy] = 0;
10804 if (player->is_moving)
10805 DrawLevelField(player->last_jx, player->last_jy);
10807 for (i = 0; i < MAX_PLAYERS; i++)
10808 if (stored_player[i].active)
10812 AllPlayersGone = TRUE;
10818 #if USE_NEW_SNAP_DELAY
10819 static void setFieldForSnapping(int x, int y, int element, int direction)
10821 struct ElementInfo *ei = &element_info[element];
10822 int direction_bit = MV_DIR_TO_BIT(direction);
10823 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10824 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10825 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10827 Feld[x][y] = EL_ELEMENT_SNAPPING;
10828 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10830 ResetGfxAnimation(x, y);
10832 GfxElement[x][y] = element;
10833 GfxAction[x][y] = action;
10834 GfxDir[x][y] = direction;
10835 GfxFrame[x][y] = -1;
10840 =============================================================================
10841 checkDiagonalPushing()
10842 -----------------------------------------------------------------------------
10843 check if diagonal input device direction results in pushing of object
10844 (by checking if the alternative direction is walkable, diggable, ...)
10845 =============================================================================
10848 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10849 int x, int y, int real_dx, int real_dy)
10851 int jx, jy, dx, dy, xx, yy;
10853 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10856 /* diagonal direction: check alternative direction */
10861 xx = jx + (dx == 0 ? real_dx : 0);
10862 yy = jy + (dy == 0 ? real_dy : 0);
10864 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10868 =============================================================================
10870 -----------------------------------------------------------------------------
10871 x, y: field next to player (non-diagonal) to try to dig to
10872 real_dx, real_dy: direction as read from input device (can be diagonal)
10873 =============================================================================
10876 int DigField(struct PlayerInfo *player,
10877 int oldx, int oldy, int x, int y,
10878 int real_dx, int real_dy, int mode)
10880 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10881 boolean player_was_pushing = player->is_pushing;
10882 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10883 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10884 int jx = oldx, jy = oldy;
10885 int dx = x - jx, dy = y - jy;
10886 int nextx = x + dx, nexty = y + dy;
10887 int move_direction = (dx == -1 ? MV_LEFT :
10888 dx == +1 ? MV_RIGHT :
10890 dy == +1 ? MV_DOWN : MV_NONE);
10891 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10892 int dig_side = MV_DIR_OPPOSITE(move_direction);
10893 int old_element = Feld[jx][jy];
10894 #if USE_FIXED_DONT_RUN_INTO
10895 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10901 if (is_player) /* function can also be called by EL_PENGUIN */
10903 if (player->MovPos == 0)
10905 player->is_digging = FALSE;
10906 player->is_collecting = FALSE;
10909 if (player->MovPos == 0) /* last pushing move finished */
10910 player->is_pushing = FALSE;
10912 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10914 player->is_switching = FALSE;
10915 player->push_delay = -1;
10917 return MP_NO_ACTION;
10921 #if !USE_FIXED_DONT_RUN_INTO
10922 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10923 return MP_NO_ACTION;
10926 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10927 old_element = Back[jx][jy];
10929 /* in case of element dropped at player position, check background */
10930 else if (Back[jx][jy] != EL_EMPTY &&
10931 game.engine_version >= VERSION_IDENT(2,2,0,0))
10932 old_element = Back[jx][jy];
10935 #if USE_FIXED_DONT_RUN_INTO
10936 if (player_can_move && DONT_RUN_INTO(element))
10938 if (element == EL_ACID && dx == 0 && dy == 1)
10941 Feld[jx][jy] = EL_PLAYER_1;
10942 InitMovingField(jx, jy, MV_DOWN);
10943 Store[jx][jy] = EL_ACID;
10944 ContinueMoving(jx, jy);
10945 BuryPlayer(player);
10948 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10950 return MP_DONT_RUN_INTO;
10956 #if USE_FIXED_DONT_RUN_INTO
10957 if (player_can_move && DONT_RUN_INTO(element))
10959 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10961 return MP_DONT_RUN_INTO;
10966 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10967 return MP_NO_ACTION; /* field has no opening in this direction */
10969 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10970 return MP_NO_ACTION; /* field has no opening in this direction */
10973 #if USE_FIXED_DONT_RUN_INTO
10974 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10977 Feld[jx][jy] = EL_PLAYER_1;
10978 InitMovingField(jx, jy, MV_DOWN);
10979 Store[jx][jy] = EL_ACID;
10980 ContinueMoving(jx, jy);
10981 BuryPlayer(player);
10983 return MP_DONT_RUN_INTO;
10989 #if USE_FIXED_DONT_RUN_INTO
10990 if (player_can_move && DONT_RUN_INTO(element))
10992 if (element == EL_ACID && dx == 0 && dy == 1)
10995 Feld[jx][jy] = EL_PLAYER_1;
10996 InitMovingField(jx, jy, MV_DOWN);
10997 Store[jx][jy] = EL_ACID;
10998 ContinueMoving(jx, jy);
10999 BuryPlayer(player);
11002 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11004 return MP_DONT_RUN_INTO;
11009 #if USE_FIXED_DONT_RUN_INTO
11010 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11011 return MP_NO_ACTION;
11014 #if !USE_FIXED_DONT_RUN_INTO
11015 element = Feld[x][y];
11018 collect_count = element_info[element].collect_count_initial;
11020 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11021 return MP_NO_ACTION;
11023 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11024 player_can_move = player_can_move_or_snap;
11026 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11027 game.engine_version >= VERSION_IDENT(2,2,0,0))
11029 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11030 player->index_bit, dig_side);
11031 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11032 player->index_bit, dig_side);
11034 if (Feld[x][y] != element) /* field changed by snapping */
11037 return MP_NO_ACTION;
11040 if (game.gravity && is_player && !player->is_auto_moving &&
11041 canFallDown(player) && move_direction != MV_DOWN &&
11042 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11043 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11045 if (player_can_move &&
11046 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11048 int sound_element = SND_ELEMENT(element);
11049 int sound_action = ACTION_WALKING;
11051 if (IS_RND_GATE(element))
11053 if (!player->key[RND_GATE_NR(element)])
11054 return MP_NO_ACTION;
11056 else if (IS_RND_GATE_GRAY(element))
11058 if (!player->key[RND_GATE_GRAY_NR(element)])
11059 return MP_NO_ACTION;
11061 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11063 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11064 return MP_NO_ACTION;
11066 else if (element == EL_EXIT_OPEN ||
11067 element == EL_SP_EXIT_OPEN ||
11068 element == EL_SP_EXIT_OPENING)
11070 sound_action = ACTION_PASSING; /* player is passing exit */
11072 else if (element == EL_EMPTY)
11074 sound_action = ACTION_MOVING; /* nothing to walk on */
11077 /* play sound from background or player, whatever is available */
11078 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11079 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11081 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11083 else if (player_can_move &&
11084 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11086 if (!ACCESS_FROM(element, opposite_direction))
11087 return MP_NO_ACTION; /* field not accessible from this direction */
11089 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11090 return MP_NO_ACTION;
11092 if (IS_EM_GATE(element))
11094 if (!player->key[EM_GATE_NR(element)])
11095 return MP_NO_ACTION;
11097 else if (IS_EM_GATE_GRAY(element))
11099 if (!player->key[EM_GATE_GRAY_NR(element)])
11100 return MP_NO_ACTION;
11102 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11104 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11105 return MP_NO_ACTION;
11107 else if (IS_SP_PORT(element))
11109 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11110 element == EL_SP_GRAVITY_PORT_RIGHT ||
11111 element == EL_SP_GRAVITY_PORT_UP ||
11112 element == EL_SP_GRAVITY_PORT_DOWN)
11113 game.gravity = !game.gravity;
11114 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11115 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11116 element == EL_SP_GRAVITY_ON_PORT_UP ||
11117 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11118 game.gravity = TRUE;
11119 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11120 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11121 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11122 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11123 game.gravity = FALSE;
11126 /* automatically move to the next field with double speed */
11127 player->programmed_action = move_direction;
11129 if (player->move_delay_reset_counter == 0)
11131 player->move_delay_reset_counter = 2; /* two double speed steps */
11133 DOUBLE_PLAYER_SPEED(player);
11136 PlayLevelSoundAction(x, y, ACTION_PASSING);
11138 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11142 if (mode != DF_SNAP)
11144 GfxElement[x][y] = GFX_ELEMENT(element);
11145 player->is_digging = TRUE;
11148 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11150 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11151 player->index_bit, dig_side);
11153 if (mode == DF_SNAP)
11155 #if USE_NEW_SNAP_DELAY
11156 if (level.block_snap_field)
11157 setFieldForSnapping(x, y, element, move_direction);
11159 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11161 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11164 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11165 player->index_bit, dig_side);
11168 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11172 if (is_player && mode != DF_SNAP)
11174 GfxElement[x][y] = element;
11175 player->is_collecting = TRUE;
11178 if (element == EL_SPEED_PILL)
11180 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11182 else if (element == EL_EXTRA_TIME && level.time > 0)
11184 TimeLeft += level.extra_time;
11185 DrawGameValue_Time(TimeLeft);
11187 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11189 player->shield_normal_time_left += level.shield_normal_time;
11190 if (element == EL_SHIELD_DEADLY)
11191 player->shield_deadly_time_left += level.shield_deadly_time;
11193 else if (element == EL_DYNAMITE ||
11194 element == EL_EM_DYNAMITE ||
11195 element == EL_SP_DISK_RED)
11197 if (player->inventory_size < MAX_INVENTORY_SIZE)
11198 player->inventory_element[player->inventory_size++] = element;
11200 DrawGameValue_Dynamite(local_player->inventory_size);
11202 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11204 player->dynabomb_count++;
11205 player->dynabombs_left++;
11207 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11209 player->dynabomb_size++;
11211 else if (element == EL_DYNABOMB_INCREASE_POWER)
11213 player->dynabomb_xl = TRUE;
11215 else if (IS_KEY(element))
11217 player->key[KEY_NR(element)] = TRUE;
11219 DrawGameValue_Keys(player->key);
11221 redraw_mask |= REDRAW_DOOR_1;
11223 else if (IS_ENVELOPE(element))
11225 player->show_envelope = element;
11227 else if (element == EL_EMC_LENSES)
11229 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11231 RedrawAllInvisibleElementsForLenses();
11233 else if (element == EL_EMC_MAGNIFIER)
11235 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11237 RedrawAllInvisibleElementsForMagnifier();
11239 else if (IS_DROPPABLE(element) ||
11240 IS_THROWABLE(element)) /* can be collected and dropped */
11244 if (collect_count == 0)
11245 player->inventory_infinite_element = element;
11247 for (i = 0; i < collect_count; i++)
11248 if (player->inventory_size < MAX_INVENTORY_SIZE)
11249 player->inventory_element[player->inventory_size++] = element;
11251 DrawGameValue_Dynamite(local_player->inventory_size);
11253 else if (collect_count > 0)
11255 local_player->gems_still_needed -= collect_count;
11256 if (local_player->gems_still_needed < 0)
11257 local_player->gems_still_needed = 0;
11259 DrawGameValue_Emeralds(local_player->gems_still_needed);
11262 RaiseScoreElement(element);
11263 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11266 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11267 player->index_bit, dig_side);
11269 if (mode == DF_SNAP)
11271 #if USE_NEW_SNAP_DELAY
11272 if (level.block_snap_field)
11273 setFieldForSnapping(x, y, element, move_direction);
11275 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11277 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11280 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11281 player->index_bit, dig_side);
11284 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11286 if (mode == DF_SNAP && element != EL_BD_ROCK)
11287 return MP_NO_ACTION;
11289 if (CAN_FALL(element) && dy)
11290 return MP_NO_ACTION;
11292 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11293 !(element == EL_SPRING && level.use_spring_bug))
11294 return MP_NO_ACTION;
11296 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11297 ((move_direction & MV_VERTICAL &&
11298 ((element_info[element].move_pattern & MV_LEFT &&
11299 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11300 (element_info[element].move_pattern & MV_RIGHT &&
11301 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11302 (move_direction & MV_HORIZONTAL &&
11303 ((element_info[element].move_pattern & MV_UP &&
11304 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11305 (element_info[element].move_pattern & MV_DOWN &&
11306 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11307 return MP_NO_ACTION;
11309 /* do not push elements already moving away faster than player */
11310 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11311 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11312 return MP_NO_ACTION;
11314 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11316 if (player->push_delay_value == -1 || !player_was_pushing)
11317 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11319 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11321 if (player->push_delay_value == -1)
11322 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11324 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11326 if (!player->is_pushing)
11327 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11330 player->is_pushing = TRUE;
11332 if (!(IN_LEV_FIELD(nextx, nexty) &&
11333 (IS_FREE(nextx, nexty) ||
11334 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11335 IS_SB_ELEMENT(element)))))
11336 return MP_NO_ACTION;
11338 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11339 return MP_NO_ACTION;
11341 if (player->push_delay == -1) /* new pushing; restart delay */
11342 player->push_delay = 0;
11344 if (player->push_delay < player->push_delay_value &&
11345 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11346 element != EL_SPRING && element != EL_BALLOON)
11348 /* make sure that there is no move delay before next try to push */
11349 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11350 player->move_delay = 0;
11352 return MP_NO_ACTION;
11355 if (IS_SB_ELEMENT(element))
11357 if (element == EL_SOKOBAN_FIELD_FULL)
11359 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11360 local_player->sokobanfields_still_needed++;
11363 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11365 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11366 local_player->sokobanfields_still_needed--;
11369 Feld[x][y] = EL_SOKOBAN_OBJECT;
11371 if (Back[x][y] == Back[nextx][nexty])
11372 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11373 else if (Back[x][y] != 0)
11374 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11377 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11380 if (local_player->sokobanfields_still_needed == 0 &&
11381 game.emulation == EMU_SOKOBAN)
11383 player->LevelSolved = player->GameOver = TRUE;
11384 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11388 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11390 InitMovingField(x, y, move_direction);
11391 GfxAction[x][y] = ACTION_PUSHING;
11393 if (mode == DF_SNAP)
11394 ContinueMoving(x, y);
11396 MovPos[x][y] = (dx != 0 ? dx : dy);
11398 Pushed[x][y] = TRUE;
11399 Pushed[nextx][nexty] = TRUE;
11401 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11402 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11404 player->push_delay_value = -1; /* get new value later */
11406 /* check for element change _after_ element has been pushed */
11407 if (game.use_change_when_pushing_bug)
11409 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11410 player->index_bit, dig_side);
11411 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11412 player->index_bit, dig_side);
11415 else if (IS_SWITCHABLE(element))
11417 if (PLAYER_SWITCHING(player, x, y))
11419 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11420 player->index_bit, dig_side);
11425 player->is_switching = TRUE;
11426 player->switch_x = x;
11427 player->switch_y = y;
11429 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11431 if (element == EL_ROBOT_WHEEL)
11433 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11437 DrawLevelField(x, y);
11439 else if (element == EL_SP_TERMINAL)
11444 SCAN_PLAYFIELD(xx, yy)
11446 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11449 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11451 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11452 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11455 else if (IS_BELT_SWITCH(element))
11457 ToggleBeltSwitch(x, y);
11459 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11460 element == EL_SWITCHGATE_SWITCH_DOWN)
11462 ToggleSwitchgateSwitch(x, y);
11464 else if (element == EL_LIGHT_SWITCH ||
11465 element == EL_LIGHT_SWITCH_ACTIVE)
11467 ToggleLightSwitch(x, y);
11469 else if (element == EL_TIMEGATE_SWITCH)
11471 ActivateTimegateSwitch(x, y);
11473 else if (element == EL_BALLOON_SWITCH_LEFT ||
11474 element == EL_BALLOON_SWITCH_RIGHT ||
11475 element == EL_BALLOON_SWITCH_UP ||
11476 element == EL_BALLOON_SWITCH_DOWN ||
11477 element == EL_BALLOON_SWITCH_NONE ||
11478 element == EL_BALLOON_SWITCH_ANY)
11480 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11481 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11482 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11483 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11484 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11487 else if (element == EL_LAMP)
11489 Feld[x][y] = EL_LAMP_ACTIVE;
11490 local_player->lights_still_needed--;
11492 ResetGfxAnimation(x, y);
11493 DrawLevelField(x, y);
11495 else if (element == EL_TIME_ORB_FULL)
11497 Feld[x][y] = EL_TIME_ORB_EMPTY;
11499 if (level.time > 0 || level.use_time_orb_bug)
11501 TimeLeft += level.time_orb_time;
11502 DrawGameValue_Time(TimeLeft);
11505 ResetGfxAnimation(x, y);
11506 DrawLevelField(x, y);
11508 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11509 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11513 game.ball_state = !game.ball_state;
11516 SCAN_PLAYFIELD(xx, yy)
11518 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11521 int e = Feld[xx][yy];
11523 if (game.ball_state)
11525 if (e == EL_EMC_MAGIC_BALL)
11526 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11527 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11528 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11532 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11533 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11534 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11535 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11540 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11541 player->index_bit, dig_side);
11543 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11544 player->index_bit, dig_side);
11546 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11547 player->index_bit, dig_side);
11553 if (!PLAYER_SWITCHING(player, x, y))
11555 player->is_switching = TRUE;
11556 player->switch_x = x;
11557 player->switch_y = y;
11559 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11560 player->index_bit, dig_side);
11561 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11562 player->index_bit, dig_side);
11564 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11565 player->index_bit, dig_side);
11566 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11567 player->index_bit, dig_side);
11570 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11571 player->index_bit, dig_side);
11572 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11573 player->index_bit, dig_side);
11575 return MP_NO_ACTION;
11578 player->push_delay = -1;
11580 if (is_player) /* function can also be called by EL_PENGUIN */
11582 if (Feld[x][y] != element) /* really digged/collected something */
11583 player->is_collecting = !player->is_digging;
11589 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11591 int jx = player->jx, jy = player->jy;
11592 int x = jx + dx, y = jy + dy;
11593 int snap_direction = (dx == -1 ? MV_LEFT :
11594 dx == +1 ? MV_RIGHT :
11596 dy == +1 ? MV_DOWN : MV_NONE);
11597 boolean can_continue_snapping = (level.continuous_snapping &&
11598 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11600 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11603 if (!player->active || !IN_LEV_FIELD(x, y))
11611 if (player->MovPos == 0)
11612 player->is_pushing = FALSE;
11614 player->is_snapping = FALSE;
11616 if (player->MovPos == 0)
11618 player->is_moving = FALSE;
11619 player->is_digging = FALSE;
11620 player->is_collecting = FALSE;
11626 #if USE_NEW_CONTINUOUS_SNAPPING
11627 /* prevent snapping with already pressed snap key when not allowed */
11628 if (player->is_snapping && !can_continue_snapping)
11631 if (player->is_snapping)
11635 player->MovDir = snap_direction;
11637 if (player->MovPos == 0)
11639 player->is_moving = FALSE;
11640 player->is_digging = FALSE;
11641 player->is_collecting = FALSE;
11644 player->is_dropping = FALSE;
11645 player->is_dropping_pressed = FALSE;
11646 player->drop_pressed_delay = 0;
11648 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11651 player->is_snapping = TRUE;
11653 if (player->MovPos == 0)
11655 player->is_moving = FALSE;
11656 player->is_digging = FALSE;
11657 player->is_collecting = FALSE;
11660 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11661 DrawLevelField(player->last_jx, player->last_jy);
11663 DrawLevelField(x, y);
11668 boolean DropElement(struct PlayerInfo *player)
11670 int old_element, new_element;
11671 int dropx = player->jx, dropy = player->jy;
11672 int drop_direction = player->MovDir;
11673 int drop_side = drop_direction;
11674 int drop_element = (player->inventory_size > 0 ?
11675 player->inventory_element[player->inventory_size - 1] :
11676 player->inventory_infinite_element != EL_UNDEFINED ?
11677 player->inventory_infinite_element :
11678 player->dynabombs_left > 0 ?
11679 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11682 player->is_dropping_pressed = TRUE;
11684 /* do not drop an element on top of another element; when holding drop key
11685 pressed without moving, dropped element must move away before the next
11686 element can be dropped (this is especially important if the next element
11687 is dynamite, which can be placed on background for historical reasons) */
11688 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11691 if (IS_THROWABLE(drop_element))
11693 dropx += GET_DX_FROM_DIR(drop_direction);
11694 dropy += GET_DY_FROM_DIR(drop_direction);
11696 if (!IN_LEV_FIELD(dropx, dropy))
11700 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11701 new_element = drop_element; /* default: no change when dropping */
11703 /* check if player is active, not moving and ready to drop */
11704 if (!player->active || player->MovPos || player->drop_delay > 0)
11707 /* check if player has anything that can be dropped */
11708 if (new_element == EL_UNDEFINED)
11711 /* check if drop key was pressed long enough for EM style dynamite */
11712 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11715 /* check if anything can be dropped at the current position */
11716 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11719 /* collected custom elements can only be dropped on empty fields */
11720 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11723 if (old_element != EL_EMPTY)
11724 Back[dropx][dropy] = old_element; /* store old element on this field */
11726 ResetGfxAnimation(dropx, dropy);
11727 ResetRandomAnimationValue(dropx, dropy);
11729 if (player->inventory_size > 0 ||
11730 player->inventory_infinite_element != EL_UNDEFINED)
11732 if (player->inventory_size > 0)
11734 player->inventory_size--;
11736 DrawGameValue_Dynamite(local_player->inventory_size);
11738 if (new_element == EL_DYNAMITE)
11739 new_element = EL_DYNAMITE_ACTIVE;
11740 else if (new_element == EL_EM_DYNAMITE)
11741 new_element = EL_EM_DYNAMITE_ACTIVE;
11742 else if (new_element == EL_SP_DISK_RED)
11743 new_element = EL_SP_DISK_RED_ACTIVE;
11746 Feld[dropx][dropy] = new_element;
11748 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11749 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11750 el2img(Feld[dropx][dropy]), 0);
11752 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11754 /* needed if previous element just changed to "empty" in the last frame */
11755 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11757 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11758 player->index_bit, drop_side);
11759 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11761 player->index_bit, drop_side);
11763 TestIfElementTouchesCustomElement(dropx, dropy);
11765 else /* player is dropping a dyna bomb */
11767 player->dynabombs_left--;
11769 Feld[dropx][dropy] = new_element;
11771 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11772 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11773 el2img(Feld[dropx][dropy]), 0);
11775 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11778 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11779 InitField_WithBug1(dropx, dropy, FALSE);
11781 new_element = Feld[dropx][dropy]; /* element might have changed */
11783 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11784 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11786 int move_direction, nextx, nexty;
11788 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11789 MovDir[dropx][dropy] = drop_direction;
11791 move_direction = MovDir[dropx][dropy];
11792 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11793 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11795 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11796 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
11799 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11800 player->is_dropping = TRUE;
11802 player->drop_pressed_delay = 0;
11803 player->is_dropping_pressed = FALSE;
11805 player->drop_x = dropx;
11806 player->drop_y = dropy;
11811 /* ------------------------------------------------------------------------- */
11812 /* game sound playing functions */
11813 /* ------------------------------------------------------------------------- */
11815 static int *loop_sound_frame = NULL;
11816 static int *loop_sound_volume = NULL;
11818 void InitPlayLevelSound()
11820 int num_sounds = getSoundListSize();
11822 checked_free(loop_sound_frame);
11823 checked_free(loop_sound_volume);
11825 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11826 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11829 static void PlayLevelSound(int x, int y, int nr)
11831 int sx = SCREENX(x), sy = SCREENY(y);
11832 int volume, stereo_position;
11833 int max_distance = 8;
11834 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11836 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11837 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11840 if (!IN_LEV_FIELD(x, y) ||
11841 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11842 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11845 volume = SOUND_MAX_VOLUME;
11847 if (!IN_SCR_FIELD(sx, sy))
11849 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11850 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11852 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11855 stereo_position = (SOUND_MAX_LEFT +
11856 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11857 (SCR_FIELDX + 2 * max_distance));
11859 if (IS_LOOP_SOUND(nr))
11861 /* This assures that quieter loop sounds do not overwrite louder ones,
11862 while restarting sound volume comparison with each new game frame. */
11864 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11867 loop_sound_volume[nr] = volume;
11868 loop_sound_frame[nr] = FrameCounter;
11871 PlaySoundExt(nr, volume, stereo_position, type);
11874 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11876 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11877 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11878 y < LEVELY(BY1) ? LEVELY(BY1) :
11879 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11883 static void PlayLevelSoundAction(int x, int y, int action)
11885 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11888 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11890 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11892 if (sound_effect != SND_UNDEFINED)
11893 PlayLevelSound(x, y, sound_effect);
11896 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11899 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11901 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11902 PlayLevelSound(x, y, sound_effect);
11905 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11907 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11909 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11910 PlayLevelSound(x, y, sound_effect);
11913 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11915 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11917 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11918 StopSound(sound_effect);
11921 static void PlayLevelMusic()
11923 if (levelset.music[level_nr] != MUS_UNDEFINED)
11924 PlayMusic(levelset.music[level_nr]); /* from config file */
11926 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11929 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11931 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11936 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11940 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11944 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11948 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11952 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11956 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11960 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11963 case SAMPLE_android_clone:
11964 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11967 case SAMPLE_android_move:
11968 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11971 case SAMPLE_spring:
11972 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11976 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
11980 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11983 case SAMPLE_eater_eat:
11984 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11988 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11991 case SAMPLE_collect:
11992 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11995 case SAMPLE_diamond:
11996 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11999 case SAMPLE_squash:
12000 /* !!! CHECK THIS !!! */
12002 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12004 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12008 case SAMPLE_wonderfall:
12009 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12013 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12017 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12021 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12025 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12029 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12033 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12036 case SAMPLE_wonder:
12037 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12041 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12044 case SAMPLE_exit_open:
12045 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12048 case SAMPLE_exit_leave:
12049 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12052 case SAMPLE_dynamite:
12053 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12057 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12061 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12065 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12069 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12073 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12077 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12081 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12086 void RaiseScore(int value)
12088 local_player->score += value;
12090 DrawGameValue_Score(local_player->score);
12093 void RaiseScoreElement(int element)
12098 case EL_BD_DIAMOND:
12099 case EL_EMERALD_YELLOW:
12100 case EL_EMERALD_RED:
12101 case EL_EMERALD_PURPLE:
12102 case EL_SP_INFOTRON:
12103 RaiseScore(level.score[SC_EMERALD]);
12106 RaiseScore(level.score[SC_DIAMOND]);
12109 RaiseScore(level.score[SC_CRYSTAL]);
12112 RaiseScore(level.score[SC_PEARL]);
12115 case EL_BD_BUTTERFLY:
12116 case EL_SP_ELECTRON:
12117 RaiseScore(level.score[SC_BUG]);
12120 case EL_BD_FIREFLY:
12121 case EL_SP_SNIKSNAK:
12122 RaiseScore(level.score[SC_SPACESHIP]);
12125 case EL_DARK_YAMYAM:
12126 RaiseScore(level.score[SC_YAMYAM]);
12129 RaiseScore(level.score[SC_ROBOT]);
12132 RaiseScore(level.score[SC_PACMAN]);
12135 RaiseScore(level.score[SC_NUT]);
12138 case EL_EM_DYNAMITE:
12139 case EL_SP_DISK_RED:
12140 case EL_DYNABOMB_INCREASE_NUMBER:
12141 case EL_DYNABOMB_INCREASE_SIZE:
12142 case EL_DYNABOMB_INCREASE_POWER:
12143 RaiseScore(level.score[SC_DYNAMITE]);
12145 case EL_SHIELD_NORMAL:
12146 case EL_SHIELD_DEADLY:
12147 RaiseScore(level.score[SC_SHIELD]);
12149 case EL_EXTRA_TIME:
12150 RaiseScore(level.extra_time_score);
12164 RaiseScore(level.score[SC_KEY]);
12167 RaiseScore(element_info[element].collect_score);
12172 void RequestQuitGame(boolean ask_if_really_quit)
12174 if (AllPlayersGone ||
12175 !ask_if_really_quit ||
12176 level_editor_test_game ||
12177 Request("Do you really want to quit the game ?",
12178 REQ_ASK | REQ_STAY_CLOSED))
12180 #if defined(NETWORK_AVALIABLE)
12181 if (options.network)
12182 SendToServer_StopPlaying();
12186 game_status = GAME_MODE_MAIN;
12192 if (tape.playing && tape.deactivate_display)
12193 TapeDeactivateDisplayOff(TRUE);
12195 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12197 if (tape.playing && tape.deactivate_display)
12198 TapeDeactivateDisplayOn();
12203 /* ---------- new game button stuff ---------------------------------------- */
12205 /* graphic position values for game buttons */
12206 #define GAME_BUTTON_XSIZE 30
12207 #define GAME_BUTTON_YSIZE 30
12208 #define GAME_BUTTON_XPOS 5
12209 #define GAME_BUTTON_YPOS 215
12210 #define SOUND_BUTTON_XPOS 5
12211 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12213 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12214 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12215 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12216 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12217 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12218 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12225 } gamebutton_info[NUM_GAME_BUTTONS] =
12228 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12233 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12234 GAME_CTRL_ID_PAUSE,
12238 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12243 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12244 SOUND_CTRL_ID_MUSIC,
12245 "background music on/off"
12248 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12249 SOUND_CTRL_ID_LOOPS,
12250 "sound loops on/off"
12253 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12254 SOUND_CTRL_ID_SIMPLE,
12255 "normal sounds on/off"
12259 void CreateGameButtons()
12263 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12265 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12266 struct GadgetInfo *gi;
12269 unsigned long event_mask;
12270 int gd_xoffset, gd_yoffset;
12271 int gd_x1, gd_x2, gd_y1, gd_y2;
12274 gd_xoffset = gamebutton_info[i].x;
12275 gd_yoffset = gamebutton_info[i].y;
12276 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12277 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12279 if (id == GAME_CTRL_ID_STOP ||
12280 id == GAME_CTRL_ID_PAUSE ||
12281 id == GAME_CTRL_ID_PLAY)
12283 button_type = GD_TYPE_NORMAL_BUTTON;
12285 event_mask = GD_EVENT_RELEASED;
12286 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12287 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12291 button_type = GD_TYPE_CHECK_BUTTON;
12293 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12294 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12295 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12296 event_mask = GD_EVENT_PRESSED;
12297 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12298 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12301 gi = CreateGadget(GDI_CUSTOM_ID, id,
12302 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12303 GDI_X, DX + gd_xoffset,
12304 GDI_Y, DY + gd_yoffset,
12305 GDI_WIDTH, GAME_BUTTON_XSIZE,
12306 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12307 GDI_TYPE, button_type,
12308 GDI_STATE, GD_BUTTON_UNPRESSED,
12309 GDI_CHECKED, checked,
12310 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12311 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12312 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12313 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12314 GDI_EVENT_MASK, event_mask,
12315 GDI_CALLBACK_ACTION, HandleGameButtons,
12319 Error(ERR_EXIT, "cannot create gadget");
12321 game_gadget[id] = gi;
12325 void FreeGameButtons()
12329 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12330 FreeGadget(game_gadget[i]);
12333 static void MapGameButtons()
12337 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12338 MapGadget(game_gadget[i]);
12341 void UnmapGameButtons()
12345 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12346 UnmapGadget(game_gadget[i]);
12349 static void HandleGameButtons(struct GadgetInfo *gi)
12351 int id = gi->custom_id;
12353 if (game_status != GAME_MODE_PLAYING)
12358 case GAME_CTRL_ID_STOP:
12359 RequestQuitGame(TRUE);
12362 case GAME_CTRL_ID_PAUSE:
12363 if (options.network)
12365 #if defined(NETWORK_AVALIABLE)
12367 SendToServer_ContinuePlaying();
12369 SendToServer_PausePlaying();
12373 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12376 case GAME_CTRL_ID_PLAY:
12379 #if defined(NETWORK_AVALIABLE)
12380 if (options.network)
12381 SendToServer_ContinuePlaying();
12385 tape.pausing = FALSE;
12386 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12391 case SOUND_CTRL_ID_MUSIC:
12392 if (setup.sound_music)
12394 setup.sound_music = FALSE;
12397 else if (audio.music_available)
12399 setup.sound = setup.sound_music = TRUE;
12401 SetAudioMode(setup.sound);
12407 case SOUND_CTRL_ID_LOOPS:
12408 if (setup.sound_loops)
12409 setup.sound_loops = FALSE;
12410 else if (audio.loops_available)
12412 setup.sound = setup.sound_loops = TRUE;
12413 SetAudioMode(setup.sound);
12417 case SOUND_CTRL_ID_SIMPLE:
12418 if (setup.sound_simple)
12419 setup.sound_simple = FALSE;
12420 else if (audio.sound_available)
12422 setup.sound = setup.sound_simple = TRUE;
12423 SetAudioMode(setup.sound);