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)
42 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
49 /* for MovePlayer() */
50 #define MP_NO_ACTION 0
53 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
55 /* for ScrollPlayer() */
57 #define SCROLL_GO_ON 1
59 /* for Bang()/Explode() */
60 #define EX_PHASE_START 0
61 #define EX_TYPE_NONE 0
62 #define EX_TYPE_NORMAL (1 << 0)
63 #define EX_TYPE_CENTER (1 << 1)
64 #define EX_TYPE_BORDER (1 << 2)
65 #define EX_TYPE_CROSS (1 << 3)
66 #define EX_TYPE_DYNA (1 << 4)
67 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
69 /* special positions in the game control window (relative to control window) */
72 #define XX_EMERALDS 29
73 #define YY_EMERALDS 54
74 #define XX_DYNAMITE 29
75 #define YY_DYNAMITE 89
84 /* special positions in the game control window (relative to main window) */
85 #define DX_LEVEL (DX + XX_LEVEL)
86 #define DY_LEVEL (DY + YY_LEVEL)
87 #define DX_EMERALDS (DX + XX_EMERALDS)
88 #define DY_EMERALDS (DY + YY_EMERALDS)
89 #define DX_DYNAMITE (DX + XX_DYNAMITE)
90 #define DY_DYNAMITE (DY + YY_DYNAMITE)
91 #define DX_KEYS (DX + XX_KEYS)
92 #define DY_KEYS (DY + YY_KEYS)
93 #define DX_SCORE (DX + XX_SCORE)
94 #define DY_SCORE (DY + YY_SCORE)
95 #define DX_TIME1 (DX + XX_TIME1)
96 #define DX_TIME2 (DX + XX_TIME2)
97 #define DY_TIME (DY + YY_TIME)
99 /* values for initial player move delay (initial delay counter value) */
100 #define INITIAL_MOVE_DELAY_OFF -1
101 #define INITIAL_MOVE_DELAY_ON 0
103 /* values for player movement speed (which is in fact a delay value) */
104 #define MOVE_DELAY_MIN_SPEED 32
105 #define MOVE_DELAY_NORMAL_SPEED 8
106 #define MOVE_DELAY_HIGH_SPEED 4
107 #define MOVE_DELAY_MAX_SPEED 1
110 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
111 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
113 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
114 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
116 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
117 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
119 /* values for other actions */
120 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
121 #define MOVE_STEPSIZE_MIN (1)
122 #define MOVE_STEPSIZE_MAX (TILEX)
124 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
125 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
127 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
129 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
130 RND(element_info[e].push_delay_random))
131 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
132 RND(element_info[e].drop_delay_random))
133 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
134 RND(element_info[e].move_delay_random))
135 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
136 (element_info[e].move_delay_random))
137 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
138 RND(element_info[e].ce_value_random_initial))
139 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
140 RND((c)->delay_random * (c)->delay_frames))
141 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
142 RND((c)->delay_random))
144 #define GET_TARGET_ELEMENT(e, ch) \
145 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
146 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
148 #define CAN_GROW_INTO(e) \
149 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
155 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
157 (CAN_MOVE_INTO_ACID(e) && \
158 Feld[x][y] == EL_ACID) || \
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
163 (CAN_MOVE_INTO_ACID(e) && \
164 Feld[x][y] == EL_ACID) || \
167 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
168 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
170 (CAN_MOVE_INTO_ACID(e) && \
171 Feld[x][y] == EL_ACID) || \
172 (DONT_COLLIDE_WITH(e) && \
174 !PLAYER_ENEMY_PROTECTED(x, y))))
176 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
179 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
180 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
182 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
185 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
188 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
189 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
191 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
194 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
195 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
197 #define PIG_CAN_ENTER_FIELD(e, x, y) \
198 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
200 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
202 IS_FOOD_PENGUIN(Feld[x][y])))
203 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
206 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
209 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
212 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
213 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
214 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
216 #define GROUP_NR(e) ((e) - EL_GROUP_START)
217 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
218 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
219 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
221 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
222 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
224 #define CE_ENTER_FIELD_COND(e, x, y) \
225 (!IS_PLAYER(x, y) && \
226 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
228 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
231 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
232 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
234 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
235 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
236 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
237 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
239 /* game button identifiers */
240 #define GAME_CTRL_ID_STOP 0
241 #define GAME_CTRL_ID_PAUSE 1
242 #define GAME_CTRL_ID_PLAY 2
243 #define SOUND_CTRL_ID_MUSIC 3
244 #define SOUND_CTRL_ID_LOOPS 4
245 #define SOUND_CTRL_ID_SIMPLE 5
247 #define NUM_GAME_BUTTONS 6
250 /* forward declaration for internal use */
252 static void CreateField(int, int, int);
254 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
255 static void AdvanceFrameAndPlayerCounters(int);
257 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
258 static boolean MovePlayer(struct PlayerInfo *, int, int);
259 static void ScrollPlayer(struct PlayerInfo *, int);
260 static void ScrollScreen(struct PlayerInfo *, int);
262 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
264 static void InitBeltMovement(void);
265 static void CloseAllOpenTimegates(void);
266 static void CheckGravityMovement(struct PlayerInfo *);
267 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
268 static void KillPlayerUnlessEnemyProtected(int, int);
269 static void KillPlayerUnlessExplosionProtected(int, int);
271 static void TestIfPlayerTouchesCustomElement(int, int);
272 static void TestIfElementTouchesCustomElement(int, int);
273 static void TestIfElementHitsCustomElement(int, int, int);
275 static void TestIfElementSmashesCustomElement(int, int, int);
278 static void HandleElementChange(int, int, int);
280 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
281 #define CheckTriggeredElementChange(x, y, e, ev) \
282 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
283 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
284 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
285 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
286 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
287 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
288 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
290 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
291 #define CheckElementChange(x, y, e, te, ev) \
292 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
293 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
294 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
295 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
296 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
298 static void PlayLevelSound(int, int, int);
299 static void PlayLevelSoundNearest(int, int, int);
300 static void PlayLevelSoundAction(int, int, int);
301 static void PlayLevelSoundElementAction(int, int, int, int);
302 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
303 static void PlayLevelSoundActionIfLoop(int, int, int);
304 static void StopLevelSoundActionIfLoop(int, int, int);
305 static void PlayLevelMusic();
307 static void MapGameButtons();
308 static void HandleGameButtons(struct GadgetInfo *);
310 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
313 /* ------------------------------------------------------------------------- */
314 /* definition of elements that automatically change to other elements after */
315 /* a specified time, eventually calling a function when changing */
316 /* ------------------------------------------------------------------------- */
318 /* forward declaration for changer functions */
319 static void InitBuggyBase(int, int);
320 static void WarnBuggyBase(int, int);
322 static void InitTrap(int, int);
323 static void ActivateTrap(int, int);
324 static void ChangeActiveTrap(int, int);
326 static void InitRobotWheel(int, int);
327 static void RunRobotWheel(int, int);
328 static void StopRobotWheel(int, int);
330 static void InitTimegateWheel(int, int);
331 static void RunTimegateWheel(int, int);
333 static void InitMagicBallDelay(int, int);
334 static void ActivateMagicBall(int, int);
336 static void InitDiagonalMovingElement(int, int);
338 struct ChangingElementInfo
343 void (*pre_change_function)(int x, int y);
344 void (*change_function)(int x, int y);
345 void (*post_change_function)(int x, int y);
348 static struct ChangingElementInfo change_delay_list[] =
399 EL_SWITCHGATE_OPENING,
407 EL_SWITCHGATE_CLOSING,
408 EL_SWITCHGATE_CLOSED,
440 EL_ACID_SPLASH_RIGHT,
449 EL_SP_BUGGY_BASE_ACTIVATING,
456 EL_SP_BUGGY_BASE_ACTIVATING,
457 EL_SP_BUGGY_BASE_ACTIVE,
464 EL_SP_BUGGY_BASE_ACTIVE,
488 EL_ROBOT_WHEEL_ACTIVE,
496 EL_TIMEGATE_SWITCH_ACTIVE,
504 EL_EMC_MAGIC_BALL_ACTIVE,
505 EL_EMC_MAGIC_BALL_ACTIVE,
512 EL_EMC_SPRING_BUMPER_ACTIVE,
513 EL_EMC_SPRING_BUMPER,
520 EL_DIAGONAL_SHRINKING,
533 InitDiagonalMovingElement
549 int push_delay_fixed, push_delay_random;
554 { EL_BALLOON, 0, 0 },
556 { EL_SOKOBAN_OBJECT, 2, 0 },
557 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
558 { EL_SATELLITE, 2, 0 },
559 { EL_SP_DISK_YELLOW, 2, 0 },
561 { EL_UNDEFINED, 0, 0 },
569 move_stepsize_list[] =
571 { EL_AMOEBA_DROP, 2 },
572 { EL_AMOEBA_DROPPING, 2 },
573 { EL_QUICKSAND_FILLING, 1 },
574 { EL_QUICKSAND_EMPTYING, 1 },
575 { EL_MAGIC_WALL_FILLING, 2 },
576 { EL_BD_MAGIC_WALL_FILLING, 2 },
577 { EL_MAGIC_WALL_EMPTYING, 2 },
578 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
588 collect_count_list[] =
591 { EL_BD_DIAMOND, 1 },
592 { EL_EMERALD_YELLOW, 1 },
593 { EL_EMERALD_RED, 1 },
594 { EL_EMERALD_PURPLE, 1 },
596 { EL_SP_INFOTRON, 1 },
608 access_direction_list[] =
610 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
611 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
612 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
613 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
614 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
615 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
616 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
617 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
618 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
619 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
620 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
622 { EL_SP_PORT_LEFT, MV_RIGHT },
623 { EL_SP_PORT_RIGHT, MV_LEFT },
624 { EL_SP_PORT_UP, MV_DOWN },
625 { EL_SP_PORT_DOWN, MV_UP },
626 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
627 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
628 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
629 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
630 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
631 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
632 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
633 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
634 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
635 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
636 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
637 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
638 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
639 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
640 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
642 { EL_UNDEFINED, MV_NONE }
645 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
647 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
648 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
649 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
650 IS_JUST_CHANGING(x, y))
652 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
654 /* static variables for playfield scan mode (scanning forward or backward) */
655 static int playfield_scan_start_x = 0;
656 static int playfield_scan_start_y = 0;
657 static int playfield_scan_delta_x = 1;
658 static int playfield_scan_delta_y = 1;
660 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
661 (y) >= 0 && (y) <= lev_fieldy - 1; \
662 (y) += playfield_scan_delta_y) \
663 for ((x) = playfield_scan_start_x; \
664 (x) >= 0 && (x) <= lev_fieldx - 1; \
665 (x) += playfield_scan_delta_x) \
667 static void InitPlayfieldScanModeVars()
669 if (game.use_reverse_scan_direction)
671 playfield_scan_start_x = lev_fieldx - 1;
672 playfield_scan_start_y = lev_fieldy - 1;
674 playfield_scan_delta_x = -1;
675 playfield_scan_delta_y = -1;
679 playfield_scan_start_x = 0;
680 playfield_scan_start_y = 0;
682 playfield_scan_delta_x = 1;
683 playfield_scan_delta_y = 1;
687 static void InitPlayfieldScanMode(int mode)
689 game.use_reverse_scan_direction =
690 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
692 InitPlayfieldScanModeVars();
695 static int get_move_delay_from_stepsize(int move_stepsize)
698 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
700 /* make sure that stepsize value is always a power of 2 */
701 move_stepsize = (1 << log_2(move_stepsize));
703 return TILEX / move_stepsize;
706 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
709 int move_delay = get_move_delay_from_stepsize(move_stepsize);
710 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
712 /* do no immediately change move delay -- the player might just be moving */
713 player->move_delay_value_next = move_delay;
715 /* information if player can move must be set separately */
716 player->cannot_move = cannot_move;
720 player->move_delay = game.initial_move_delay;
721 player->move_delay_value = game.initial_move_delay_value;
723 player->move_delay_value_next = -1;
725 player->move_delay_reset_counter = 0;
729 void GetPlayerConfig()
731 if (!audio.sound_available)
732 setup.sound_simple = FALSE;
734 if (!audio.loops_available)
735 setup.sound_loops = FALSE;
737 if (!audio.music_available)
738 setup.sound_music = FALSE;
740 if (!video.fullscreen_available)
741 setup.fullscreen = FALSE;
743 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
745 SetAudioMode(setup.sound);
749 static int getBeltNrFromBeltElement(int element)
751 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
752 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
753 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
756 static int getBeltNrFromBeltActiveElement(int element)
758 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
759 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
760 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
763 static int getBeltNrFromBeltSwitchElement(int element)
765 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
766 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
767 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
770 static int getBeltDirNrFromBeltSwitchElement(int element)
772 static int belt_base_element[4] =
774 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
775 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
776 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
777 EL_CONVEYOR_BELT_4_SWITCH_LEFT
780 int belt_nr = getBeltNrFromBeltSwitchElement(element);
781 int belt_dir_nr = element - belt_base_element[belt_nr];
783 return (belt_dir_nr % 3);
786 static int getBeltDirFromBeltSwitchElement(int element)
788 static int belt_move_dir[3] =
795 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
797 return belt_move_dir[belt_dir_nr];
800 static void InitPlayerField(int x, int y, int element, boolean init_game)
802 if (element == EL_SP_MURPHY)
806 if (stored_player[0].present)
808 Feld[x][y] = EL_SP_MURPHY_CLONE;
814 stored_player[0].use_murphy = TRUE;
817 Feld[x][y] = EL_PLAYER_1;
823 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
824 int jx = player->jx, jy = player->jy;
826 player->present = TRUE;
828 player->block_last_field = (element == EL_SP_MURPHY ?
829 level.sp_block_last_field :
830 level.block_last_field);
832 /* ---------- initialize player's last field block delay --------------- */
834 /* always start with reliable default value (no adjustment needed) */
835 player->block_delay_adjustment = 0;
837 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
838 if (player->block_last_field && element == EL_SP_MURPHY)
839 player->block_delay_adjustment = 1;
841 /* special case 2: in game engines before 3.1.1, blocking was different */
842 if (game.use_block_last_field_bug)
843 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
845 if (!options.network || player->connected)
847 player->active = TRUE;
849 /* remove potentially duplicate players */
850 if (StorePlayer[jx][jy] == Feld[x][y])
851 StorePlayer[jx][jy] = 0;
853 StorePlayer[x][y] = Feld[x][y];
857 printf("Player %d activated.\n", player->element_nr);
858 printf("[Local player is %d and currently %s.]\n",
859 local_player->element_nr,
860 local_player->active ? "active" : "not active");
864 Feld[x][y] = EL_EMPTY;
866 player->jx = player->last_jx = x;
867 player->jy = player->last_jy = y;
871 static void InitField(int x, int y, boolean init_game)
873 int element = Feld[x][y];
882 InitPlayerField(x, y, element, init_game);
885 case EL_SOKOBAN_FIELD_PLAYER:
886 element = Feld[x][y] = EL_PLAYER_1;
887 InitField(x, y, init_game);
889 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
890 InitField(x, y, init_game);
893 case EL_SOKOBAN_FIELD_EMPTY:
894 local_player->sokobanfields_still_needed++;
898 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
899 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
900 else if (x > 0 && Feld[x-1][y] == EL_ACID)
901 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
902 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
903 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
904 else if (y > 0 && Feld[x][y-1] == EL_ACID)
905 Feld[x][y] = EL_ACID_POOL_BOTTOM;
906 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
907 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
915 case EL_SPACESHIP_RIGHT:
916 case EL_SPACESHIP_UP:
917 case EL_SPACESHIP_LEFT:
918 case EL_SPACESHIP_DOWN:
920 case EL_BD_BUTTERFLY_RIGHT:
921 case EL_BD_BUTTERFLY_UP:
922 case EL_BD_BUTTERFLY_LEFT:
923 case EL_BD_BUTTERFLY_DOWN:
924 case EL_BD_BUTTERFLY:
925 case EL_BD_FIREFLY_RIGHT:
926 case EL_BD_FIREFLY_UP:
927 case EL_BD_FIREFLY_LEFT:
928 case EL_BD_FIREFLY_DOWN:
930 case EL_PACMAN_RIGHT:
954 if (y == lev_fieldy - 1)
956 Feld[x][y] = EL_AMOEBA_GROWING;
957 Store[x][y] = EL_AMOEBA_WET;
961 case EL_DYNAMITE_ACTIVE:
962 case EL_SP_DISK_RED_ACTIVE:
963 case EL_DYNABOMB_PLAYER_1_ACTIVE:
964 case EL_DYNABOMB_PLAYER_2_ACTIVE:
965 case EL_DYNABOMB_PLAYER_3_ACTIVE:
966 case EL_DYNABOMB_PLAYER_4_ACTIVE:
971 local_player->lights_still_needed++;
975 local_player->friends_still_needed++;
980 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
983 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
984 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
985 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
986 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
987 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
988 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
989 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
990 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
991 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
992 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
993 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
994 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
997 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
998 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
999 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1001 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1003 game.belt_dir[belt_nr] = belt_dir;
1004 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1006 else /* more than one switch -- set it like the first switch */
1008 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1013 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1015 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1018 case EL_LIGHT_SWITCH_ACTIVE:
1020 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1023 case EL_EMC_MAGIC_BALL:
1024 if (game.ball_state)
1025 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1028 case EL_EMC_MAGIC_BALL_SWITCH:
1029 if (game.ball_state)
1030 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1035 if (IS_CUSTOM_ELEMENT(element))
1037 if (CAN_MOVE(element))
1040 #if USE_NEW_CUSTOM_VALUE
1041 if (!element_info[element].use_last_ce_value || init_game)
1042 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1046 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1049 else if (IS_GROUP_ELEMENT(element))
1051 struct ElementGroupInfo *group = element_info[element].group;
1052 int last_anim_random_frame = gfx.anim_random_frame;
1055 if (group->choice_mode == ANIM_RANDOM)
1056 gfx.anim_random_frame = RND(group->num_elements_resolved);
1058 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1059 group->choice_mode, 0,
1062 if (group->choice_mode == ANIM_RANDOM)
1063 gfx.anim_random_frame = last_anim_random_frame;
1065 group->choice_pos++;
1067 Feld[x][y] = group->element_resolved[element_pos];
1069 InitField(x, y, init_game);
1076 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1081 #if USE_NEW_CUSTOM_VALUE
1084 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1086 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1094 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1096 InitField(x, y, init_game);
1098 /* not needed to call InitMovDir() -- already done by InitField()! */
1099 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1100 CAN_MOVE(Feld[x][y]))
1104 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1106 int old_element = Feld[x][y];
1108 InitField(x, y, init_game);
1110 /* not needed to call InitMovDir() -- already done by InitField()! */
1111 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1112 CAN_MOVE(old_element) &&
1113 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1116 /* this case is in fact a combination of not less than three bugs:
1117 first, it calls InitMovDir() for elements that can move, although this is
1118 already done by InitField(); then, it checks the element that was at this
1119 field _before_ the call to InitField() (which can change it); lastly, it
1120 was not called for "mole with direction" elements, which were treated as
1121 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1125 inline void DrawGameValue_Emeralds(int value)
1127 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1130 inline void DrawGameValue_Dynamite(int value)
1132 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1135 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1139 /* currently only 4 of 8 possible keys are displayed */
1140 for (i = 0; i < STD_NUM_KEYS; i++)
1143 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1144 el2edimg(EL_KEY_1 + i));
1146 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1147 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1148 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1152 inline void DrawGameValue_Score(int value)
1154 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1157 inline void DrawGameValue_Time(int value)
1160 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1162 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1165 inline void DrawGameValue_Level(int value)
1168 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1171 /* misuse area for displaying emeralds to draw bigger level number */
1172 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1173 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1175 /* now copy it to the area for displaying level number */
1176 BlitBitmap(drawto, drawto,
1177 DX_EMERALDS, DY_EMERALDS + 1,
1178 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1179 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1180 DX_LEVEL - 1, DY_LEVEL + 1);
1182 /* restore the area for displaying emeralds */
1183 DrawGameValue_Emeralds(local_player->gems_still_needed);
1185 /* yes, this is all really ugly :-) */
1189 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1192 int key[MAX_NUM_KEYS];
1195 for (i = 0; i < MAX_NUM_KEYS; i++)
1196 key[i] = key_bits & (1 << i);
1198 DrawGameValue_Level(level_nr);
1200 DrawGameValue_Emeralds(emeralds);
1201 DrawGameValue_Dynamite(dynamite);
1202 DrawGameValue_Score(score);
1203 DrawGameValue_Time(time);
1205 DrawGameValue_Keys(key);
1208 void DrawGameDoorValues()
1212 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1214 DrawGameDoorValues_EM();
1219 DrawGameValue_Level(level_nr);
1221 DrawGameValue_Emeralds(local_player->gems_still_needed);
1222 DrawGameValue_Dynamite(local_player->inventory_size);
1223 DrawGameValue_Score(local_player->score);
1224 DrawGameValue_Time(TimeLeft);
1226 for (i = 0; i < MAX_PLAYERS; i++)
1227 DrawGameValue_Keys(stored_player[i].key);
1230 static void resolve_group_element(int group_element, int recursion_depth)
1232 static int group_nr;
1233 static struct ElementGroupInfo *group;
1234 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1237 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1239 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1240 group_element - EL_GROUP_START + 1);
1242 /* replace element which caused too deep recursion by question mark */
1243 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1248 if (recursion_depth == 0) /* initialization */
1250 group = element_info[group_element].group;
1251 group_nr = group_element - EL_GROUP_START;
1253 group->num_elements_resolved = 0;
1254 group->choice_pos = 0;
1257 for (i = 0; i < actual_group->num_elements; i++)
1259 int element = actual_group->element[i];
1261 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1264 if (IS_GROUP_ELEMENT(element))
1265 resolve_group_element(element, recursion_depth + 1);
1268 group->element_resolved[group->num_elements_resolved++] = element;
1269 element_info[element].in_group[group_nr] = TRUE;
1276 =============================================================================
1278 -----------------------------------------------------------------------------
1279 initialize game engine due to level / tape version number
1280 =============================================================================
1283 static void InitGameEngine()
1285 int i, j, k, l, x, y;
1287 /* set game engine from tape file when re-playing, else from level file */
1288 game.engine_version = (tape.playing ? tape.engine_version :
1289 level.game_version);
1291 /* ---------------------------------------------------------------------- */
1292 /* set flags for bugs and changes according to active game engine version */
1293 /* ---------------------------------------------------------------------- */
1296 Summary of bugfix/change:
1297 Fixed handling for custom elements that change when pushed by the player.
1299 Fixed/changed in version:
1303 Before 3.1.0, custom elements that "change when pushing" changed directly
1304 after the player started pushing them (until then handled in "DigField()").
1305 Since 3.1.0, these custom elements are not changed until the "pushing"
1306 move of the element is finished (now handled in "ContinueMoving()").
1308 Affected levels/tapes:
1309 The first condition is generally needed for all levels/tapes before version
1310 3.1.0, which might use the old behaviour before it was changed; known tapes
1311 that are affected are some tapes from the level set "Walpurgis Gardens" by
1313 The second condition is an exception from the above case and is needed for
1314 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1315 above (including some development versions of 3.1.0), but before it was
1316 known that this change would break tapes like the above and was fixed in
1317 3.1.1, so that the changed behaviour was active although the engine version
1318 while recording maybe was before 3.1.0. There is at least one tape that is
1319 affected by this exception, which is the tape for the one-level set "Bug
1320 Machine" by Juergen Bonhagen.
1323 game.use_change_when_pushing_bug =
1324 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1326 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1327 tape.game_version < VERSION_IDENT(3,1,1,0)));
1330 Summary of bugfix/change:
1331 Fixed handling for blocking the field the player leaves when moving.
1333 Fixed/changed in version:
1337 Before 3.1.1, when "block last field when moving" was enabled, the field
1338 the player is leaving when moving was blocked for the time of the move,
1339 and was directly unblocked afterwards. This resulted in the last field
1340 being blocked for exactly one less than the number of frames of one player
1341 move. Additionally, even when blocking was disabled, the last field was
1342 blocked for exactly one frame.
1343 Since 3.1.1, due to changes in player movement handling, the last field
1344 is not blocked at all when blocking is disabled. When blocking is enabled,
1345 the last field is blocked for exactly the number of frames of one player
1346 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1347 last field is blocked for exactly one more than the number of frames of
1350 Affected levels/tapes:
1351 (!!! yet to be determined -- probably many !!!)
1354 game.use_block_last_field_bug =
1355 (game.engine_version < VERSION_IDENT(3,1,1,0));
1358 Summary of bugfix/change:
1359 Changed behaviour of CE changes with multiple changes per single frame.
1361 Fixed/changed in version:
1365 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1366 This resulted in race conditions where CEs seem to behave strange in some
1367 situations (where triggered CE changes were just skipped because there was
1368 already a CE change on that tile in the playfield in that engine frame).
1369 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1370 (The number of changes per frame must be limited in any case, because else
1371 it is easily possible to define CE changes that would result in an infinite
1372 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1373 should be set large enough so that it would only be reached in cases where
1374 the corresponding CE change conditions run into a loop. Therefore, it seems
1375 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1376 maximal number of change pages for custom elements.)
1378 Affected levels/tapes:
1382 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1383 game.max_num_changes_per_frame = 1;
1385 game.max_num_changes_per_frame =
1386 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1389 /* ---------------------------------------------------------------------- */
1391 /* default scan direction: scan playfield from top/left to bottom/right */
1392 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1394 /* dynamically adjust element properties according to game engine version */
1395 InitElementPropertiesEngine(game.engine_version);
1398 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1399 printf(" tape version == %06d [%s] [file: %06d]\n",
1400 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1402 printf(" => game.engine_version == %06d\n", game.engine_version);
1405 /* ---------- recursively resolve group elements ------------------------- */
1407 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1408 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1409 element_info[i].in_group[j] = FALSE;
1411 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1412 resolve_group_element(EL_GROUP_START + i, 0);
1414 /* ---------- initialize player's initial move delay --------------------- */
1417 /* dynamically adjust player properties according to level information */
1418 game.initial_move_delay_value =
1419 get_move_delay_from_stepsize(level.initial_player_stepsize);
1421 /* dynamically adjust player properties according to level information */
1422 game.initial_move_delay_value =
1423 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1426 /* dynamically adjust player properties according to game engine version */
1427 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1428 game.initial_move_delay_value : 0);
1430 /* ---------- initialize player's initial push delay --------------------- */
1432 /* dynamically adjust player properties according to game engine version */
1433 game.initial_push_delay_value =
1434 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1436 /* ---------- initialize changing elements ------------------------------- */
1438 /* initialize changing elements information */
1439 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1441 struct ElementInfo *ei = &element_info[i];
1443 /* this pointer might have been changed in the level editor */
1444 ei->change = &ei->change_page[0];
1446 if (!IS_CUSTOM_ELEMENT(i))
1448 ei->change->target_element = EL_EMPTY_SPACE;
1449 ei->change->delay_fixed = 0;
1450 ei->change->delay_random = 0;
1451 ei->change->delay_frames = 1;
1454 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1456 ei->has_change_event[j] = FALSE;
1458 ei->event_page_nr[j] = 0;
1459 ei->event_page[j] = &ei->change_page[0];
1463 /* add changing elements from pre-defined list */
1464 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1466 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1467 struct ElementInfo *ei = &element_info[ch_delay->element];
1469 ei->change->target_element = ch_delay->target_element;
1470 ei->change->delay_fixed = ch_delay->change_delay;
1472 ei->change->pre_change_function = ch_delay->pre_change_function;
1473 ei->change->change_function = ch_delay->change_function;
1474 ei->change->post_change_function = ch_delay->post_change_function;
1476 ei->change->can_change = TRUE;
1477 ei->change->can_change_or_has_action = TRUE;
1479 ei->has_change_event[CE_DELAY] = TRUE;
1481 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1482 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1485 /* ---------- initialize internal run-time variables ------------- */
1487 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1489 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1491 for (j = 0; j < ei->num_change_pages; j++)
1493 ei->change_page[j].can_change_or_has_action =
1494 (ei->change_page[j].can_change |
1495 ei->change_page[j].has_action);
1499 /* add change events from custom element configuration */
1500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1502 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1504 for (j = 0; j < ei->num_change_pages; j++)
1506 if (!ei->change_page[j].can_change_or_has_action)
1509 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1511 /* only add event page for the first page found with this event */
1512 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1514 ei->has_change_event[k] = TRUE;
1516 ei->event_page_nr[k] = j;
1517 ei->event_page[k] = &ei->change_page[j];
1523 /* ---------- initialize run-time trigger player and element ------------- */
1525 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1527 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1529 for (j = 0; j < ei->num_change_pages; j++)
1531 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1532 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1533 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1534 ei->change_page[j].actual_trigger_ce_value = 0;
1538 /* ---------- initialize trigger events ---------------------------------- */
1540 /* initialize trigger events information */
1541 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1542 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1543 trigger_events[i][j] = FALSE;
1545 /* add trigger events from element change event properties */
1546 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1548 struct ElementInfo *ei = &element_info[i];
1550 for (j = 0; j < ei->num_change_pages; j++)
1552 if (!ei->change_page[j].can_change_or_has_action)
1555 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1557 int trigger_element = ei->change_page[j].trigger_element;
1559 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1561 if (ei->change_page[j].has_event[k])
1563 if (IS_GROUP_ELEMENT(trigger_element))
1565 struct ElementGroupInfo *group =
1566 element_info[trigger_element].group;
1568 for (l = 0; l < group->num_elements_resolved; l++)
1569 trigger_events[group->element_resolved[l]][k] = TRUE;
1572 trigger_events[trigger_element][k] = TRUE;
1579 /* ---------- initialize push delay -------------------------------------- */
1581 /* initialize push delay values to default */
1582 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1584 if (!IS_CUSTOM_ELEMENT(i))
1586 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1587 element_info[i].push_delay_random = game.default_push_delay_random;
1591 /* set push delay value for certain elements from pre-defined list */
1592 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1594 int e = push_delay_list[i].element;
1596 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1597 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1600 /* set push delay value for Supaplex elements for newer engine versions */
1601 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1605 if (IS_SP_ELEMENT(i))
1607 /* set SP push delay to just enough to push under a falling zonk */
1608 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1610 element_info[i].push_delay_fixed = delay;
1611 element_info[i].push_delay_random = 0;
1616 /* ---------- initialize move stepsize ----------------------------------- */
1618 /* initialize move stepsize values to default */
1619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 if (!IS_CUSTOM_ELEMENT(i))
1621 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1623 /* set move stepsize value for certain elements from pre-defined list */
1624 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1626 int e = move_stepsize_list[i].element;
1628 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1631 /* ---------- initialize collect score ----------------------------------- */
1633 /* initialize collect score values for custom elements from initial value */
1634 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1635 if (IS_CUSTOM_ELEMENT(i))
1636 element_info[i].collect_score = element_info[i].collect_score_initial;
1638 /* ---------- initialize collect count ----------------------------------- */
1640 /* initialize collect count values for non-custom elements */
1641 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1642 if (!IS_CUSTOM_ELEMENT(i))
1643 element_info[i].collect_count_initial = 0;
1645 /* add collect count values for all elements from pre-defined list */
1646 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1647 element_info[collect_count_list[i].element].collect_count_initial =
1648 collect_count_list[i].count;
1650 /* ---------- initialize access direction -------------------------------- */
1652 /* initialize access direction values to default (access from every side) */
1653 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1654 if (!IS_CUSTOM_ELEMENT(i))
1655 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1657 /* set access direction value for certain elements from pre-defined list */
1658 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1659 element_info[access_direction_list[i].element].access_direction =
1660 access_direction_list[i].direction;
1662 /* ---------- initialize explosion content ------------------------------- */
1663 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1665 if (IS_CUSTOM_ELEMENT(i))
1668 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1670 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1672 element_info[i].content.e[x][y] =
1673 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1674 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1675 i == EL_PLAYER_3 ? EL_EMERALD :
1676 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1677 i == EL_MOLE ? EL_EMERALD_RED :
1678 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1679 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1680 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1681 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1682 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1683 i == EL_WALL_EMERALD ? EL_EMERALD :
1684 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1685 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1686 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1687 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1688 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1689 i == EL_WALL_PEARL ? EL_PEARL :
1690 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1696 int get_num_special_action(int element, int action_first, int action_last)
1698 int num_special_action = 0;
1701 for (i = action_first; i <= action_last; i++)
1703 boolean found = FALSE;
1705 for (j = 0; j < NUM_DIRECTIONS; j++)
1706 if (el_act_dir2img(element, i, j) !=
1707 el_act_dir2img(element, ACTION_DEFAULT, j))
1711 num_special_action++;
1716 return num_special_action;
1720 =============================================================================
1722 -----------------------------------------------------------------------------
1723 initialize and start new game
1724 =============================================================================
1729 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1730 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1731 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1736 /* don't play tapes over network */
1737 network_playing = (options.network && !tape.playing);
1739 for (i = 0; i < MAX_PLAYERS; i++)
1741 struct PlayerInfo *player = &stored_player[i];
1743 player->index_nr = i;
1744 player->index_bit = (1 << i);
1745 player->element_nr = EL_PLAYER_1 + i;
1747 player->present = FALSE;
1748 player->active = FALSE;
1751 player->effective_action = 0;
1752 player->programmed_action = 0;
1755 player->gems_still_needed = level.gems_needed;
1756 player->sokobanfields_still_needed = 0;
1757 player->lights_still_needed = 0;
1758 player->friends_still_needed = 0;
1760 for (j = 0; j < MAX_NUM_KEYS; j++)
1761 player->key[j] = FALSE;
1763 player->dynabomb_count = 0;
1764 player->dynabomb_size = 1;
1765 player->dynabombs_left = 0;
1766 player->dynabomb_xl = FALSE;
1768 player->MovDir = MV_NONE;
1771 player->GfxDir = MV_NONE;
1772 player->GfxAction = ACTION_DEFAULT;
1774 player->StepFrame = 0;
1776 player->use_murphy = FALSE;
1777 player->artwork_element =
1778 (level.use_artwork_element[i] ? level.artwork_element[i] :
1779 player->element_nr);
1781 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1782 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1784 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1786 player->actual_frame_counter = 0;
1788 player->step_counter = 0;
1790 player->last_move_dir = MV_NONE;
1792 player->is_waiting = FALSE;
1793 player->is_moving = FALSE;
1794 player->is_auto_moving = FALSE;
1795 player->is_digging = FALSE;
1796 player->is_snapping = FALSE;
1797 player->is_collecting = FALSE;
1798 player->is_pushing = FALSE;
1799 player->is_switching = FALSE;
1800 player->is_dropping = FALSE;
1802 player->is_bored = FALSE;
1803 player->is_sleeping = FALSE;
1805 player->frame_counter_bored = -1;
1806 player->frame_counter_sleeping = -1;
1808 player->anim_delay_counter = 0;
1809 player->post_delay_counter = 0;
1811 player->action_waiting = ACTION_DEFAULT;
1812 player->last_action_waiting = ACTION_DEFAULT;
1813 player->special_action_bored = ACTION_DEFAULT;
1814 player->special_action_sleeping = ACTION_DEFAULT;
1816 /* set number of special actions for bored and sleeping animation */
1817 player->num_special_action_bored =
1818 get_num_special_action(player->artwork_element,
1819 ACTION_BORING_1, ACTION_BORING_LAST);
1820 player->num_special_action_sleeping =
1821 get_num_special_action(player->artwork_element,
1822 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1824 player->switch_x = -1;
1825 player->switch_y = -1;
1827 player->drop_x = -1;
1828 player->drop_y = -1;
1830 player->show_envelope = 0;
1833 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1835 player->move_delay = game.initial_move_delay;
1836 player->move_delay_value = game.initial_move_delay_value;
1838 player->move_delay_value_next = -1;
1840 player->move_delay_reset_counter = 0;
1842 player->cannot_move = FALSE;
1845 player->push_delay = -1; /* initialized when pushing starts */
1846 player->push_delay_value = game.initial_push_delay_value;
1848 player->drop_delay = 0;
1850 player->last_jx = player->last_jy = 0;
1851 player->jx = player->jy = 0;
1853 player->shield_normal_time_left = 0;
1854 player->shield_deadly_time_left = 0;
1856 player->inventory_infinite_element = EL_UNDEFINED;
1857 player->inventory_size = 0;
1859 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1860 SnapField(player, 0, 0);
1862 player->LevelSolved = FALSE;
1863 player->GameOver = FALSE;
1866 network_player_action_received = FALSE;
1868 #if defined(NETWORK_AVALIABLE)
1869 /* initial null action */
1870 if (network_playing)
1871 SendToServer_MovePlayer(MV_NONE);
1880 TimeLeft = level.time;
1883 ScreenMovDir = MV_NONE;
1887 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1889 AllPlayersGone = FALSE;
1891 game.yamyam_content_nr = 0;
1892 game.magic_wall_active = FALSE;
1893 game.magic_wall_time_left = 0;
1894 game.light_time_left = 0;
1895 game.timegate_time_left = 0;
1896 game.switchgate_pos = 0;
1897 game.wind_direction = level.wind_direction_initial;
1898 game.gravity = level.initial_gravity;
1899 game.explosions_delayed = TRUE;
1901 game.lenses_time_left = 0;
1902 game.magnify_time_left = 0;
1904 game.ball_state = level.ball_state_initial;
1905 game.ball_content_nr = 0;
1907 game.envelope_active = FALSE;
1909 for (i = 0; i < NUM_BELTS; i++)
1911 game.belt_dir[i] = MV_NONE;
1912 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1915 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1916 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1919 SCAN_PLAYFIELD(x, y)
1921 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1924 Feld[x][y] = level.field[x][y];
1925 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1926 ChangeDelay[x][y] = 0;
1927 ChangePage[x][y] = -1;
1928 #if USE_NEW_CUSTOM_VALUE
1929 CustomValue[x][y] = 0; /* initialized in InitField() */
1931 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1933 WasJustMoving[x][y] = 0;
1934 WasJustFalling[x][y] = 0;
1935 CheckCollision[x][y] = 0;
1937 Pushed[x][y] = FALSE;
1939 ChangeCount[x][y] = 0;
1940 ChangeEvent[x][y] = -1;
1942 ExplodePhase[x][y] = 0;
1943 ExplodeDelay[x][y] = 0;
1944 ExplodeField[x][y] = EX_TYPE_NONE;
1946 RunnerVisit[x][y] = 0;
1947 PlayerVisit[x][y] = 0;
1950 GfxRandom[x][y] = INIT_GFX_RANDOM();
1951 GfxElement[x][y] = EL_UNDEFINED;
1952 GfxAction[x][y] = ACTION_DEFAULT;
1953 GfxDir[x][y] = MV_NONE;
1957 SCAN_PLAYFIELD(x, y)
1959 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1962 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1964 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1966 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1969 InitField(x, y, TRUE);
1974 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1975 emulate_sb ? EMU_SOKOBAN :
1976 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1978 #if USE_NEW_ALL_SLIPPERY
1979 /* initialize type of slippery elements */
1980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1982 if (!IS_CUSTOM_ELEMENT(i))
1984 /* default: elements slip down either to the left or right randomly */
1985 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
1987 /* SP style elements prefer to slip down on the left side */
1988 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
1989 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1991 /* BD style elements prefer to slip down on the left side */
1992 if (game.emulation == EMU_BOULDERDASH)
1993 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
1998 /* initialize explosion and ignition delay */
1999 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2001 if (!IS_CUSTOM_ELEMENT(i))
2004 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2005 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2006 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2007 int last_phase = (num_phase + 1) * delay;
2008 int half_phase = (num_phase / 2) * delay;
2010 element_info[i].explosion_delay = last_phase - 1;
2011 element_info[i].ignition_delay = half_phase;
2013 if (i == EL_BLACK_ORB)
2014 element_info[i].ignition_delay = 1;
2018 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2019 element_info[i].explosion_delay = 1;
2021 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2022 element_info[i].ignition_delay = 1;
2026 /* correct non-moving belts to start moving left */
2027 for (i = 0; i < NUM_BELTS; i++)
2028 if (game.belt_dir[i] == MV_NONE)
2029 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2031 /* check if any connected player was not found in playfield */
2032 for (i = 0; i < MAX_PLAYERS; i++)
2034 struct PlayerInfo *player = &stored_player[i];
2036 if (player->connected && !player->present)
2038 for (j = 0; j < MAX_PLAYERS; j++)
2040 struct PlayerInfo *some_player = &stored_player[j];
2041 int jx = some_player->jx, jy = some_player->jy;
2043 /* assign first free player found that is present in the playfield */
2044 if (some_player->present && !some_player->connected)
2046 player->present = TRUE;
2047 player->active = TRUE;
2049 some_player->present = FALSE;
2050 some_player->active = FALSE;
2053 player->element_nr = some_player->element_nr;
2056 player->artwork_element = some_player->artwork_element;
2058 player->block_last_field = some_player->block_last_field;
2059 player->block_delay_adjustment = some_player->block_delay_adjustment;
2061 StorePlayer[jx][jy] = player->element_nr;
2062 player->jx = player->last_jx = jx;
2063 player->jy = player->last_jy = jy;
2073 /* when playing a tape, eliminate all players which do not participate */
2075 for (i = 0; i < MAX_PLAYERS; i++)
2077 if (stored_player[i].active && !tape.player_participates[i])
2079 struct PlayerInfo *player = &stored_player[i];
2080 int jx = player->jx, jy = player->jy;
2082 player->active = FALSE;
2083 StorePlayer[jx][jy] = 0;
2084 Feld[jx][jy] = EL_EMPTY;
2088 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2090 /* when in single player mode, eliminate all but the first active player */
2092 for (i = 0; i < MAX_PLAYERS; i++)
2094 if (stored_player[i].active)
2096 for (j = i + 1; j < MAX_PLAYERS; j++)
2098 if (stored_player[j].active)
2100 struct PlayerInfo *player = &stored_player[j];
2101 int jx = player->jx, jy = player->jy;
2103 player->active = FALSE;
2104 player->present = FALSE;
2106 StorePlayer[jx][jy] = 0;
2107 Feld[jx][jy] = EL_EMPTY;
2114 /* when recording the game, store which players take part in the game */
2117 for (i = 0; i < MAX_PLAYERS; i++)
2118 if (stored_player[i].active)
2119 tape.player_participates[i] = TRUE;
2124 for (i = 0; i < MAX_PLAYERS; i++)
2126 struct PlayerInfo *player = &stored_player[i];
2128 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2133 if (local_player == player)
2134 printf("Player %d is local player.\n", i+1);
2138 if (BorderElement == EL_EMPTY)
2141 SBX_Right = lev_fieldx - SCR_FIELDX;
2143 SBY_Lower = lev_fieldy - SCR_FIELDY;
2148 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2150 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2153 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2154 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2156 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2157 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2159 /* if local player not found, look for custom element that might create
2160 the player (make some assumptions about the right custom element) */
2161 if (!local_player->present)
2163 int start_x = 0, start_y = 0;
2164 int found_rating = 0;
2165 int found_element = EL_UNDEFINED;
2166 int player_nr = local_player->index_nr;
2169 SCAN_PLAYFIELD(x, y)
2171 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2174 int element = Feld[x][y];
2179 if (level.use_start_element[player_nr] &&
2180 level.start_element[player_nr] == element &&
2187 found_element = element;
2190 if (!IS_CUSTOM_ELEMENT(element))
2193 if (CAN_CHANGE(element))
2195 for (i = 0; i < element_info[element].num_change_pages; i++)
2197 /* check for player created from custom element as single target */
2198 content = element_info[element].change_page[i].target_element;
2199 is_player = ELEM_IS_PLAYER(content);
2201 if (is_player && (found_rating < 3 || element < found_element))
2207 found_element = element;
2212 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2214 /* check for player created from custom element as explosion content */
2215 content = element_info[element].content.e[xx][yy];
2216 is_player = ELEM_IS_PLAYER(content);
2218 if (is_player && (found_rating < 2 || element < found_element))
2220 start_x = x + xx - 1;
2221 start_y = y + yy - 1;
2224 found_element = element;
2227 if (!CAN_CHANGE(element))
2230 for (i = 0; i < element_info[element].num_change_pages; i++)
2232 /* check for player created from custom element as extended target */
2234 element_info[element].change_page[i].target_content.e[xx][yy];
2236 is_player = ELEM_IS_PLAYER(content);
2238 if (is_player && (found_rating < 1 || element < found_element))
2240 start_x = x + xx - 1;
2241 start_y = y + yy - 1;
2244 found_element = element;
2250 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2251 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2254 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2255 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2260 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2261 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2262 local_player->jx - MIDPOSX);
2264 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2265 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2266 local_player->jy - MIDPOSY);
2269 if (!game.restart_level)
2270 CloseDoor(DOOR_CLOSE_1);
2272 /* !!! FIX THIS (START) !!! */
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 InitGameEngine_EM();
2282 /* after drawing the level, correct some elements */
2283 if (game.timegate_time_left == 0)
2284 CloseAllOpenTimegates();
2286 if (setup.soft_scrolling)
2287 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2289 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2292 /* !!! FIX THIS (END) !!! */
2294 if (!game.restart_level)
2296 /* copy default game door content to main double buffer */
2297 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2298 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2301 DrawGameDoorValues();
2303 if (!game.restart_level)
2307 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2308 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2309 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2313 /* copy actual game door content to door double buffer for OpenDoor() */
2314 BlitBitmap(drawto, bitmap_db_door,
2315 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2317 OpenDoor(DOOR_OPEN_ALL);
2319 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2321 if (setup.sound_music)
2324 KeyboardAutoRepeatOffUnlessAutoplay();
2328 for (i = 0; i < MAX_PLAYERS; i++)
2329 printf("Player %d %sactive.\n",
2330 i + 1, (stored_player[i].active ? "" : "not "));
2334 game.restart_level = FALSE;
2337 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2339 /* this is used for non-R'n'D game engines to update certain engine values */
2341 /* needed to determine if sounds are played within the visible screen area */
2342 scroll_x = actual_scroll_x;
2343 scroll_y = actual_scroll_y;
2346 void InitMovDir(int x, int y)
2348 int i, element = Feld[x][y];
2349 static int xy[4][2] =
2356 static int direction[3][4] =
2358 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2359 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2360 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2369 Feld[x][y] = EL_BUG;
2370 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2373 case EL_SPACESHIP_RIGHT:
2374 case EL_SPACESHIP_UP:
2375 case EL_SPACESHIP_LEFT:
2376 case EL_SPACESHIP_DOWN:
2377 Feld[x][y] = EL_SPACESHIP;
2378 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2381 case EL_BD_BUTTERFLY_RIGHT:
2382 case EL_BD_BUTTERFLY_UP:
2383 case EL_BD_BUTTERFLY_LEFT:
2384 case EL_BD_BUTTERFLY_DOWN:
2385 Feld[x][y] = EL_BD_BUTTERFLY;
2386 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2389 case EL_BD_FIREFLY_RIGHT:
2390 case EL_BD_FIREFLY_UP:
2391 case EL_BD_FIREFLY_LEFT:
2392 case EL_BD_FIREFLY_DOWN:
2393 Feld[x][y] = EL_BD_FIREFLY;
2394 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2397 case EL_PACMAN_RIGHT:
2399 case EL_PACMAN_LEFT:
2400 case EL_PACMAN_DOWN:
2401 Feld[x][y] = EL_PACMAN;
2402 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2405 case EL_SP_SNIKSNAK:
2406 MovDir[x][y] = MV_UP;
2409 case EL_SP_ELECTRON:
2410 MovDir[x][y] = MV_LEFT;
2417 Feld[x][y] = EL_MOLE;
2418 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2422 if (IS_CUSTOM_ELEMENT(element))
2424 struct ElementInfo *ei = &element_info[element];
2425 int move_direction_initial = ei->move_direction_initial;
2426 int move_pattern = ei->move_pattern;
2428 if (move_direction_initial == MV_START_PREVIOUS)
2430 if (MovDir[x][y] != MV_NONE)
2433 move_direction_initial = MV_START_AUTOMATIC;
2436 if (move_direction_initial == MV_START_RANDOM)
2437 MovDir[x][y] = 1 << RND(4);
2438 else if (move_direction_initial & MV_ANY_DIRECTION)
2439 MovDir[x][y] = move_direction_initial;
2440 else if (move_pattern == MV_ALL_DIRECTIONS ||
2441 move_pattern == MV_TURNING_LEFT ||
2442 move_pattern == MV_TURNING_RIGHT ||
2443 move_pattern == MV_TURNING_LEFT_RIGHT ||
2444 move_pattern == MV_TURNING_RIGHT_LEFT ||
2445 move_pattern == MV_TURNING_RANDOM)
2446 MovDir[x][y] = 1 << RND(4);
2447 else if (move_pattern == MV_HORIZONTAL)
2448 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2449 else if (move_pattern == MV_VERTICAL)
2450 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2451 else if (move_pattern & MV_ANY_DIRECTION)
2452 MovDir[x][y] = element_info[element].move_pattern;
2453 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2454 move_pattern == MV_ALONG_RIGHT_SIDE)
2456 /* use random direction as default start direction */
2457 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2458 MovDir[x][y] = 1 << RND(4);
2460 for (i = 0; i < NUM_DIRECTIONS; i++)
2462 int x1 = x + xy[i][0];
2463 int y1 = y + xy[i][1];
2465 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2467 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2468 MovDir[x][y] = direction[0][i];
2470 MovDir[x][y] = direction[1][i];
2479 MovDir[x][y] = 1 << RND(4);
2481 if (element != EL_BUG &&
2482 element != EL_SPACESHIP &&
2483 element != EL_BD_BUTTERFLY &&
2484 element != EL_BD_FIREFLY)
2487 for (i = 0; i < NUM_DIRECTIONS; i++)
2489 int x1 = x + xy[i][0];
2490 int y1 = y + xy[i][1];
2492 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2494 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2496 MovDir[x][y] = direction[0][i];
2499 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2500 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2502 MovDir[x][y] = direction[1][i];
2511 GfxDir[x][y] = MovDir[x][y];
2514 void InitAmoebaNr(int x, int y)
2517 int group_nr = AmoebeNachbarNr(x, y);
2521 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2523 if (AmoebaCnt[i] == 0)
2531 AmoebaNr[x][y] = group_nr;
2532 AmoebaCnt[group_nr]++;
2533 AmoebaCnt2[group_nr]++;
2539 boolean raise_level = FALSE;
2541 if (local_player->MovPos)
2544 if (tape.auto_play) /* tape might already be stopped here */
2545 tape.auto_play_level_solved = TRUE;
2547 local_player->LevelSolved = FALSE;
2549 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2553 if (!tape.playing && setup.sound_loops)
2554 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2555 SND_CTRL_PLAY_LOOP);
2557 while (TimeLeft > 0)
2559 if (!tape.playing && !setup.sound_loops)
2560 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2562 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2565 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2570 RaiseScore(level.score[SC_TIME_BONUS]);
2573 DrawGameValue_Time(TimeLeft);
2581 if (!tape.playing && setup.sound_loops)
2582 StopSound(SND_GAME_LEVELTIME_BONUS);
2584 else if (level.time == 0) /* level without time limit */
2586 if (!tape.playing && setup.sound_loops)
2587 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2588 SND_CTRL_PLAY_LOOP);
2590 while (TimePlayed < 999)
2592 if (!tape.playing && !setup.sound_loops)
2593 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2595 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2598 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2603 RaiseScore(level.score[SC_TIME_BONUS]);
2606 DrawGameValue_Time(TimePlayed);
2614 if (!tape.playing && setup.sound_loops)
2615 StopSound(SND_GAME_LEVELTIME_BONUS);
2618 /* close exit door after last player */
2619 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2620 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2621 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2623 int element = Feld[ExitX][ExitY];
2625 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2626 EL_SP_EXIT_CLOSING);
2628 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2631 /* player disappears */
2632 if (ExitX >= 0 && ExitY >= 0)
2633 DrawLevelField(ExitX, ExitY);
2640 CloseDoor(DOOR_CLOSE_1);
2645 SaveTape(tape.level_nr); /* Ask to save tape */
2648 if (level_nr == leveldir_current->handicap_level)
2650 leveldir_current->handicap_level++;
2651 SaveLevelSetup_SeriesInfo();
2654 if (level_editor_test_game)
2655 local_player->score = -1; /* no highscore when playing from editor */
2656 else if (level_nr < leveldir_current->last_level)
2657 raise_level = TRUE; /* advance to next level */
2659 if ((hi_pos = NewHiScore()) >= 0)
2661 game_status = GAME_MODE_SCORES;
2662 DrawHallOfFame(hi_pos);
2671 game_status = GAME_MODE_MAIN;
2688 LoadScore(level_nr);
2690 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2691 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2694 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2696 if (local_player->score > highscore[k].Score)
2698 /* player has made it to the hall of fame */
2700 if (k < MAX_SCORE_ENTRIES - 1)
2702 int m = MAX_SCORE_ENTRIES - 1;
2705 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2706 if (!strcmp(setup.player_name, highscore[l].Name))
2708 if (m == k) /* player's new highscore overwrites his old one */
2712 for (l = m; l > k; l--)
2714 strcpy(highscore[l].Name, highscore[l - 1].Name);
2715 highscore[l].Score = highscore[l - 1].Score;
2722 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2723 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2724 highscore[k].Score = local_player->score;
2730 else if (!strncmp(setup.player_name, highscore[k].Name,
2731 MAX_PLAYER_NAME_LEN))
2732 break; /* player already there with a higher score */
2738 SaveScore(level_nr);
2743 inline static int getElementMoveStepsize(int x, int y)
2745 int element = Feld[x][y];
2746 int direction = MovDir[x][y];
2747 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2748 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2749 int horiz_move = (dx != 0);
2750 int sign = (horiz_move ? dx : dy);
2751 int step = sign * element_info[element].move_stepsize;
2753 /* special values for move stepsize for spring and things on conveyor belt */
2757 if (element == EL_SPRING)
2758 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2759 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2760 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2761 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2763 if (CAN_FALL(element) &&
2764 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2765 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2766 else if (element == EL_SPRING)
2767 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2774 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2776 if (player->GfxAction != action || player->GfxDir != dir)
2779 printf("Player frame reset! (%d => %d, %d => %d)\n",
2780 player->GfxAction, action, player->GfxDir, dir);
2783 player->GfxAction = action;
2784 player->GfxDir = dir;
2786 player->StepFrame = 0;
2790 static void ResetRandomAnimationValue(int x, int y)
2792 GfxRandom[x][y] = INIT_GFX_RANDOM();
2795 static void ResetGfxAnimation(int x, int y)
2798 GfxAction[x][y] = ACTION_DEFAULT;
2799 GfxDir[x][y] = MovDir[x][y];
2802 void InitMovingField(int x, int y, int direction)
2804 int element = Feld[x][y];
2805 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2806 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2810 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2811 ResetGfxAnimation(x, y);
2813 MovDir[x][y] = direction;
2814 GfxDir[x][y] = direction;
2815 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2816 ACTION_FALLING : ACTION_MOVING);
2818 /* this is needed for CEs with property "can move" / "not moving" */
2820 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2822 if (Feld[newx][newy] == EL_EMPTY)
2823 Feld[newx][newy] = EL_BLOCKED;
2825 MovDir[newx][newy] = MovDir[x][y];
2827 #if USE_NEW_CUSTOM_VALUE
2828 CustomValue[newx][newy] = CustomValue[x][y];
2831 GfxFrame[newx][newy] = GfxFrame[x][y];
2832 GfxRandom[newx][newy] = GfxRandom[x][y];
2833 GfxAction[newx][newy] = GfxAction[x][y];
2834 GfxDir[newx][newy] = GfxDir[x][y];
2838 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2840 int direction = MovDir[x][y];
2842 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2843 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2845 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2846 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2853 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2855 int oldx = x, oldy = y;
2856 int direction = MovDir[x][y];
2858 if (direction == MV_LEFT)
2860 else if (direction == MV_RIGHT)
2862 else if (direction == MV_UP)
2864 else if (direction == MV_DOWN)
2867 *comes_from_x = oldx;
2868 *comes_from_y = oldy;
2871 int MovingOrBlocked2Element(int x, int y)
2873 int element = Feld[x][y];
2875 if (element == EL_BLOCKED)
2879 Blocked2Moving(x, y, &oldx, &oldy);
2880 return Feld[oldx][oldy];
2886 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2888 /* like MovingOrBlocked2Element(), but if element is moving
2889 and (x,y) is the field the moving element is just leaving,
2890 return EL_BLOCKED instead of the element value */
2891 int element = Feld[x][y];
2893 if (IS_MOVING(x, y))
2895 if (element == EL_BLOCKED)
2899 Blocked2Moving(x, y, &oldx, &oldy);
2900 return Feld[oldx][oldy];
2909 static void RemoveField(int x, int y)
2911 Feld[x][y] = EL_EMPTY;
2917 #if USE_NEW_CUSTOM_VALUE
2918 CustomValue[x][y] = 0;
2922 ChangeDelay[x][y] = 0;
2923 ChangePage[x][y] = -1;
2924 Pushed[x][y] = FALSE;
2927 ExplodeField[x][y] = EX_TYPE_NONE;
2930 GfxElement[x][y] = EL_UNDEFINED;
2931 GfxAction[x][y] = ACTION_DEFAULT;
2932 GfxDir[x][y] = MV_NONE;
2935 void RemoveMovingField(int x, int y)
2937 int oldx = x, oldy = y, newx = x, newy = y;
2938 int element = Feld[x][y];
2939 int next_element = EL_UNDEFINED;
2941 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2944 if (IS_MOVING(x, y))
2946 Moving2Blocked(x, y, &newx, &newy);
2948 if (Feld[newx][newy] != EL_BLOCKED)
2950 /* element is moving, but target field is not free (blocked), but
2951 already occupied by something different (example: acid pool);
2952 in this case, only remove the moving field, but not the target */
2954 RemoveField(oldx, oldy);
2956 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2958 DrawLevelField(oldx, oldy);
2963 else if (element == EL_BLOCKED)
2965 Blocked2Moving(x, y, &oldx, &oldy);
2966 if (!IS_MOVING(oldx, oldy))
2970 if (element == EL_BLOCKED &&
2971 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2972 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2973 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2974 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2975 next_element = get_next_element(Feld[oldx][oldy]);
2977 RemoveField(oldx, oldy);
2978 RemoveField(newx, newy);
2980 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2982 if (next_element != EL_UNDEFINED)
2983 Feld[oldx][oldy] = next_element;
2985 DrawLevelField(oldx, oldy);
2986 DrawLevelField(newx, newy);
2989 void DrawDynamite(int x, int y)
2991 int sx = SCREENX(x), sy = SCREENY(y);
2992 int graphic = el2img(Feld[x][y]);
2995 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2998 if (IS_WALKABLE_INSIDE(Back[x][y]))
3002 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3003 else if (Store[x][y])
3004 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3006 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3008 if (Back[x][y] || Store[x][y])
3009 DrawGraphicThruMask(sx, sy, graphic, frame);
3011 DrawGraphic(sx, sy, graphic, frame);
3014 void CheckDynamite(int x, int y)
3016 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3020 if (MovDelay[x][y] != 0)
3023 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3029 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3034 void DrawRelocatePlayer(struct PlayerInfo *player)
3036 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3037 boolean no_delay = (tape.warp_forward);
3038 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3039 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3040 int jx = player->jx;
3041 int jy = player->jy;
3043 if (level.instant_relocation)
3045 int offset = (setup.scroll_delay ? 3 : 0);
3047 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3049 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3050 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3051 local_player->jx - MIDPOSX);
3053 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3054 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3055 local_player->jy - MIDPOSY);
3059 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3060 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3061 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3063 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3064 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3065 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3067 /* don't scroll over playfield boundaries */
3068 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3069 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3071 /* don't scroll over playfield boundaries */
3072 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3073 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3076 RedrawPlayfield(TRUE, 0,0,0,0);
3080 int scroll_xx = -999, scroll_yy = -999;
3082 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3084 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3087 int fx = FX, fy = FY;
3089 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3090 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3091 local_player->jx - MIDPOSX);
3093 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3094 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3095 local_player->jy - MIDPOSY);
3097 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3098 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3100 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3106 fx += dx * TILEX / 2;
3107 fy += dy * TILEY / 2;
3109 ScrollLevel(dx, dy);
3112 /* scroll in two steps of half tile size to make things smoother */
3113 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3115 Delay(wait_delay_value);
3117 /* scroll second step to align at full tile size */
3119 Delay(wait_delay_value);
3124 Delay(wait_delay_value);
3128 void RelocatePlayer(int jx, int jy, int el_player_raw)
3130 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3131 int player_nr = GET_PLAYER_NR(el_player);
3132 struct PlayerInfo *player = &stored_player[player_nr];
3133 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3134 boolean no_delay = (tape.warp_forward);
3135 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3136 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3137 int old_jx = player->jx;
3138 int old_jy = player->jy;
3139 int old_element = Feld[old_jx][old_jy];
3140 int element = Feld[jx][jy];
3141 boolean player_relocated = (old_jx != jx || old_jy != jy);
3143 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3144 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3145 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3146 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3147 int leave_side_horiz = move_dir_horiz;
3148 int leave_side_vert = move_dir_vert;
3149 int enter_side = enter_side_horiz | enter_side_vert;
3150 int leave_side = leave_side_horiz | leave_side_vert;
3152 if (player->GameOver) /* do not reanimate dead player */
3155 if (!player_relocated) /* no need to relocate the player */
3158 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3160 RemoveField(jx, jy); /* temporarily remove newly placed player */
3161 DrawLevelField(jx, jy);
3164 if (player->present)
3166 while (player->MovPos)
3168 ScrollPlayer(player, SCROLL_GO_ON);
3169 ScrollScreen(NULL, SCROLL_GO_ON);
3171 AdvanceFrameAndPlayerCounters(player->index_nr);
3176 Delay(wait_delay_value);
3179 DrawPlayer(player); /* needed here only to cleanup last field */
3180 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3182 player->is_moving = FALSE;
3185 if (IS_CUSTOM_ELEMENT(old_element))
3186 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3188 player->index_bit, leave_side);
3190 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3192 player->index_bit, leave_side);
3194 Feld[jx][jy] = el_player;
3195 InitPlayerField(jx, jy, el_player, TRUE);
3197 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3199 Feld[jx][jy] = element;
3200 InitField(jx, jy, FALSE);
3203 if (player == local_player) /* only visually relocate local player */
3204 DrawRelocatePlayer(player);
3206 TestIfPlayerTouchesBadThing(jx, jy);
3207 TestIfPlayerTouchesCustomElement(jx, jy);
3209 if (IS_CUSTOM_ELEMENT(element))
3210 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3211 player->index_bit, enter_side);
3213 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3214 player->index_bit, enter_side);
3217 void Explode(int ex, int ey, int phase, int mode)
3223 /* !!! eliminate this variable !!! */
3224 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3226 if (game.explosions_delayed)
3228 ExplodeField[ex][ey] = mode;
3232 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3234 int center_element = Feld[ex][ey];
3235 int artwork_element, explosion_element; /* set these values later */
3238 /* --- This is only really needed (and now handled) in "Impact()". --- */
3239 /* do not explode moving elements that left the explode field in time */
3240 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3241 center_element == EL_EMPTY &&
3242 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3247 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3248 if (mode == EX_TYPE_NORMAL ||
3249 mode == EX_TYPE_CENTER ||
3250 mode == EX_TYPE_CROSS)
3251 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3254 /* remove things displayed in background while burning dynamite */
3255 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3258 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3260 /* put moving element to center field (and let it explode there) */
3261 center_element = MovingOrBlocked2Element(ex, ey);
3262 RemoveMovingField(ex, ey);
3263 Feld[ex][ey] = center_element;
3266 /* now "center_element" is finally determined -- set related values now */
3267 artwork_element = center_element; /* for custom player artwork */
3268 explosion_element = center_element; /* for custom player artwork */
3270 if (IS_PLAYER(ex, ey))
3272 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3274 artwork_element = stored_player[player_nr].artwork_element;
3276 if (level.use_explosion_element[player_nr])
3278 explosion_element = level.explosion_element[player_nr];
3279 artwork_element = explosion_element;
3284 if (mode == EX_TYPE_NORMAL ||
3285 mode == EX_TYPE_CENTER ||
3286 mode == EX_TYPE_CROSS)
3287 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3291 last_phase = element_info[explosion_element].explosion_delay + 1;
3293 last_phase = element_info[center_element].explosion_delay + 1;
3296 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3298 int xx = x - ex + 1;
3299 int yy = y - ey + 1;
3302 if (!IN_LEV_FIELD(x, y) ||
3303 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3304 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3307 element = Feld[x][y];
3309 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3311 element = MovingOrBlocked2Element(x, y);
3313 if (!IS_EXPLOSION_PROOF(element))
3314 RemoveMovingField(x, y);
3317 /* indestructible elements can only explode in center (but not flames) */
3318 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3319 mode == EX_TYPE_BORDER)) ||
3320 element == EL_FLAMES)
3323 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3324 behaviour, for example when touching a yamyam that explodes to rocks
3325 with active deadly shield, a rock is created under the player !!! */
3326 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3328 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3329 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3330 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3332 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3335 if (IS_ACTIVE_BOMB(element))
3337 /* re-activate things under the bomb like gate or penguin */
3338 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3345 /* save walkable background elements while explosion on same tile */
3346 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3347 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3348 Back[x][y] = element;
3350 /* ignite explodable elements reached by other explosion */
3351 if (element == EL_EXPLOSION)
3352 element = Store2[x][y];
3354 if (AmoebaNr[x][y] &&
3355 (element == EL_AMOEBA_FULL ||
3356 element == EL_BD_AMOEBA ||
3357 element == EL_AMOEBA_GROWING))
3359 AmoebaCnt[AmoebaNr[x][y]]--;
3360 AmoebaCnt2[AmoebaNr[x][y]]--;
3365 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3368 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3370 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3372 switch(StorePlayer[ex][ey])
3375 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3378 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3381 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3385 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3390 if (PLAYERINFO(ex, ey)->use_murphy)
3391 Store[x][y] = EL_EMPTY;
3394 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3395 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3396 else if (ELEM_IS_PLAYER(center_element))
3397 Store[x][y] = EL_EMPTY;
3398 else if (center_element == EL_YAMYAM)
3399 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3400 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3401 Store[x][y] = element_info[center_element].content.e[xx][yy];
3403 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3404 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3405 otherwise) -- FIX THIS !!! */
3406 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3407 Store[x][y] = element_info[element].content.e[1][1];
3409 else if (!CAN_EXPLODE(element))
3410 Store[x][y] = element_info[element].content.e[1][1];
3413 Store[x][y] = EL_EMPTY;
3415 else if (center_element == EL_MOLE)
3416 Store[x][y] = EL_EMERALD_RED;
3417 else if (center_element == EL_PENGUIN)
3418 Store[x][y] = EL_EMERALD_PURPLE;
3419 else if (center_element == EL_BUG)
3420 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3421 else if (center_element == EL_BD_BUTTERFLY)
3422 Store[x][y] = EL_BD_DIAMOND;
3423 else if (center_element == EL_SP_ELECTRON)
3424 Store[x][y] = EL_SP_INFOTRON;
3425 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3426 Store[x][y] = level.amoeba_content;
3427 else if (center_element == EL_YAMYAM)
3428 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3429 else if (IS_CUSTOM_ELEMENT(center_element) &&
3430 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3431 Store[x][y] = element_info[center_element].content.e[xx][yy];
3432 else if (element == EL_WALL_EMERALD)
3433 Store[x][y] = EL_EMERALD;
3434 else if (element == EL_WALL_DIAMOND)
3435 Store[x][y] = EL_DIAMOND;
3436 else if (element == EL_WALL_BD_DIAMOND)
3437 Store[x][y] = EL_BD_DIAMOND;
3438 else if (element == EL_WALL_EMERALD_YELLOW)
3439 Store[x][y] = EL_EMERALD_YELLOW;
3440 else if (element == EL_WALL_EMERALD_RED)
3441 Store[x][y] = EL_EMERALD_RED;
3442 else if (element == EL_WALL_EMERALD_PURPLE)
3443 Store[x][y] = EL_EMERALD_PURPLE;
3444 else if (element == EL_WALL_PEARL)
3445 Store[x][y] = EL_PEARL;
3446 else if (element == EL_WALL_CRYSTAL)
3447 Store[x][y] = EL_CRYSTAL;
3448 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3449 Store[x][y] = element_info[element].content.e[1][1];
3451 Store[x][y] = EL_EMPTY;
3454 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3455 center_element == EL_AMOEBA_TO_DIAMOND)
3456 Store2[x][y] = element;
3458 Feld[x][y] = EL_EXPLOSION;
3459 GfxElement[x][y] = artwork_element;
3461 ExplodePhase[x][y] = 1;
3462 ExplodeDelay[x][y] = last_phase;
3467 if (center_element == EL_YAMYAM)
3468 game.yamyam_content_nr =
3469 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3481 GfxFrame[x][y] = 0; /* restart explosion animation */
3483 last_phase = ExplodeDelay[x][y];
3485 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3489 /* activate this even in non-DEBUG version until cause for crash in
3490 getGraphicAnimationFrame() (see below) is found and eliminated */
3495 if (GfxElement[x][y] == EL_UNDEFINED)
3498 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3499 printf("Explode(): This should never happen!\n");
3502 GfxElement[x][y] = EL_EMPTY;
3506 border_element = Store2[x][y];
3507 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3508 border_element = StorePlayer[x][y];
3510 if (phase == element_info[border_element].ignition_delay ||
3511 phase == last_phase)
3513 boolean border_explosion = FALSE;
3515 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3516 !PLAYER_EXPLOSION_PROTECTED(x, y))
3518 KillPlayerUnlessExplosionProtected(x, y);
3519 border_explosion = TRUE;
3521 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3523 Feld[x][y] = Store2[x][y];
3526 border_explosion = TRUE;
3528 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3530 AmoebeUmwandeln(x, y);
3532 border_explosion = TRUE;
3535 /* if an element just explodes due to another explosion (chain-reaction),
3536 do not immediately end the new explosion when it was the last frame of
3537 the explosion (as it would be done in the following "if"-statement!) */
3538 if (border_explosion && phase == last_phase)
3542 if (phase == last_phase)
3546 element = Feld[x][y] = Store[x][y];
3547 Store[x][y] = Store2[x][y] = 0;
3548 GfxElement[x][y] = EL_UNDEFINED;
3550 /* player can escape from explosions and might therefore be still alive */
3551 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3552 element <= EL_PLAYER_IS_EXPLODING_4)
3554 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3555 int explosion_element = EL_PLAYER_1 + player_nr;
3556 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3557 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3559 if (level.use_explosion_element[player_nr])
3560 explosion_element = level.explosion_element[player_nr];
3562 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3563 element_info[explosion_element].content.e[xx][yy]);
3566 /* restore probably existing indestructible background element */
3567 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3568 element = Feld[x][y] = Back[x][y];
3571 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3572 GfxDir[x][y] = MV_NONE;
3573 ChangeDelay[x][y] = 0;
3574 ChangePage[x][y] = -1;
3576 #if USE_NEW_CUSTOM_VALUE
3577 CustomValue[x][y] = 0;
3580 InitField_WithBug2(x, y, FALSE);
3582 DrawLevelField(x, y);
3584 TestIfElementTouchesCustomElement(x, y);
3586 if (GFX_CRUMBLED(element))
3587 DrawLevelFieldCrumbledSandNeighbours(x, y);
3589 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3590 StorePlayer[x][y] = 0;
3592 if (ELEM_IS_PLAYER(element))
3593 RelocatePlayer(x, y, element);
3595 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3597 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3598 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3601 DrawLevelFieldCrumbledSand(x, y);
3603 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3605 DrawLevelElement(x, y, Back[x][y]);
3606 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3608 else if (IS_WALKABLE_UNDER(Back[x][y]))
3610 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3611 DrawLevelElementThruMask(x, y, Back[x][y]);
3613 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3614 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3618 void DynaExplode(int ex, int ey)
3621 int dynabomb_element = Feld[ex][ey];
3622 int dynabomb_size = 1;
3623 boolean dynabomb_xl = FALSE;
3624 struct PlayerInfo *player;
3625 static int xy[4][2] =
3633 if (IS_ACTIVE_BOMB(dynabomb_element))
3635 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3636 dynabomb_size = player->dynabomb_size;
3637 dynabomb_xl = player->dynabomb_xl;
3638 player->dynabombs_left++;
3641 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3643 for (i = 0; i < NUM_DIRECTIONS; i++)
3645 for (j = 1; j <= dynabomb_size; j++)
3647 int x = ex + j * xy[i][0];
3648 int y = ey + j * xy[i][1];
3651 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3654 element = Feld[x][y];
3656 /* do not restart explosions of fields with active bombs */
3657 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3660 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3662 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3663 !IS_DIGGABLE(element) && !dynabomb_xl)
3669 void Bang(int x, int y)
3671 int element = MovingOrBlocked2Element(x, y);
3672 int explosion_type = EX_TYPE_NORMAL;
3674 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3676 struct PlayerInfo *player = PLAYERINFO(x, y);
3678 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3679 player->element_nr);
3681 if (level.use_explosion_element[player->index_nr])
3683 int explosion_element = level.explosion_element[player->index_nr];
3685 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3686 explosion_type = EX_TYPE_CROSS;
3687 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3688 explosion_type = EX_TYPE_CENTER;
3696 case EL_BD_BUTTERFLY:
3699 case EL_DARK_YAMYAM:
3703 RaiseScoreElement(element);
3706 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3707 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3708 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3709 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3710 case EL_DYNABOMB_INCREASE_NUMBER:
3711 case EL_DYNABOMB_INCREASE_SIZE:
3712 case EL_DYNABOMB_INCREASE_POWER:
3713 explosion_type = EX_TYPE_DYNA;
3718 case EL_LAMP_ACTIVE:
3719 case EL_AMOEBA_TO_DIAMOND:
3720 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3721 explosion_type = EX_TYPE_CENTER;
3725 if (element_info[element].explosion_type == EXPLODES_CROSS)
3726 explosion_type = EX_TYPE_CROSS;
3727 else if (element_info[element].explosion_type == EXPLODES_1X1)
3728 explosion_type = EX_TYPE_CENTER;
3732 if (explosion_type == EX_TYPE_DYNA)
3735 Explode(x, y, EX_PHASE_START, explosion_type);
3737 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3740 void SplashAcid(int x, int y)
3742 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3743 (!IN_LEV_FIELD(x - 1, y - 2) ||
3744 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3745 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3747 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3748 (!IN_LEV_FIELD(x + 1, y - 2) ||
3749 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3750 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3752 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3755 static void InitBeltMovement()
3757 static int belt_base_element[4] =
3759 EL_CONVEYOR_BELT_1_LEFT,
3760 EL_CONVEYOR_BELT_2_LEFT,
3761 EL_CONVEYOR_BELT_3_LEFT,
3762 EL_CONVEYOR_BELT_4_LEFT
3764 static int belt_base_active_element[4] =
3766 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3767 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3768 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3769 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3774 /* set frame order for belt animation graphic according to belt direction */
3775 for (i = 0; i < NUM_BELTS; i++)
3779 for (j = 0; j < NUM_BELT_PARTS; j++)
3781 int element = belt_base_active_element[belt_nr] + j;
3782 int graphic = el2img(element);
3784 if (game.belt_dir[i] == MV_LEFT)
3785 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3787 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3792 SCAN_PLAYFIELD(x, y)
3794 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3797 int element = Feld[x][y];
3799 for (i = 0; i < NUM_BELTS; i++)
3801 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3803 int e_belt_nr = getBeltNrFromBeltElement(element);
3806 if (e_belt_nr == belt_nr)
3808 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3810 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3817 static void ToggleBeltSwitch(int x, int y)
3819 static int belt_base_element[4] =
3821 EL_CONVEYOR_BELT_1_LEFT,
3822 EL_CONVEYOR_BELT_2_LEFT,
3823 EL_CONVEYOR_BELT_3_LEFT,
3824 EL_CONVEYOR_BELT_4_LEFT
3826 static int belt_base_active_element[4] =
3828 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3829 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3830 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3831 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3833 static int belt_base_switch_element[4] =
3835 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3836 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3837 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3838 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3840 static int belt_move_dir[4] =
3848 int element = Feld[x][y];
3849 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3850 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3851 int belt_dir = belt_move_dir[belt_dir_nr];
3854 if (!IS_BELT_SWITCH(element))
3857 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3858 game.belt_dir[belt_nr] = belt_dir;
3860 if (belt_dir_nr == 3)
3863 /* set frame order for belt animation graphic according to belt direction */
3864 for (i = 0; i < NUM_BELT_PARTS; i++)
3866 int element = belt_base_active_element[belt_nr] + i;
3867 int graphic = el2img(element);
3869 if (belt_dir == MV_LEFT)
3870 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3872 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3876 SCAN_PLAYFIELD(xx, yy)
3878 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3881 int element = Feld[xx][yy];
3883 if (IS_BELT_SWITCH(element))
3885 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3887 if (e_belt_nr == belt_nr)
3889 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3890 DrawLevelField(xx, yy);
3893 else if (IS_BELT(element) && belt_dir != MV_NONE)
3895 int e_belt_nr = getBeltNrFromBeltElement(element);
3897 if (e_belt_nr == belt_nr)
3899 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3901 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3902 DrawLevelField(xx, yy);
3905 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3907 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3909 if (e_belt_nr == belt_nr)
3911 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3913 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3914 DrawLevelField(xx, yy);
3920 static void ToggleSwitchgateSwitch(int x, int y)
3924 game.switchgate_pos = !game.switchgate_pos;
3927 SCAN_PLAYFIELD(xx, yy)
3929 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3932 int element = Feld[xx][yy];
3934 if (element == EL_SWITCHGATE_SWITCH_UP ||
3935 element == EL_SWITCHGATE_SWITCH_DOWN)
3937 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3938 DrawLevelField(xx, yy);
3940 else if (element == EL_SWITCHGATE_OPEN ||
3941 element == EL_SWITCHGATE_OPENING)
3943 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3945 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3947 else if (element == EL_SWITCHGATE_CLOSED ||
3948 element == EL_SWITCHGATE_CLOSING)
3950 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3952 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3957 static int getInvisibleActiveFromInvisibleElement(int element)
3959 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3960 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3961 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3965 static int getInvisibleFromInvisibleActiveElement(int element)
3967 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3968 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3969 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3973 static void RedrawAllLightSwitchesAndInvisibleElements()
3978 SCAN_PLAYFIELD(x, y)
3980 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3983 int element = Feld[x][y];
3985 if (element == EL_LIGHT_SWITCH &&
3986 game.light_time_left > 0)
3988 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3989 DrawLevelField(x, y);
3991 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3992 game.light_time_left == 0)
3994 Feld[x][y] = EL_LIGHT_SWITCH;
3995 DrawLevelField(x, y);
3997 else if (element == EL_EMC_DRIPPER &&
3998 game.light_time_left > 0)
4000 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4001 DrawLevelField(x, y);
4003 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4004 game.light_time_left == 0)
4006 Feld[x][y] = EL_EMC_DRIPPER;
4007 DrawLevelField(x, y);
4009 else if (element == EL_INVISIBLE_STEELWALL ||
4010 element == EL_INVISIBLE_WALL ||
4011 element == EL_INVISIBLE_SAND)
4013 if (game.light_time_left > 0)
4014 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4016 DrawLevelField(x, y);
4018 /* uncrumble neighbour fields, if needed */
4019 if (element == EL_INVISIBLE_SAND)
4020 DrawLevelFieldCrumbledSandNeighbours(x, y);
4022 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4023 element == EL_INVISIBLE_WALL_ACTIVE ||
4024 element == EL_INVISIBLE_SAND_ACTIVE)
4026 if (game.light_time_left == 0)
4027 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4029 DrawLevelField(x, y);
4031 /* re-crumble neighbour fields, if needed */
4032 if (element == EL_INVISIBLE_SAND)
4033 DrawLevelFieldCrumbledSandNeighbours(x, y);
4038 static void RedrawAllInvisibleElementsForLenses()
4043 SCAN_PLAYFIELD(x, y)
4045 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4048 int element = Feld[x][y];
4050 if (element == EL_EMC_DRIPPER &&
4051 game.lenses_time_left > 0)
4053 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4054 DrawLevelField(x, y);
4056 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4057 game.lenses_time_left == 0)
4059 Feld[x][y] = EL_EMC_DRIPPER;
4060 DrawLevelField(x, y);
4062 else if (element == EL_INVISIBLE_STEELWALL ||
4063 element == EL_INVISIBLE_WALL ||
4064 element == EL_INVISIBLE_SAND)
4066 if (game.lenses_time_left > 0)
4067 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4069 DrawLevelField(x, y);
4071 /* uncrumble neighbour fields, if needed */
4072 if (element == EL_INVISIBLE_SAND)
4073 DrawLevelFieldCrumbledSandNeighbours(x, y);
4075 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4076 element == EL_INVISIBLE_WALL_ACTIVE ||
4077 element == EL_INVISIBLE_SAND_ACTIVE)
4079 if (game.lenses_time_left == 0)
4080 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4082 DrawLevelField(x, y);
4084 /* re-crumble neighbour fields, if needed */
4085 if (element == EL_INVISIBLE_SAND)
4086 DrawLevelFieldCrumbledSandNeighbours(x, y);
4091 static void RedrawAllInvisibleElementsForMagnifier()
4096 SCAN_PLAYFIELD(x, y)
4098 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4101 int element = Feld[x][y];
4103 if (element == EL_EMC_FAKE_GRASS &&
4104 game.magnify_time_left > 0)
4106 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4107 DrawLevelField(x, y);
4109 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4110 game.magnify_time_left == 0)
4112 Feld[x][y] = EL_EMC_FAKE_GRASS;
4113 DrawLevelField(x, y);
4115 else if (IS_GATE_GRAY(element) &&
4116 game.magnify_time_left > 0)
4118 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4119 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4120 IS_EM_GATE_GRAY(element) ?
4121 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4122 IS_EMC_GATE_GRAY(element) ?
4123 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4125 DrawLevelField(x, y);
4127 else if (IS_GATE_GRAY_ACTIVE(element) &&
4128 game.magnify_time_left == 0)
4130 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4131 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4132 IS_EM_GATE_GRAY_ACTIVE(element) ?
4133 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4134 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4135 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4137 DrawLevelField(x, y);
4142 static void ToggleLightSwitch(int x, int y)
4144 int element = Feld[x][y];
4146 game.light_time_left =
4147 (element == EL_LIGHT_SWITCH ?
4148 level.time_light * FRAMES_PER_SECOND : 0);
4150 RedrawAllLightSwitchesAndInvisibleElements();
4153 static void ActivateTimegateSwitch(int x, int y)
4157 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4160 SCAN_PLAYFIELD(xx, yy)
4162 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4165 int element = Feld[xx][yy];
4167 if (element == EL_TIMEGATE_CLOSED ||
4168 element == EL_TIMEGATE_CLOSING)
4170 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4171 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4175 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4177 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4178 DrawLevelField(xx, yy);
4184 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4187 void Impact(int x, int y)
4189 boolean last_line = (y == lev_fieldy - 1);
4190 boolean object_hit = FALSE;
4191 boolean impact = (last_line || object_hit);
4192 int element = Feld[x][y];
4193 int smashed = EL_STEELWALL;
4195 if (!last_line) /* check if element below was hit */
4197 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4200 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4201 MovDir[x][y + 1] != MV_DOWN ||
4202 MovPos[x][y + 1] <= TILEY / 2));
4204 /* do not smash moving elements that left the smashed field in time */
4205 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4206 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4209 #if USE_QUICKSAND_IMPACT_BUGFIX
4210 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4212 RemoveMovingField(x, y + 1);
4213 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4214 Feld[x][y + 2] = EL_ROCK;
4215 DrawLevelField(x, y + 2);
4222 smashed = MovingOrBlocked2Element(x, y + 1);
4224 impact = (last_line || object_hit);
4227 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4229 SplashAcid(x, y + 1);
4233 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4234 /* only reset graphic animation if graphic really changes after impact */
4236 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4238 ResetGfxAnimation(x, y);
4239 DrawLevelField(x, y);
4242 if (impact && CAN_EXPLODE_IMPACT(element))
4247 else if (impact && element == EL_PEARL)
4249 ResetGfxAnimation(x, y);
4251 Feld[x][y] = EL_PEARL_BREAKING;
4252 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4255 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4257 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4262 if (impact && element == EL_AMOEBA_DROP)
4264 if (object_hit && IS_PLAYER(x, y + 1))
4265 KillPlayerUnlessEnemyProtected(x, y + 1);
4266 else if (object_hit && smashed == EL_PENGUIN)
4270 Feld[x][y] = EL_AMOEBA_GROWING;
4271 Store[x][y] = EL_AMOEBA_WET;
4273 ResetRandomAnimationValue(x, y);
4278 if (object_hit) /* check which object was hit */
4280 if (CAN_PASS_MAGIC_WALL(element) &&
4281 (smashed == EL_MAGIC_WALL ||
4282 smashed == EL_BD_MAGIC_WALL))
4285 int activated_magic_wall =
4286 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4287 EL_BD_MAGIC_WALL_ACTIVE);
4289 /* activate magic wall / mill */
4291 SCAN_PLAYFIELD(xx, yy)
4293 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4295 if (Feld[xx][yy] == smashed)
4296 Feld[xx][yy] = activated_magic_wall;
4298 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4299 game.magic_wall_active = TRUE;
4301 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4302 SND_MAGIC_WALL_ACTIVATING :
4303 SND_BD_MAGIC_WALL_ACTIVATING));
4306 if (IS_PLAYER(x, y + 1))
4308 if (CAN_SMASH_PLAYER(element))
4310 KillPlayerUnlessEnemyProtected(x, y + 1);
4314 else if (smashed == EL_PENGUIN)
4316 if (CAN_SMASH_PLAYER(element))
4322 else if (element == EL_BD_DIAMOND)
4324 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4330 else if (((element == EL_SP_INFOTRON ||
4331 element == EL_SP_ZONK) &&
4332 (smashed == EL_SP_SNIKSNAK ||
4333 smashed == EL_SP_ELECTRON ||
4334 smashed == EL_SP_DISK_ORANGE)) ||
4335 (element == EL_SP_INFOTRON &&
4336 smashed == EL_SP_DISK_YELLOW))
4341 else if (CAN_SMASH_EVERYTHING(element))
4343 if (IS_CLASSIC_ENEMY(smashed) ||
4344 CAN_EXPLODE_SMASHED(smashed))
4349 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4351 if (smashed == EL_LAMP ||
4352 smashed == EL_LAMP_ACTIVE)
4357 else if (smashed == EL_NUT)
4359 Feld[x][y + 1] = EL_NUT_BREAKING;
4360 PlayLevelSound(x, y, SND_NUT_BREAKING);
4361 RaiseScoreElement(EL_NUT);
4364 else if (smashed == EL_PEARL)
4366 ResetGfxAnimation(x, y);
4368 Feld[x][y + 1] = EL_PEARL_BREAKING;
4369 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4372 else if (smashed == EL_DIAMOND)
4374 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4375 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4378 else if (IS_BELT_SWITCH(smashed))
4380 ToggleBeltSwitch(x, y + 1);
4382 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4383 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4385 ToggleSwitchgateSwitch(x, y + 1);
4387 else if (smashed == EL_LIGHT_SWITCH ||
4388 smashed == EL_LIGHT_SWITCH_ACTIVE)
4390 ToggleLightSwitch(x, y + 1);
4395 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4398 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4400 CheckElementChangeBySide(x, y + 1, smashed, element,
4401 CE_SWITCHED, CH_SIDE_TOP);
4402 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4408 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4413 /* play sound of magic wall / mill */
4415 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4416 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4418 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4419 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4420 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4421 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4426 /* play sound of object that hits the ground */
4427 if (last_line || object_hit)
4428 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4431 inline static void TurnRoundExt(int x, int y)
4443 { 0, 0 }, { 0, 0 }, { 0, 0 },
4448 int left, right, back;
4452 { MV_DOWN, MV_UP, MV_RIGHT },
4453 { MV_UP, MV_DOWN, MV_LEFT },
4455 { MV_LEFT, MV_RIGHT, MV_DOWN },
4459 { MV_RIGHT, MV_LEFT, MV_UP }
4462 int element = Feld[x][y];
4463 int move_pattern = element_info[element].move_pattern;
4465 int old_move_dir = MovDir[x][y];
4466 int left_dir = turn[old_move_dir].left;
4467 int right_dir = turn[old_move_dir].right;
4468 int back_dir = turn[old_move_dir].back;
4470 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4471 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4472 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4473 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4475 int left_x = x + left_dx, left_y = y + left_dy;
4476 int right_x = x + right_dx, right_y = y + right_dy;
4477 int move_x = x + move_dx, move_y = y + move_dy;
4481 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4483 TestIfBadThingTouchesOtherBadThing(x, y);
4485 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4486 MovDir[x][y] = right_dir;
4487 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4488 MovDir[x][y] = left_dir;
4490 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4492 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4495 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4497 TestIfBadThingTouchesOtherBadThing(x, y);
4499 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4500 MovDir[x][y] = left_dir;
4501 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4502 MovDir[x][y] = right_dir;
4504 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4506 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4509 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4511 TestIfBadThingTouchesOtherBadThing(x, y);
4513 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4514 MovDir[x][y] = left_dir;
4515 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4516 MovDir[x][y] = right_dir;
4518 if (MovDir[x][y] != old_move_dir)
4521 else if (element == EL_YAMYAM)
4523 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4524 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4526 if (can_turn_left && can_turn_right)
4527 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4528 else if (can_turn_left)
4529 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4530 else if (can_turn_right)
4531 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4533 MovDir[x][y] = back_dir;
4535 MovDelay[x][y] = 16 + 16 * RND(3);
4537 else if (element == EL_DARK_YAMYAM)
4539 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4541 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4544 if (can_turn_left && can_turn_right)
4545 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4546 else if (can_turn_left)
4547 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4548 else if (can_turn_right)
4549 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4551 MovDir[x][y] = back_dir;
4553 MovDelay[x][y] = 16 + 16 * RND(3);
4555 else if (element == EL_PACMAN)
4557 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4558 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4560 if (can_turn_left && can_turn_right)
4561 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4562 else if (can_turn_left)
4563 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4564 else if (can_turn_right)
4565 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4567 MovDir[x][y] = back_dir;
4569 MovDelay[x][y] = 6 + RND(40);
4571 else if (element == EL_PIG)
4573 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4574 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4575 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4576 boolean should_turn_left, should_turn_right, should_move_on;
4578 int rnd = RND(rnd_value);
4580 should_turn_left = (can_turn_left &&
4582 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4583 y + back_dy + left_dy)));
4584 should_turn_right = (can_turn_right &&
4586 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4587 y + back_dy + right_dy)));
4588 should_move_on = (can_move_on &&
4591 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4592 y + move_dy + left_dy) ||
4593 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4594 y + move_dy + right_dy)));
4596 if (should_turn_left || should_turn_right || should_move_on)
4598 if (should_turn_left && should_turn_right && should_move_on)
4599 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4600 rnd < 2 * rnd_value / 3 ? right_dir :
4602 else if (should_turn_left && should_turn_right)
4603 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4604 else if (should_turn_left && should_move_on)
4605 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4606 else if (should_turn_right && should_move_on)
4607 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4608 else if (should_turn_left)
4609 MovDir[x][y] = left_dir;
4610 else if (should_turn_right)
4611 MovDir[x][y] = right_dir;
4612 else if (should_move_on)
4613 MovDir[x][y] = old_move_dir;
4615 else if (can_move_on && rnd > rnd_value / 8)
4616 MovDir[x][y] = old_move_dir;
4617 else if (can_turn_left && can_turn_right)
4618 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4619 else if (can_turn_left && rnd > rnd_value / 8)
4620 MovDir[x][y] = left_dir;
4621 else if (can_turn_right && rnd > rnd_value/8)
4622 MovDir[x][y] = right_dir;
4624 MovDir[x][y] = back_dir;
4626 xx = x + move_xy[MovDir[x][y]].dx;
4627 yy = y + move_xy[MovDir[x][y]].dy;
4629 if (!IN_LEV_FIELD(xx, yy) ||
4630 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4631 MovDir[x][y] = old_move_dir;
4635 else if (element == EL_DRAGON)
4637 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4638 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4639 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4641 int rnd = RND(rnd_value);
4643 if (can_move_on && rnd > rnd_value / 8)
4644 MovDir[x][y] = old_move_dir;
4645 else if (can_turn_left && can_turn_right)
4646 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4647 else if (can_turn_left && rnd > rnd_value / 8)
4648 MovDir[x][y] = left_dir;
4649 else if (can_turn_right && rnd > rnd_value / 8)
4650 MovDir[x][y] = right_dir;
4652 MovDir[x][y] = back_dir;
4654 xx = x + move_xy[MovDir[x][y]].dx;
4655 yy = y + move_xy[MovDir[x][y]].dy;
4657 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4658 MovDir[x][y] = old_move_dir;
4662 else if (element == EL_MOLE)
4664 boolean can_move_on =
4665 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4666 IS_AMOEBOID(Feld[move_x][move_y]) ||
4667 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4670 boolean can_turn_left =
4671 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4672 IS_AMOEBOID(Feld[left_x][left_y])));
4674 boolean can_turn_right =
4675 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4676 IS_AMOEBOID(Feld[right_x][right_y])));
4678 if (can_turn_left && can_turn_right)
4679 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4680 else if (can_turn_left)
4681 MovDir[x][y] = left_dir;
4683 MovDir[x][y] = right_dir;
4686 if (MovDir[x][y] != old_move_dir)
4689 else if (element == EL_BALLOON)
4691 MovDir[x][y] = game.wind_direction;
4694 else if (element == EL_SPRING)
4696 #if USE_NEW_SPRING_BUMPER
4697 if (MovDir[x][y] & MV_HORIZONTAL)
4699 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4700 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4702 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4703 ResetGfxAnimation(move_x, move_y);
4704 DrawLevelField(move_x, move_y);
4706 MovDir[x][y] = back_dir;
4708 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4709 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4710 MovDir[x][y] = MV_NONE;
4713 if (MovDir[x][y] & MV_HORIZONTAL &&
4714 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4715 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4716 MovDir[x][y] = MV_NONE;
4721 else if (element == EL_ROBOT ||
4722 element == EL_SATELLITE ||
4723 element == EL_PENGUIN ||
4724 element == EL_EMC_ANDROID)
4726 int attr_x = -1, attr_y = -1;
4737 for (i = 0; i < MAX_PLAYERS; i++)
4739 struct PlayerInfo *player = &stored_player[i];
4740 int jx = player->jx, jy = player->jy;
4742 if (!player->active)
4746 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4754 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4755 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4756 game.engine_version < VERSION_IDENT(3,1,0,0)))
4762 if (element == EL_PENGUIN)
4765 static int xy[4][2] =
4773 for (i = 0; i < NUM_DIRECTIONS; i++)
4775 int ex = x + xy[i][0];
4776 int ey = y + xy[i][1];
4778 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4787 MovDir[x][y] = MV_NONE;
4789 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4790 else if (attr_x > x)
4791 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4793 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4794 else if (attr_y > y)
4795 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4797 if (element == EL_ROBOT)
4801 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4802 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4803 Moving2Blocked(x, y, &newx, &newy);
4805 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4806 MovDelay[x][y] = 8 + 8 * !RND(3);
4808 MovDelay[x][y] = 16;
4810 else if (element == EL_PENGUIN)
4816 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4818 boolean first_horiz = RND(2);
4819 int new_move_dir = MovDir[x][y];
4822 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4823 Moving2Blocked(x, y, &newx, &newy);
4825 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4829 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4830 Moving2Blocked(x, y, &newx, &newy);
4832 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4835 MovDir[x][y] = old_move_dir;
4839 else if (element == EL_SATELLITE)
4845 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4847 boolean first_horiz = RND(2);
4848 int new_move_dir = MovDir[x][y];
4851 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4852 Moving2Blocked(x, y, &newx, &newy);
4854 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4858 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4859 Moving2Blocked(x, y, &newx, &newy);
4861 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4864 MovDir[x][y] = old_move_dir;
4868 else if (element == EL_EMC_ANDROID)
4870 static int check_pos[16] =
4872 -1, /* 0 => (invalid) */
4873 7, /* 1 => MV_LEFT */
4874 3, /* 2 => MV_RIGHT */
4875 -1, /* 3 => (invalid) */
4877 0, /* 5 => MV_LEFT | MV_UP */
4878 2, /* 6 => MV_RIGHT | MV_UP */
4879 -1, /* 7 => (invalid) */
4880 5, /* 8 => MV_DOWN */
4881 6, /* 9 => MV_LEFT | MV_DOWN */
4882 4, /* 10 => MV_RIGHT | MV_DOWN */
4883 -1, /* 11 => (invalid) */
4884 -1, /* 12 => (invalid) */
4885 -1, /* 13 => (invalid) */
4886 -1, /* 14 => (invalid) */
4887 -1, /* 15 => (invalid) */
4895 { -1, -1, MV_LEFT | MV_UP },
4897 { +1, -1, MV_RIGHT | MV_UP },
4898 { +1, 0, MV_RIGHT },
4899 { +1, +1, MV_RIGHT | MV_DOWN },
4901 { -1, +1, MV_LEFT | MV_DOWN },
4904 int check_order = (RND(2) ? -1 : +1);
4905 int start_pos = check_pos[MovDir[x][y] & 0x0f];
4908 MovDelay[x][y] = level.android_move_time * 8 + 1;
4910 if (start_pos < 0) /* (should never happen) */
4913 for (i = 0; i < 3; i++)
4915 /* first check start_pos, then previous/next or (next/previous) pos */
4916 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
4917 int pos = (pos_raw + 8) % 8;
4918 int newx = x + check_xy[pos].dx;
4919 int newy = y + check_xy[pos].dy;
4920 int new_move_dir = check_xy[pos].dir;
4922 if (IS_PLAYER(newx, newy))
4925 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
4927 MovDir[x][y] = new_move_dir;
4934 else if (move_pattern == MV_TURNING_LEFT ||
4935 move_pattern == MV_TURNING_RIGHT ||
4936 move_pattern == MV_TURNING_LEFT_RIGHT ||
4937 move_pattern == MV_TURNING_RIGHT_LEFT ||
4938 move_pattern == MV_TURNING_RANDOM ||
4939 move_pattern == MV_ALL_DIRECTIONS)
4941 boolean can_turn_left =
4942 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4943 boolean can_turn_right =
4944 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4946 if (element_info[element].move_stepsize == 0) /* "not moving" */
4949 if (move_pattern == MV_TURNING_LEFT)
4950 MovDir[x][y] = left_dir;
4951 else if (move_pattern == MV_TURNING_RIGHT)
4952 MovDir[x][y] = right_dir;
4953 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4954 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4955 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4956 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4957 else if (move_pattern == MV_TURNING_RANDOM)
4958 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4959 can_turn_right && !can_turn_left ? right_dir :
4960 RND(2) ? left_dir : right_dir);
4961 else if (can_turn_left && can_turn_right)
4962 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4963 else if (can_turn_left)
4964 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4965 else if (can_turn_right)
4966 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4968 MovDir[x][y] = back_dir;
4970 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4972 else if (move_pattern == MV_HORIZONTAL ||
4973 move_pattern == MV_VERTICAL)
4975 if (move_pattern & old_move_dir)
4976 MovDir[x][y] = back_dir;
4977 else if (move_pattern == MV_HORIZONTAL)
4978 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4979 else if (move_pattern == MV_VERTICAL)
4980 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4982 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4984 else if (move_pattern & MV_ANY_DIRECTION)
4986 MovDir[x][y] = move_pattern;
4987 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4989 else if (move_pattern & MV_WIND_DIRECTION)
4991 MovDir[x][y] = game.wind_direction;
4992 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4994 else if (move_pattern == MV_ALONG_LEFT_SIDE)
4996 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4997 MovDir[x][y] = left_dir;
4998 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4999 MovDir[x][y] = right_dir;
5001 if (MovDir[x][y] != old_move_dir)
5002 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5004 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5006 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5007 MovDir[x][y] = right_dir;
5008 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5009 MovDir[x][y] = left_dir;
5011 if (MovDir[x][y] != old_move_dir)
5012 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5014 else if (move_pattern == MV_TOWARDS_PLAYER ||
5015 move_pattern == MV_AWAY_FROM_PLAYER)
5017 int attr_x = -1, attr_y = -1;
5019 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5030 for (i = 0; i < MAX_PLAYERS; i++)
5032 struct PlayerInfo *player = &stored_player[i];
5033 int jx = player->jx, jy = player->jy;
5035 if (!player->active)
5039 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5047 MovDir[x][y] = MV_NONE;
5049 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5050 else if (attr_x > x)
5051 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5053 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5054 else if (attr_y > y)
5055 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5057 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5059 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5061 boolean first_horiz = RND(2);
5062 int new_move_dir = MovDir[x][y];
5064 if (element_info[element].move_stepsize == 0) /* "not moving" */
5066 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5067 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5073 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5074 Moving2Blocked(x, y, &newx, &newy);
5076 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5080 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5081 Moving2Blocked(x, y, &newx, &newy);
5083 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5086 MovDir[x][y] = old_move_dir;
5089 else if (move_pattern == MV_WHEN_PUSHED ||
5090 move_pattern == MV_WHEN_DROPPED)
5092 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5093 MovDir[x][y] = MV_NONE;
5097 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5099 static int test_xy[7][2] =
5109 static int test_dir[7] =
5119 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5120 int move_preference = -1000000; /* start with very low preference */
5121 int new_move_dir = MV_NONE;
5122 int start_test = RND(4);
5125 for (i = 0; i < NUM_DIRECTIONS; i++)
5127 int move_dir = test_dir[start_test + i];
5128 int move_dir_preference;
5130 xx = x + test_xy[start_test + i][0];
5131 yy = y + test_xy[start_test + i][1];
5133 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5134 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5136 new_move_dir = move_dir;
5141 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5144 move_dir_preference = -1 * RunnerVisit[xx][yy];
5145 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5146 move_dir_preference = PlayerVisit[xx][yy];
5148 if (move_dir_preference > move_preference)
5150 /* prefer field that has not been visited for the longest time */
5151 move_preference = move_dir_preference;
5152 new_move_dir = move_dir;
5154 else if (move_dir_preference == move_preference &&
5155 move_dir == old_move_dir)
5157 /* prefer last direction when all directions are preferred equally */
5158 move_preference = move_dir_preference;
5159 new_move_dir = move_dir;
5163 MovDir[x][y] = new_move_dir;
5164 if (old_move_dir != new_move_dir)
5165 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5169 static void TurnRound(int x, int y)
5171 int direction = MovDir[x][y];
5175 GfxDir[x][y] = MovDir[x][y];
5177 if (direction != MovDir[x][y])
5181 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5184 static boolean JustBeingPushed(int x, int y)
5188 for (i = 0; i < MAX_PLAYERS; i++)
5190 struct PlayerInfo *player = &stored_player[i];
5192 if (player->active && player->is_pushing && player->MovPos)
5194 int next_jx = player->jx + (player->jx - player->last_jx);
5195 int next_jy = player->jy + (player->jy - player->last_jy);
5197 if (x == next_jx && y == next_jy)
5205 void StartMoving(int x, int y)
5207 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5208 int element = Feld[x][y];
5213 if (MovDelay[x][y] == 0)
5214 GfxAction[x][y] = ACTION_DEFAULT;
5216 if (CAN_FALL(element) && y < lev_fieldy - 1)
5218 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5219 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5220 if (JustBeingPushed(x, y))
5223 if (element == EL_QUICKSAND_FULL)
5225 if (IS_FREE(x, y + 1))
5227 InitMovingField(x, y, MV_DOWN);
5228 started_moving = TRUE;
5230 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5231 Store[x][y] = EL_ROCK;
5233 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5235 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5237 if (!MovDelay[x][y])
5238 MovDelay[x][y] = TILEY + 1;
5247 Feld[x][y] = EL_QUICKSAND_EMPTY;
5248 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5249 Store[x][y + 1] = Store[x][y];
5252 PlayLevelSoundAction(x, y, ACTION_FILLING);
5255 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5256 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5258 InitMovingField(x, y, MV_DOWN);
5259 started_moving = TRUE;
5261 Feld[x][y] = EL_QUICKSAND_FILLING;
5262 Store[x][y] = element;
5264 PlayLevelSoundAction(x, y, ACTION_FILLING);
5266 else if (element == EL_MAGIC_WALL_FULL)
5268 if (IS_FREE(x, y + 1))
5270 InitMovingField(x, y, MV_DOWN);
5271 started_moving = TRUE;
5273 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5274 Store[x][y] = EL_CHANGED(Store[x][y]);
5276 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5278 if (!MovDelay[x][y])
5279 MovDelay[x][y] = TILEY/4 + 1;
5288 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5289 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5290 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5294 else if (element == EL_BD_MAGIC_WALL_FULL)
5296 if (IS_FREE(x, y + 1))
5298 InitMovingField(x, y, MV_DOWN);
5299 started_moving = TRUE;
5301 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5302 Store[x][y] = EL_CHANGED2(Store[x][y]);
5304 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5306 if (!MovDelay[x][y])
5307 MovDelay[x][y] = TILEY/4 + 1;
5316 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5317 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5318 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5322 else if (CAN_PASS_MAGIC_WALL(element) &&
5323 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5324 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5326 InitMovingField(x, y, MV_DOWN);
5327 started_moving = TRUE;
5330 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5331 EL_BD_MAGIC_WALL_FILLING);
5332 Store[x][y] = element;
5334 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5336 SplashAcid(x, y + 1);
5338 InitMovingField(x, y, MV_DOWN);
5339 started_moving = TRUE;
5341 Store[x][y] = EL_ACID;
5343 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5344 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5346 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5347 CAN_FALL(element) && WasJustFalling[x][y] &&
5348 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5350 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5351 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5352 (Feld[x][y + 1] == EL_BLOCKED)))
5354 /* this is needed for a special case not covered by calling "Impact()"
5355 from "ContinueMoving()": if an element moves to a tile directly below
5356 another element which was just falling on that tile (which was empty
5357 in the previous frame), the falling element above would just stop
5358 instead of smashing the element below (in previous version, the above
5359 element was just checked for "moving" instead of "falling", resulting
5360 in incorrect smashes caused by horizontal movement of the above
5361 element; also, the case of the player being the element to smash was
5362 simply not covered here... :-/ ) */
5364 CheckCollision[x][y] = 0;
5368 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5370 if (MovDir[x][y] == MV_NONE)
5372 InitMovingField(x, y, MV_DOWN);
5373 started_moving = TRUE;
5376 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5378 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5379 MovDir[x][y] = MV_DOWN;
5381 InitMovingField(x, y, MV_DOWN);
5382 started_moving = TRUE;
5384 else if (element == EL_AMOEBA_DROP)
5386 Feld[x][y] = EL_AMOEBA_GROWING;
5387 Store[x][y] = EL_AMOEBA_WET;
5389 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5390 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5391 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5392 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5394 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5395 (IS_FREE(x - 1, y + 1) ||
5396 Feld[x - 1][y + 1] == EL_ACID));
5397 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5398 (IS_FREE(x + 1, y + 1) ||
5399 Feld[x + 1][y + 1] == EL_ACID));
5400 boolean can_fall_any = (can_fall_left || can_fall_right);
5401 boolean can_fall_both = (can_fall_left && can_fall_right);
5402 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5404 #if USE_NEW_ALL_SLIPPERY
5405 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5407 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5408 can_fall_right = FALSE;
5409 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5410 can_fall_left = FALSE;
5411 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5412 can_fall_right = FALSE;
5413 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5414 can_fall_left = FALSE;
5416 can_fall_any = (can_fall_left || can_fall_right);
5417 can_fall_both = FALSE;
5420 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5422 if (slippery_type == SLIPPERY_ONLY_LEFT)
5423 can_fall_right = FALSE;
5424 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5425 can_fall_left = FALSE;
5426 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5427 can_fall_right = FALSE;
5428 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5429 can_fall_left = FALSE;
5431 can_fall_any = (can_fall_left || can_fall_right);
5432 can_fall_both = (can_fall_left && can_fall_right);
5436 #if USE_NEW_ALL_SLIPPERY
5438 #if USE_NEW_SP_SLIPPERY
5439 /* !!! better use the same properties as for custom elements here !!! */
5440 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5441 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5443 can_fall_right = FALSE; /* slip down on left side */
5444 can_fall_both = FALSE;
5449 #if USE_NEW_ALL_SLIPPERY
5452 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5453 can_fall_right = FALSE; /* slip down on left side */
5455 can_fall_left = !(can_fall_right = RND(2));
5457 can_fall_both = FALSE;
5462 if (game.emulation == EMU_BOULDERDASH ||
5463 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5464 can_fall_right = FALSE; /* slip down on left side */
5466 can_fall_left = !(can_fall_right = RND(2));
5468 can_fall_both = FALSE;
5474 /* if not determined otherwise, prefer left side for slipping down */
5475 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5476 started_moving = TRUE;
5480 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5482 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5485 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5486 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5487 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5488 int belt_dir = game.belt_dir[belt_nr];
5490 if ((belt_dir == MV_LEFT && left_is_free) ||
5491 (belt_dir == MV_RIGHT && right_is_free))
5493 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5495 InitMovingField(x, y, belt_dir);
5496 started_moving = TRUE;
5498 Pushed[x][y] = TRUE;
5499 Pushed[nextx][y] = TRUE;
5501 GfxAction[x][y] = ACTION_DEFAULT;
5505 MovDir[x][y] = 0; /* if element was moving, stop it */
5510 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5512 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5514 if (CAN_MOVE(element) && !started_moving)
5517 int move_pattern = element_info[element].move_pattern;
5522 if (MovDir[x][y] == MV_NONE)
5524 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5525 x, y, element, element_info[element].token_name);
5526 printf("StartMoving(): This should never happen!\n");
5531 Moving2Blocked(x, y, &newx, &newy);
5533 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5536 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5537 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5539 WasJustMoving[x][y] = 0;
5540 CheckCollision[x][y] = 0;
5542 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5544 if (Feld[x][y] != element) /* element has changed */
5548 if (!MovDelay[x][y]) /* start new movement phase */
5550 /* all objects that can change their move direction after each step
5551 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5553 if (element != EL_YAMYAM &&
5554 element != EL_DARK_YAMYAM &&
5555 element != EL_PACMAN &&
5556 !(move_pattern & MV_ANY_DIRECTION) &&
5557 move_pattern != MV_TURNING_LEFT &&
5558 move_pattern != MV_TURNING_RIGHT &&
5559 move_pattern != MV_TURNING_LEFT_RIGHT &&
5560 move_pattern != MV_TURNING_RIGHT_LEFT &&
5561 move_pattern != MV_TURNING_RANDOM)
5565 if (MovDelay[x][y] && (element == EL_BUG ||
5566 element == EL_SPACESHIP ||
5567 element == EL_SP_SNIKSNAK ||
5568 element == EL_SP_ELECTRON ||
5569 element == EL_MOLE))
5570 DrawLevelField(x, y);
5574 if (MovDelay[x][y]) /* wait some time before next movement */
5578 if (element == EL_ROBOT ||
5579 element == EL_YAMYAM ||
5580 element == EL_DARK_YAMYAM)
5582 DrawLevelElementAnimationIfNeeded(x, y, element);
5583 PlayLevelSoundAction(x, y, ACTION_WAITING);
5585 else if (element == EL_SP_ELECTRON)
5586 DrawLevelElementAnimationIfNeeded(x, y, element);
5587 else if (element == EL_DRAGON)
5590 int dir = MovDir[x][y];
5591 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5592 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5593 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5594 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5595 dir == MV_UP ? IMG_FLAMES_1_UP :
5596 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5597 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5599 GfxAction[x][y] = ACTION_ATTACKING;
5601 if (IS_PLAYER(x, y))
5602 DrawPlayerField(x, y);
5604 DrawLevelField(x, y);
5606 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5608 for (i = 1; i <= 3; i++)
5610 int xx = x + i * dx;
5611 int yy = y + i * dy;
5612 int sx = SCREENX(xx);
5613 int sy = SCREENY(yy);
5614 int flame_graphic = graphic + (i - 1);
5616 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5621 int flamed = MovingOrBlocked2Element(xx, yy);
5625 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5627 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5628 RemoveMovingField(xx, yy);
5630 RemoveField(xx, yy);
5632 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5635 RemoveMovingField(xx, yy);
5638 ChangeDelay[xx][yy] = 0;
5640 Feld[xx][yy] = EL_FLAMES;
5642 if (IN_SCR_FIELD(sx, sy))
5644 DrawLevelFieldCrumbledSand(xx, yy);
5645 DrawGraphic(sx, sy, flame_graphic, frame);
5650 if (Feld[xx][yy] == EL_FLAMES)
5651 Feld[xx][yy] = EL_EMPTY;
5652 DrawLevelField(xx, yy);
5657 if (MovDelay[x][y]) /* element still has to wait some time */
5659 PlayLevelSoundAction(x, y, ACTION_WAITING);
5665 /* now make next step */
5667 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5669 if (DONT_COLLIDE_WITH(element) &&
5670 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5671 !PLAYER_ENEMY_PROTECTED(newx, newy))
5673 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5678 else if (CAN_MOVE_INTO_ACID(element) &&
5679 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5680 (MovDir[x][y] == MV_DOWN ||
5681 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5683 SplashAcid(newx, newy);
5684 Store[x][y] = EL_ACID;
5686 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5688 if (Feld[newx][newy] == EL_EXIT_OPEN)
5691 DrawLevelField(x, y);
5693 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5694 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5695 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5697 local_player->friends_still_needed--;
5698 if (!local_player->friends_still_needed &&
5699 !local_player->GameOver && AllPlayersGone)
5700 local_player->LevelSolved = local_player->GameOver = TRUE;
5704 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5706 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5707 DrawLevelField(newx, newy);
5709 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5711 else if (!IS_FREE(newx, newy))
5713 GfxAction[x][y] = ACTION_WAITING;
5715 if (IS_PLAYER(x, y))
5716 DrawPlayerField(x, y);
5718 DrawLevelField(x, y);
5723 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5725 if (IS_FOOD_PIG(Feld[newx][newy]))
5727 if (IS_MOVING(newx, newy))
5728 RemoveMovingField(newx, newy);
5731 Feld[newx][newy] = EL_EMPTY;
5732 DrawLevelField(newx, newy);
5735 PlayLevelSound(x, y, SND_PIG_DIGGING);
5737 else if (!IS_FREE(newx, newy))
5739 if (IS_PLAYER(x, y))
5740 DrawPlayerField(x, y);
5742 DrawLevelField(x, y);
5747 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5749 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL &&
5750 ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5752 int diagonal_move_dir = MovDir[x][y];
5753 int change_delay = 8;
5756 /* android is moving diagonally */
5758 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5760 GfxElement[x][y] = EL_EMC_ANDROID;
5761 GfxAction[x][y] = ACTION_SHRINKING;
5762 GfxDir[x][y] = diagonal_move_dir;
5763 ChangeDelay[x][y] = change_delay;
5765 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5768 DrawLevelGraphicAnimation(x, y, graphic);
5769 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5772 CheckTriggeredElementChangeBySide(x, y, element,
5773 CE_MOVE_OF_X, new_move_dir);
5775 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5777 TestIfElementHitsCustomElement(newx, newy, new_move_dir);
5778 TestIfElementTouchesCustomElement(newx, newy);
5781 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5783 Store[newx][newy] = EL_EMC_ANDROID;
5784 GfxElement[newx][newy] = EL_EMC_ANDROID;
5785 GfxAction[newx][newy] = ACTION_GROWING;
5786 GfxDir[newx][newy] = diagonal_move_dir;
5787 ChangeDelay[newx][newy] = change_delay;
5789 graphic = el_act_dir2img(GfxElement[newx][newy], GfxAction[newx][newy],
5790 GfxDir[newx][newy]);
5792 DrawLevelGraphicAnimation(newx, newy, graphic);
5793 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5797 else if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5799 Feld[newx][newy] = EL_EMPTY;
5800 DrawLevelField(newx, newy);
5802 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5804 else if (!IS_FREE(newx, newy))
5807 if (IS_PLAYER(x, y))
5808 DrawPlayerField(x, y);
5810 DrawLevelField(x, y);
5816 else if (IS_CUSTOM_ELEMENT(element) &&
5817 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5819 int new_element = Feld[newx][newy];
5821 if (!IS_FREE(newx, newy))
5823 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5824 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5827 /* no element can dig solid indestructible elements */
5828 if (IS_INDESTRUCTIBLE(new_element) &&
5829 !IS_DIGGABLE(new_element) &&
5830 !IS_COLLECTIBLE(new_element))
5833 if (AmoebaNr[newx][newy] &&
5834 (new_element == EL_AMOEBA_FULL ||
5835 new_element == EL_BD_AMOEBA ||
5836 new_element == EL_AMOEBA_GROWING))
5838 AmoebaCnt[AmoebaNr[newx][newy]]--;
5839 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5842 if (IS_MOVING(newx, newy))
5843 RemoveMovingField(newx, newy);
5846 RemoveField(newx, newy);
5847 DrawLevelField(newx, newy);
5850 /* if digged element was about to explode, prevent the explosion */
5851 ExplodeField[newx][newy] = EX_TYPE_NONE;
5853 PlayLevelSoundAction(x, y, action);
5856 Store[newx][newy] = EL_EMPTY;
5858 /* this makes it possible to leave the removed element again */
5859 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5860 Store[newx][newy] = new_element;
5862 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5864 int move_leave_element = element_info[element].move_leave_element;
5866 /* this makes it possible to leave the removed element again */
5867 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5868 new_element : move_leave_element);
5872 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5874 RunnerVisit[x][y] = FrameCounter;
5875 PlayerVisit[x][y] /= 8; /* expire player visit path */
5878 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5880 if (!IS_FREE(newx, newy))
5882 if (IS_PLAYER(x, y))
5883 DrawPlayerField(x, y);
5885 DrawLevelField(x, y);
5891 boolean wanna_flame = !RND(10);
5892 int dx = newx - x, dy = newy - y;
5893 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5894 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5895 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5896 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5897 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5898 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5901 IS_CLASSIC_ENEMY(element1) ||
5902 IS_CLASSIC_ENEMY(element2)) &&
5903 element1 != EL_DRAGON && element2 != EL_DRAGON &&
5904 element1 != EL_FLAMES && element2 != EL_FLAMES)
5906 ResetGfxAnimation(x, y);
5907 GfxAction[x][y] = ACTION_ATTACKING;
5909 if (IS_PLAYER(x, y))
5910 DrawPlayerField(x, y);
5912 DrawLevelField(x, y);
5914 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5916 MovDelay[x][y] = 50;
5920 RemoveField(newx, newy);
5922 Feld[newx][newy] = EL_FLAMES;
5923 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5926 RemoveField(newx1, newy1);
5928 Feld[newx1][newy1] = EL_FLAMES;
5930 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5933 RemoveField(newx2, newy2);
5935 Feld[newx2][newy2] = EL_FLAMES;
5942 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5943 Feld[newx][newy] == EL_DIAMOND)
5945 if (IS_MOVING(newx, newy))
5946 RemoveMovingField(newx, newy);
5949 Feld[newx][newy] = EL_EMPTY;
5950 DrawLevelField(newx, newy);
5953 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5955 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5956 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5958 if (AmoebaNr[newx][newy])
5960 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5961 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5962 Feld[newx][newy] == EL_BD_AMOEBA)
5963 AmoebaCnt[AmoebaNr[newx][newy]]--;
5968 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5970 RemoveMovingField(newx, newy);
5973 if (IS_MOVING(newx, newy))
5975 RemoveMovingField(newx, newy);
5980 Feld[newx][newy] = EL_EMPTY;
5981 DrawLevelField(newx, newy);
5984 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5986 else if ((element == EL_PACMAN || element == EL_MOLE)
5987 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5989 if (AmoebaNr[newx][newy])
5991 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5992 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5993 Feld[newx][newy] == EL_BD_AMOEBA)
5994 AmoebaCnt[AmoebaNr[newx][newy]]--;
5997 if (element == EL_MOLE)
5999 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6000 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6002 ResetGfxAnimation(x, y);
6003 GfxAction[x][y] = ACTION_DIGGING;
6004 DrawLevelField(x, y);
6006 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6008 return; /* wait for shrinking amoeba */
6010 else /* element == EL_PACMAN */
6012 Feld[newx][newy] = EL_EMPTY;
6013 DrawLevelField(newx, newy);
6014 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6017 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6018 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6019 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6021 /* wait for shrinking amoeba to completely disappear */
6024 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6026 /* object was running against a wall */
6031 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6032 if (move_pattern & MV_ANY_DIRECTION &&
6033 move_pattern == MovDir[x][y])
6035 int blocking_element =
6036 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6038 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6041 element = Feld[x][y]; /* element might have changed */
6045 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6046 DrawLevelElementAnimation(x, y, element);
6048 if (DONT_TOUCH(element))
6049 TestIfBadThingTouchesPlayer(x, y);
6054 InitMovingField(x, y, MovDir[x][y]);
6056 PlayLevelSoundAction(x, y, ACTION_MOVING);
6060 ContinueMoving(x, y);
6063 void ContinueMoving(int x, int y)
6065 int element = Feld[x][y];
6066 struct ElementInfo *ei = &element_info[element];
6067 int direction = MovDir[x][y];
6068 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6069 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6070 int newx = x + dx, newy = y + dy;
6071 int stored = Store[x][y];
6072 int stored_new = Store[newx][newy];
6073 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6074 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6075 boolean last_line = (newy == lev_fieldy - 1);
6077 MovPos[x][y] += getElementMoveStepsize(x, y);
6079 if (pushed_by_player) /* special case: moving object pushed by player */
6080 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6082 if (ABS(MovPos[x][y]) < TILEX)
6084 DrawLevelField(x, y);
6086 return; /* element is still moving */
6089 /* element reached destination field */
6091 Feld[x][y] = EL_EMPTY;
6092 Feld[newx][newy] = element;
6093 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6095 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6097 element = Feld[newx][newy] = EL_ACID;
6099 else if (element == EL_MOLE)
6101 Feld[x][y] = EL_SAND;
6103 DrawLevelFieldCrumbledSandNeighbours(x, y);
6105 else if (element == EL_QUICKSAND_FILLING)
6107 element = Feld[newx][newy] = get_next_element(element);
6108 Store[newx][newy] = Store[x][y];
6110 else if (element == EL_QUICKSAND_EMPTYING)
6112 Feld[x][y] = get_next_element(element);
6113 element = Feld[newx][newy] = Store[x][y];
6115 else if (element == EL_MAGIC_WALL_FILLING)
6117 element = Feld[newx][newy] = get_next_element(element);
6118 if (!game.magic_wall_active)
6119 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6120 Store[newx][newy] = Store[x][y];
6122 else if (element == EL_MAGIC_WALL_EMPTYING)
6124 Feld[x][y] = get_next_element(element);
6125 if (!game.magic_wall_active)
6126 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6127 element = Feld[newx][newy] = Store[x][y];
6129 #if USE_NEW_CUSTOM_VALUE
6130 InitField(newx, newy, FALSE);
6133 else if (element == EL_BD_MAGIC_WALL_FILLING)
6135 element = Feld[newx][newy] = get_next_element(element);
6136 if (!game.magic_wall_active)
6137 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6138 Store[newx][newy] = Store[x][y];
6140 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6142 Feld[x][y] = get_next_element(element);
6143 if (!game.magic_wall_active)
6144 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6145 element = Feld[newx][newy] = Store[x][y];
6147 #if USE_NEW_CUSTOM_VALUE
6148 InitField(newx, newy, FALSE);
6151 else if (element == EL_AMOEBA_DROPPING)
6153 Feld[x][y] = get_next_element(element);
6154 element = Feld[newx][newy] = Store[x][y];
6156 else if (element == EL_SOKOBAN_OBJECT)
6159 Feld[x][y] = Back[x][y];
6161 if (Back[newx][newy])
6162 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6164 Back[x][y] = Back[newx][newy] = 0;
6167 Store[x][y] = EL_EMPTY;
6172 MovDelay[newx][newy] = 0;
6175 if (CAN_CHANGE_OR_HAS_ACTION(element))
6177 if (CAN_CHANGE(element))
6180 /* copy element change control values to new field */
6181 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6182 ChangePage[newx][newy] = ChangePage[x][y];
6183 ChangeCount[newx][newy] = ChangeCount[x][y];
6184 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6187 #if USE_NEW_CUSTOM_VALUE
6188 CustomValue[newx][newy] = CustomValue[x][y];
6194 #if USE_NEW_CUSTOM_VALUE
6195 CustomValue[newx][newy] = CustomValue[x][y];
6199 ChangeDelay[x][y] = 0;
6200 ChangePage[x][y] = -1;
6201 ChangeCount[x][y] = 0;
6202 ChangeEvent[x][y] = -1;
6204 #if USE_NEW_CUSTOM_VALUE
6205 CustomValue[x][y] = 0;
6208 /* copy animation control values to new field */
6209 GfxFrame[newx][newy] = GfxFrame[x][y];
6210 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6211 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6212 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6214 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6216 /* some elements can leave other elements behind after moving */
6217 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6218 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6219 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6221 int move_leave_element = ei->move_leave_element;
6224 /* this makes it possible to leave the removed element again */
6225 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6226 move_leave_element = stored;
6228 /* this makes it possible to leave the removed element again */
6229 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6230 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6231 move_leave_element = stored;
6234 Feld[x][y] = move_leave_element;
6236 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6237 MovDir[x][y] = direction;
6239 InitField(x, y, FALSE);
6241 if (GFX_CRUMBLED(Feld[x][y]))
6242 DrawLevelFieldCrumbledSandNeighbours(x, y);
6244 if (ELEM_IS_PLAYER(move_leave_element))
6245 RelocatePlayer(x, y, move_leave_element);
6248 /* do this after checking for left-behind element */
6249 ResetGfxAnimation(x, y); /* reset animation values for old field */
6251 if (!CAN_MOVE(element) ||
6252 (CAN_FALL(element) && direction == MV_DOWN &&
6253 (element == EL_SPRING ||
6254 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6255 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6256 GfxDir[x][y] = MovDir[newx][newy] = 0;
6258 DrawLevelField(x, y);
6259 DrawLevelField(newx, newy);
6261 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6263 /* prevent pushed element from moving on in pushed direction */
6264 if (pushed_by_player && CAN_MOVE(element) &&
6265 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6266 !(element_info[element].move_pattern & direction))
6267 TurnRound(newx, newy);
6269 /* prevent elements on conveyor belt from moving on in last direction */
6270 if (pushed_by_conveyor && CAN_FALL(element) &&
6271 direction & MV_HORIZONTAL)
6272 MovDir[newx][newy] = 0;
6274 if (!pushed_by_player)
6276 int nextx = newx + dx, nexty = newy + dy;
6277 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6279 WasJustMoving[newx][newy] = 3;
6281 if (CAN_FALL(element) && direction == MV_DOWN)
6282 WasJustFalling[newx][newy] = 3;
6284 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6285 CheckCollision[newx][newy] = 2;
6288 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6290 TestIfBadThingTouchesPlayer(newx, newy);
6291 TestIfBadThingTouchesFriend(newx, newy);
6293 if (!IS_CUSTOM_ELEMENT(element))
6294 TestIfBadThingTouchesOtherBadThing(newx, newy);
6296 else if (element == EL_PENGUIN)
6297 TestIfFriendTouchesBadThing(newx, newy);
6299 /* give the player one last chance (one more frame) to move away */
6300 if (CAN_FALL(element) && direction == MV_DOWN &&
6301 (last_line || (!IS_FREE(x, newy + 1) &&
6302 (!IS_PLAYER(x, newy + 1) ||
6303 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6306 if (pushed_by_player && !game.use_change_when_pushing_bug)
6308 int push_side = MV_DIR_OPPOSITE(direction);
6309 struct PlayerInfo *player = PLAYERINFO(x, y);
6311 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6312 player->index_bit, push_side);
6313 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6314 player->index_bit, push_side);
6317 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6318 MovDelay[newx][newy] = 1;
6320 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6322 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6324 TestIfElementHitsCustomElement(newx, newy, direction);
6325 TestIfPlayerTouchesCustomElement(newx, newy);
6326 TestIfElementTouchesCustomElement(newx, newy);
6329 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6330 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6331 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6332 MV_DIR_OPPOSITE(direction));
6336 int AmoebeNachbarNr(int ax, int ay)
6339 int element = Feld[ax][ay];
6341 static int xy[4][2] =
6349 for (i = 0; i < NUM_DIRECTIONS; i++)
6351 int x = ax + xy[i][0];
6352 int y = ay + xy[i][1];
6354 if (!IN_LEV_FIELD(x, y))
6357 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6358 group_nr = AmoebaNr[x][y];
6364 void AmoebenVereinigen(int ax, int ay)
6366 int i, x, y, xx, yy;
6367 int new_group_nr = AmoebaNr[ax][ay];
6368 static int xy[4][2] =
6376 if (new_group_nr == 0)
6379 for (i = 0; i < NUM_DIRECTIONS; i++)
6384 if (!IN_LEV_FIELD(x, y))
6387 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6388 Feld[x][y] == EL_BD_AMOEBA ||
6389 Feld[x][y] == EL_AMOEBA_DEAD) &&
6390 AmoebaNr[x][y] != new_group_nr)
6392 int old_group_nr = AmoebaNr[x][y];
6394 if (old_group_nr == 0)
6397 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6398 AmoebaCnt[old_group_nr] = 0;
6399 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6400 AmoebaCnt2[old_group_nr] = 0;
6403 SCAN_PLAYFIELD(xx, yy)
6405 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6408 if (AmoebaNr[xx][yy] == old_group_nr)
6409 AmoebaNr[xx][yy] = new_group_nr;
6415 void AmoebeUmwandeln(int ax, int ay)
6419 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6421 int group_nr = AmoebaNr[ax][ay];
6426 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6427 printf("AmoebeUmwandeln(): This should never happen!\n");
6433 SCAN_PLAYFIELD(x, y)
6435 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6438 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6441 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6445 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6446 SND_AMOEBA_TURNING_TO_GEM :
6447 SND_AMOEBA_TURNING_TO_ROCK));
6452 static int xy[4][2] =
6460 for (i = 0; i < NUM_DIRECTIONS; i++)
6465 if (!IN_LEV_FIELD(x, y))
6468 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6470 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6471 SND_AMOEBA_TURNING_TO_GEM :
6472 SND_AMOEBA_TURNING_TO_ROCK));
6479 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6482 int group_nr = AmoebaNr[ax][ay];
6483 boolean done = FALSE;
6488 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6489 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6495 SCAN_PLAYFIELD(x, y)
6497 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6500 if (AmoebaNr[x][y] == group_nr &&
6501 (Feld[x][y] == EL_AMOEBA_DEAD ||
6502 Feld[x][y] == EL_BD_AMOEBA ||
6503 Feld[x][y] == EL_AMOEBA_GROWING))
6506 Feld[x][y] = new_element;
6507 InitField(x, y, FALSE);
6508 DrawLevelField(x, y);
6514 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6515 SND_BD_AMOEBA_TURNING_TO_ROCK :
6516 SND_BD_AMOEBA_TURNING_TO_GEM));
6519 void AmoebeWaechst(int x, int y)
6521 static unsigned long sound_delay = 0;
6522 static unsigned long sound_delay_value = 0;
6524 if (!MovDelay[x][y]) /* start new growing cycle */
6528 if (DelayReached(&sound_delay, sound_delay_value))
6530 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6531 sound_delay_value = 30;
6535 if (MovDelay[x][y]) /* wait some time before growing bigger */
6538 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6540 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6541 6 - MovDelay[x][y]);
6543 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6546 if (!MovDelay[x][y])
6548 Feld[x][y] = Store[x][y];
6550 DrawLevelField(x, y);
6555 void AmoebaDisappearing(int x, int y)
6557 static unsigned long sound_delay = 0;
6558 static unsigned long sound_delay_value = 0;
6560 if (!MovDelay[x][y]) /* start new shrinking cycle */
6564 if (DelayReached(&sound_delay, sound_delay_value))
6565 sound_delay_value = 30;
6568 if (MovDelay[x][y]) /* wait some time before shrinking */
6571 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6573 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6574 6 - MovDelay[x][y]);
6576 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6579 if (!MovDelay[x][y])
6581 Feld[x][y] = EL_EMPTY;
6582 DrawLevelField(x, y);
6584 /* don't let mole enter this field in this cycle;
6585 (give priority to objects falling to this field from above) */
6591 void AmoebeAbleger(int ax, int ay)
6594 int element = Feld[ax][ay];
6595 int graphic = el2img(element);
6596 int newax = ax, neway = ay;
6597 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6598 static int xy[4][2] =
6606 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6608 Feld[ax][ay] = EL_AMOEBA_DEAD;
6609 DrawLevelField(ax, ay);
6613 if (IS_ANIMATED(graphic))
6614 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6616 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6617 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6619 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6622 if (MovDelay[ax][ay])
6626 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6629 int x = ax + xy[start][0];
6630 int y = ay + xy[start][1];
6632 if (!IN_LEV_FIELD(x, y))
6635 if (IS_FREE(x, y) ||
6636 CAN_GROW_INTO(Feld[x][y]) ||
6637 Feld[x][y] == EL_QUICKSAND_EMPTY)
6643 if (newax == ax && neway == ay)
6646 else /* normal or "filled" (BD style) amoeba */
6649 boolean waiting_for_player = FALSE;
6651 for (i = 0; i < NUM_DIRECTIONS; i++)
6653 int j = (start + i) % 4;
6654 int x = ax + xy[j][0];
6655 int y = ay + xy[j][1];
6657 if (!IN_LEV_FIELD(x, y))
6660 if (IS_FREE(x, y) ||
6661 CAN_GROW_INTO(Feld[x][y]) ||
6662 Feld[x][y] == EL_QUICKSAND_EMPTY)
6668 else if (IS_PLAYER(x, y))
6669 waiting_for_player = TRUE;
6672 if (newax == ax && neway == ay) /* amoeba cannot grow */
6674 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6676 Feld[ax][ay] = EL_AMOEBA_DEAD;
6677 DrawLevelField(ax, ay);
6678 AmoebaCnt[AmoebaNr[ax][ay]]--;
6680 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6682 if (element == EL_AMOEBA_FULL)
6683 AmoebeUmwandeln(ax, ay);
6684 else if (element == EL_BD_AMOEBA)
6685 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6690 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6692 /* amoeba gets larger by growing in some direction */
6694 int new_group_nr = AmoebaNr[ax][ay];
6697 if (new_group_nr == 0)
6699 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6700 printf("AmoebeAbleger(): This should never happen!\n");
6705 AmoebaNr[newax][neway] = new_group_nr;
6706 AmoebaCnt[new_group_nr]++;
6707 AmoebaCnt2[new_group_nr]++;
6709 /* if amoeba touches other amoeba(s) after growing, unify them */
6710 AmoebenVereinigen(newax, neway);
6712 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6714 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6720 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6721 (neway == lev_fieldy - 1 && newax != ax))
6723 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6724 Store[newax][neway] = element;
6726 else if (neway == ay || element == EL_EMC_DRIPPER)
6728 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6730 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6734 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6735 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6736 Store[ax][ay] = EL_AMOEBA_DROP;
6737 ContinueMoving(ax, ay);
6741 DrawLevelField(newax, neway);
6744 void Life(int ax, int ay)
6748 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6751 int element = Feld[ax][ay];
6752 int graphic = el2img(element);
6753 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6755 boolean changed = FALSE;
6757 if (IS_ANIMATED(graphic))
6758 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6763 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6764 MovDelay[ax][ay] = life_time;
6766 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6769 if (MovDelay[ax][ay])
6773 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6775 int xx = ax+x1, yy = ay+y1;
6778 if (!IN_LEV_FIELD(xx, yy))
6781 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6783 int x = xx+x2, y = yy+y2;
6785 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6788 if (((Feld[x][y] == element ||
6789 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6791 (IS_FREE(x, y) && Stop[x][y]))
6795 if (xx == ax && yy == ay) /* field in the middle */
6797 if (nachbarn < life_parameter[0] ||
6798 nachbarn > life_parameter[1])
6800 Feld[xx][yy] = EL_EMPTY;
6802 DrawLevelField(xx, yy);
6803 Stop[xx][yy] = TRUE;
6807 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6808 { /* free border field */
6809 if (nachbarn >= life_parameter[2] &&
6810 nachbarn <= life_parameter[3])
6812 Feld[xx][yy] = element;
6813 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6815 DrawLevelField(xx, yy);
6816 Stop[xx][yy] = TRUE;
6823 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6824 SND_GAME_OF_LIFE_GROWING);
6827 static void InitRobotWheel(int x, int y)
6829 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6832 static void RunRobotWheel(int x, int y)
6834 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6837 static void StopRobotWheel(int x, int y)
6839 if (ZX == x && ZY == y)
6843 static void InitTimegateWheel(int x, int y)
6845 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6848 static void RunTimegateWheel(int x, int y)
6850 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6853 static void InitMagicBallDelay(int x, int y)
6856 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
6858 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
6862 static void ActivateMagicBall(int bx, int by)
6866 if (level.ball_random)
6868 int pos_border = RND(8); /* select one of the eight border elements */
6869 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
6870 int xx = pos_content % 3;
6871 int yy = pos_content / 3;
6876 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6877 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6881 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
6883 int xx = x - bx + 1;
6884 int yy = y - by + 1;
6886 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6887 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6891 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
6894 static void InitDiagonalMovingElement(int x, int y)
6897 MovDelay[x][y] = level.android_move_time;
6901 void CheckExit(int x, int y)
6903 if (local_player->gems_still_needed > 0 ||
6904 local_player->sokobanfields_still_needed > 0 ||
6905 local_player->lights_still_needed > 0)
6907 int element = Feld[x][y];
6908 int graphic = el2img(element);
6910 if (IS_ANIMATED(graphic))
6911 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6916 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6919 Feld[x][y] = EL_EXIT_OPENING;
6921 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6924 void CheckExitSP(int x, int y)
6926 if (local_player->gems_still_needed > 0)
6928 int element = Feld[x][y];
6929 int graphic = el2img(element);
6931 if (IS_ANIMATED(graphic))
6932 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6937 if (AllPlayersGone) /* do not re-open exit door closed after last player */
6940 Feld[x][y] = EL_SP_EXIT_OPENING;
6942 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6945 static void CloseAllOpenTimegates()
6950 SCAN_PLAYFIELD(x, y)
6952 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6955 int element = Feld[x][y];
6957 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6959 Feld[x][y] = EL_TIMEGATE_CLOSING;
6961 PlayLevelSoundAction(x, y, ACTION_CLOSING);
6966 void EdelsteinFunkeln(int x, int y)
6968 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6971 if (Feld[x][y] == EL_BD_DIAMOND)
6974 if (MovDelay[x][y] == 0) /* next animation frame */
6975 MovDelay[x][y] = 11 * !SimpleRND(500);
6977 if (MovDelay[x][y] != 0) /* wait some time before next frame */
6981 if (setup.direct_draw && MovDelay[x][y])
6982 SetDrawtoField(DRAW_BUFFERED);
6984 DrawLevelElementAnimation(x, y, Feld[x][y]);
6986 if (MovDelay[x][y] != 0)
6988 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6989 10 - MovDelay[x][y]);
6991 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6993 if (setup.direct_draw)
6997 dest_x = FX + SCREENX(x) * TILEX;
6998 dest_y = FY + SCREENY(y) * TILEY;
7000 BlitBitmap(drawto_field, window,
7001 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7002 SetDrawtoField(DRAW_DIRECT);
7008 void MauerWaechst(int x, int y)
7012 if (!MovDelay[x][y]) /* next animation frame */
7013 MovDelay[x][y] = 3 * delay;
7015 if (MovDelay[x][y]) /* wait some time before next frame */
7019 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7021 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7022 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7024 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7027 if (!MovDelay[x][y])
7029 if (MovDir[x][y] == MV_LEFT)
7031 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7032 DrawLevelField(x - 1, y);
7034 else if (MovDir[x][y] == MV_RIGHT)
7036 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7037 DrawLevelField(x + 1, y);
7039 else if (MovDir[x][y] == MV_UP)
7041 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7042 DrawLevelField(x, y - 1);
7046 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7047 DrawLevelField(x, y + 1);
7050 Feld[x][y] = Store[x][y];
7052 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7053 DrawLevelField(x, y);
7058 void MauerAbleger(int ax, int ay)
7060 int element = Feld[ax][ay];
7061 int graphic = el2img(element);
7062 boolean oben_frei = FALSE, unten_frei = FALSE;
7063 boolean links_frei = FALSE, rechts_frei = FALSE;
7064 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7065 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7066 boolean new_wall = FALSE;
7068 if (IS_ANIMATED(graphic))
7069 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7071 if (!MovDelay[ax][ay]) /* start building new wall */
7072 MovDelay[ax][ay] = 6;
7074 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7077 if (MovDelay[ax][ay])
7081 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7083 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7085 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7087 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7090 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7091 element == EL_EXPANDABLE_WALL_ANY)
7095 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7096 Store[ax][ay-1] = element;
7097 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7098 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7099 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7100 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7105 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7106 Store[ax][ay+1] = element;
7107 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7108 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7109 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7110 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7115 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7116 element == EL_EXPANDABLE_WALL_ANY ||
7117 element == EL_EXPANDABLE_WALL)
7121 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7122 Store[ax-1][ay] = element;
7123 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7124 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7125 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7126 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7132 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7133 Store[ax+1][ay] = element;
7134 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7135 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7136 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7137 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7142 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7143 DrawLevelField(ax, ay);
7145 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7147 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7148 unten_massiv = TRUE;
7149 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7150 links_massiv = TRUE;
7151 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7152 rechts_massiv = TRUE;
7154 if (((oben_massiv && unten_massiv) ||
7155 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7156 element == EL_EXPANDABLE_WALL) &&
7157 ((links_massiv && rechts_massiv) ||
7158 element == EL_EXPANDABLE_WALL_VERTICAL))
7159 Feld[ax][ay] = EL_WALL;
7162 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7165 void CheckForDragon(int x, int y)
7168 boolean dragon_found = FALSE;
7169 static int xy[4][2] =
7177 for (i = 0; i < NUM_DIRECTIONS; i++)
7179 for (j = 0; j < 4; j++)
7181 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7183 if (IN_LEV_FIELD(xx, yy) &&
7184 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7186 if (Feld[xx][yy] == EL_DRAGON)
7187 dragon_found = TRUE;
7196 for (i = 0; i < NUM_DIRECTIONS; i++)
7198 for (j = 0; j < 3; j++)
7200 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7202 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7204 Feld[xx][yy] = EL_EMPTY;
7205 DrawLevelField(xx, yy);
7214 static void InitBuggyBase(int x, int y)
7216 int element = Feld[x][y];
7217 int activating_delay = FRAMES_PER_SECOND / 4;
7220 (element == EL_SP_BUGGY_BASE ?
7221 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7222 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7224 element == EL_SP_BUGGY_BASE_ACTIVE ?
7225 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7228 static void WarnBuggyBase(int x, int y)
7231 static int xy[4][2] =
7239 for (i = 0; i < NUM_DIRECTIONS; i++)
7241 int xx = x + xy[i][0], yy = y + xy[i][1];
7243 if (IS_PLAYER(xx, yy))
7245 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7252 static void InitTrap(int x, int y)
7254 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7257 static void ActivateTrap(int x, int y)
7259 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7262 static void ChangeActiveTrap(int x, int y)
7264 int graphic = IMG_TRAP_ACTIVE;
7266 /* if new animation frame was drawn, correct crumbled sand border */
7267 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7268 DrawLevelFieldCrumbledSand(x, y);
7271 static int getSpecialActionElement(int element, int number, int base_element)
7273 return (element != EL_EMPTY ? element :
7274 number != -1 ? base_element + number - 1 :
7278 static int getModifiedActionNumber(int value_old, int operator, int operand,
7279 int value_min, int value_max)
7281 int value_new = (operator == CA_MODE_SET ? operand :
7282 operator == CA_MODE_ADD ? value_old + operand :
7283 operator == CA_MODE_SUBTRACT ? value_old - operand :
7284 operator == CA_MODE_MULTIPLY ? value_old * operand :
7285 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7286 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7289 return (value_new < value_min ? value_min :
7290 value_new > value_max ? value_max :
7294 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7296 struct ElementInfo *ei = &element_info[element];
7297 struct ElementChangeInfo *change = &ei->change_page[page];
7298 int action_type = change->action_type;
7299 int action_mode = change->action_mode;
7300 int action_arg = change->action_arg;
7303 if (!change->has_action)
7306 /* ---------- determine action paramater values -------------------------- */
7308 int level_time_value =
7309 (level.time > 0 ? TimeLeft :
7312 int action_arg_element =
7313 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7314 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7315 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7318 int action_arg_direction =
7319 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7320 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7321 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7322 change->actual_trigger_side :
7323 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7324 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7327 int action_arg_number_min =
7328 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7331 int action_arg_number_max =
7332 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7333 action_type == CA_SET_LEVEL_GEMS ? 999 :
7334 action_type == CA_SET_LEVEL_TIME ? 9999 :
7335 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7336 action_type == CA_SET_CE_SCORE ? 9999 :
7337 action_type == CA_SET_CE_VALUE ? 9999 :
7340 int action_arg_number_reset =
7341 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7342 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7343 action_type == CA_SET_LEVEL_TIME ? level.time :
7344 action_type == CA_SET_LEVEL_SCORE ? 0 :
7345 action_type == CA_SET_CE_SCORE ? 0 :
7347 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7349 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7353 int action_arg_number =
7354 (action_arg <= CA_ARG_MAX ? action_arg :
7355 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7356 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7357 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7358 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7359 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7360 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7361 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7362 #if USE_NEW_CUSTOM_VALUE
7363 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7365 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7367 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7368 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7369 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7370 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7371 action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7372 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
7375 int action_arg_number_old =
7376 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7377 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7378 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7379 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7380 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7383 int action_arg_number_new =
7384 getModifiedActionNumber(action_arg_number_old,
7385 action_mode, action_arg_number,
7386 action_arg_number_min, action_arg_number_max);
7388 int trigger_player_bits =
7389 (change->actual_trigger_player >= EL_PLAYER_1 &&
7390 change->actual_trigger_player <= EL_PLAYER_4 ?
7391 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7394 int action_arg_player_bits =
7395 (action_arg >= CA_ARG_PLAYER_1 &&
7396 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7397 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7400 /* ---------- execute action -------------------------------------------- */
7409 /* ---------- level actions ------------------------------------------- */
7411 case CA_RESTART_LEVEL:
7413 game.restart_level = TRUE;
7418 case CA_SHOW_ENVELOPE:
7420 int element = getSpecialActionElement(action_arg_element,
7421 action_arg_number, EL_ENVELOPE_1);
7423 if (IS_ENVELOPE(element))
7424 local_player->show_envelope = element;
7429 case CA_SET_LEVEL_TIME:
7431 if (level.time > 0) /* only modify limited time value */
7433 TimeLeft = action_arg_number_new;
7435 DrawGameValue_Time(TimeLeft);
7437 if (!TimeLeft && setup.time_limit)
7438 for (i = 0; i < MAX_PLAYERS; i++)
7439 KillPlayer(&stored_player[i]);
7445 case CA_SET_LEVEL_SCORE:
7447 local_player->score = action_arg_number_new;
7449 DrawGameValue_Score(local_player->score);
7454 case CA_SET_LEVEL_GEMS:
7456 local_player->gems_still_needed = action_arg_number_new;
7458 DrawGameValue_Emeralds(local_player->gems_still_needed);
7463 case CA_SET_LEVEL_GRAVITY:
7465 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7466 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7467 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7472 case CA_SET_LEVEL_WIND:
7474 game.wind_direction = action_arg_direction;
7479 /* ---------- player actions ------------------------------------------ */
7481 case CA_MOVE_PLAYER:
7483 /* automatically move to the next field in specified direction */
7484 for (i = 0; i < MAX_PLAYERS; i++)
7485 if (trigger_player_bits & (1 << i))
7486 stored_player[i].programmed_action = action_arg_direction;
7491 case CA_EXIT_PLAYER:
7493 for (i = 0; i < MAX_PLAYERS; i++)
7494 if (action_arg_player_bits & (1 << i))
7495 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7500 case CA_KILL_PLAYER:
7502 for (i = 0; i < MAX_PLAYERS; i++)
7503 if (action_arg_player_bits & (1 << i))
7504 KillPlayer(&stored_player[i]);
7509 case CA_SET_PLAYER_KEYS:
7511 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7512 int element = getSpecialActionElement(action_arg_element,
7513 action_arg_number, EL_KEY_1);
7515 if (IS_KEY(element))
7517 for (i = 0; i < MAX_PLAYERS; i++)
7519 if (trigger_player_bits & (1 << i))
7521 stored_player[i].key[KEY_NR(element)] = key_state;
7523 DrawGameValue_Keys(stored_player[i].key);
7525 redraw_mask |= REDRAW_DOOR_1;
7533 case CA_SET_PLAYER_SPEED:
7535 for (i = 0; i < MAX_PLAYERS; i++)
7537 if (trigger_player_bits & (1 << i))
7539 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7541 if (action_arg == CA_ARG_SPEED_FASTER &&
7542 stored_player[i].cannot_move)
7544 action_arg_number = STEPSIZE_VERY_SLOW;
7546 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7547 action_arg == CA_ARG_SPEED_FASTER)
7549 action_arg_number = 2;
7550 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7555 getModifiedActionNumber(move_stepsize,
7558 action_arg_number_min,
7559 action_arg_number_max);
7562 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7564 /* make sure that value is power of 2 */
7565 move_stepsize = (1 << log_2(move_stepsize));
7567 /* do no immediately change -- the player might just be moving */
7568 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7570 stored_player[i].cannot_move =
7571 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7579 case CA_SET_PLAYER_SHIELD:
7581 for (i = 0; i < MAX_PLAYERS; i++)
7583 if (trigger_player_bits & (1 << i))
7585 if (action_arg == CA_ARG_SHIELD_OFF)
7587 stored_player[i].shield_normal_time_left = 0;
7588 stored_player[i].shield_deadly_time_left = 0;
7590 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7592 stored_player[i].shield_normal_time_left = 999999;
7594 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7596 stored_player[i].shield_normal_time_left = 999999;
7597 stored_player[i].shield_deadly_time_left = 999999;
7605 case CA_SET_PLAYER_ARTWORK:
7607 for (i = 0; i < MAX_PLAYERS; i++)
7609 if (trigger_player_bits & (1 << i))
7611 int artwork_element = action_arg_element;
7613 if (action_arg == CA_ARG_ELEMENT_RESET)
7615 (level.use_artwork_element[i] ? level.artwork_element[i] :
7616 stored_player[i].element_nr);
7618 stored_player[i].artwork_element = artwork_element;
7620 SetPlayerWaiting(&stored_player[i], FALSE);
7622 /* set number of special actions for bored and sleeping animation */
7623 stored_player[i].num_special_action_bored =
7624 get_num_special_action(artwork_element,
7625 ACTION_BORING_1, ACTION_BORING_LAST);
7626 stored_player[i].num_special_action_sleeping =
7627 get_num_special_action(artwork_element,
7628 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7635 /* ---------- CE actions ---------------------------------------------- */
7637 case CA_SET_CE_SCORE:
7639 ei->collect_score = action_arg_number_new;
7644 case CA_SET_CE_VALUE:
7646 #if USE_NEW_CUSTOM_VALUE
7647 int last_custom_value = CustomValue[x][y];
7649 CustomValue[x][y] = action_arg_number_new;
7652 printf("::: Count == %d\n", CustomValue[x][y]);
7655 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7658 printf("::: CE_VALUE_GETS_ZERO\n");
7661 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7662 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7669 /* ---------- engine actions ------------------------------------------ */
7671 case CA_SET_ENGINE_SCAN_MODE:
7673 InitPlayfieldScanMode(action_arg);
7683 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7685 int previous_move_direction = MovDir[x][y];
7686 #if USE_NEW_CUSTOM_VALUE
7687 int last_ce_value = CustomValue[x][y];
7689 boolean add_player = (ELEM_IS_PLAYER(element) &&
7690 IS_WALKABLE(Feld[x][y]));
7692 /* check if element under player changes from accessible to unaccessible
7693 (needed for special case of dropping element which then changes) */
7694 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7695 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7704 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7705 RemoveMovingField(x, y);
7709 Feld[x][y] = element;
7711 ResetGfxAnimation(x, y);
7712 ResetRandomAnimationValue(x, y);
7714 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7715 MovDir[x][y] = previous_move_direction;
7717 #if USE_NEW_CUSTOM_VALUE
7718 if (element_info[Feld[x][y]].use_last_ce_value)
7719 CustomValue[x][y] = last_ce_value;
7722 InitField_WithBug1(x, y, FALSE);
7724 DrawLevelField(x, y);
7726 if (GFX_CRUMBLED(Feld[x][y]))
7727 DrawLevelFieldCrumbledSandNeighbours(x, y);
7730 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7731 if (ELEM_IS_PLAYER(element))
7732 RelocatePlayer(x, y, element);
7735 ChangeCount[x][y]++; /* count number of changes in the same frame */
7737 TestIfBadThingTouchesPlayer(x, y);
7738 TestIfPlayerTouchesCustomElement(x, y);
7739 TestIfElementTouchesCustomElement(x, y);
7742 static void CreateField(int x, int y, int element)
7744 CreateFieldExt(x, y, element, FALSE);
7747 static void CreateElementFromChange(int x, int y, int element)
7749 CreateFieldExt(x, y, element, TRUE);
7752 static boolean ChangeElement(int x, int y, int element, int page)
7754 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7756 int old_element = Feld[x][y];
7758 /* always use default change event to prevent running into a loop */
7759 if (ChangeEvent[x][y] == -1)
7760 ChangeEvent[x][y] = CE_DELAY;
7762 if (ChangeEvent[x][y] == CE_DELAY)
7764 /* reset actual trigger element, trigger player and action element */
7765 change->actual_trigger_element = EL_EMPTY;
7766 change->actual_trigger_player = EL_PLAYER_1;
7767 change->actual_trigger_side = CH_SIDE_NONE;
7768 change->actual_trigger_ce_value = 0;
7771 /* do not change elements more than a specified maximum number of changes */
7772 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7775 ChangeCount[x][y]++; /* count number of changes in the same frame */
7777 if (change->explode)
7784 if (change->use_target_content)
7786 boolean complete_replace = TRUE;
7787 boolean can_replace[3][3];
7790 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7793 boolean is_walkable;
7794 boolean is_diggable;
7795 boolean is_collectible;
7796 boolean is_removable;
7797 boolean is_destructible;
7798 int ex = x + xx - 1;
7799 int ey = y + yy - 1;
7800 int content_element = change->target_content.e[xx][yy];
7803 can_replace[xx][yy] = TRUE;
7805 if (ex == x && ey == y) /* do not check changing element itself */
7808 if (content_element == EL_EMPTY_SPACE)
7810 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7815 if (!IN_LEV_FIELD(ex, ey))
7817 can_replace[xx][yy] = FALSE;
7818 complete_replace = FALSE;
7825 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7826 e = MovingOrBlocked2Element(ex, ey);
7828 is_empty = (IS_FREE(ex, ey) ||
7829 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7831 is_walkable = (is_empty || IS_WALKABLE(e));
7832 is_diggable = (is_empty || IS_DIGGABLE(e));
7833 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7834 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7835 is_removable = (is_diggable || is_collectible);
7837 can_replace[xx][yy] =
7838 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7839 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7840 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7841 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7842 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7843 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7844 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7846 if (!can_replace[xx][yy])
7847 complete_replace = FALSE;
7850 if (!change->only_if_complete || complete_replace)
7852 boolean something_has_changed = FALSE;
7854 if (change->only_if_complete && change->use_random_replace &&
7855 RND(100) < change->random_percentage)
7858 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7860 int ex = x + xx - 1;
7861 int ey = y + yy - 1;
7862 int content_element;
7864 if (can_replace[xx][yy] && (!change->use_random_replace ||
7865 RND(100) < change->random_percentage))
7867 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7868 RemoveMovingField(ex, ey);
7870 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7872 content_element = change->target_content.e[xx][yy];
7873 target_element = GET_TARGET_ELEMENT(content_element, change);
7875 CreateElementFromChange(ex, ey, target_element);
7877 something_has_changed = TRUE;
7879 /* for symmetry reasons, freeze newly created border elements */
7880 if (ex != x || ey != y)
7881 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7885 if (something_has_changed)
7887 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7888 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7894 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7896 if (element == EL_DIAGONAL_GROWING)
7897 target_element = Store[x][y];
7899 CreateElementFromChange(x, y, target_element);
7901 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7902 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7905 /* this uses direct change before indirect change */
7906 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7911 #if USE_NEW_DELAYED_ACTION
7913 static void HandleElementChange(int x, int y, int page)
7915 int element = MovingOrBlocked2Element(x, y);
7916 struct ElementInfo *ei = &element_info[element];
7917 struct ElementChangeInfo *change = &ei->change_page[page];
7920 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7921 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7924 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7925 x, y, element, element_info[element].token_name);
7926 printf("HandleElementChange(): This should never happen!\n");
7931 /* this can happen with classic bombs on walkable, changing elements */
7932 if (!CAN_CHANGE_OR_HAS_ACTION(element))
7935 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7936 ChangeDelay[x][y] = 0;
7942 if (ChangeDelay[x][y] == 0) /* initialize element change */
7944 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7946 if (change->can_change)
7948 ResetGfxAnimation(x, y);
7949 ResetRandomAnimationValue(x, y);
7951 if (change->pre_change_function)
7952 change->pre_change_function(x, y);
7956 ChangeDelay[x][y]--;
7958 if (ChangeDelay[x][y] != 0) /* continue element change */
7960 if (change->can_change)
7962 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7964 if (IS_ANIMATED(graphic))
7965 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7967 if (change->change_function)
7968 change->change_function(x, y);
7971 else /* finish element change */
7973 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7975 page = ChangePage[x][y];
7976 ChangePage[x][y] = -1;
7978 change = &ei->change_page[page];
7981 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7983 ChangeDelay[x][y] = 1; /* try change after next move step */
7984 ChangePage[x][y] = page; /* remember page to use for change */
7989 if (change->can_change)
7991 if (ChangeElement(x, y, element, page))
7993 if (change->post_change_function)
7994 change->post_change_function(x, y);
7998 if (change->has_action)
7999 ExecuteCustomElementAction(x, y, element, page);
8005 static void HandleElementChange(int x, int y, int page)
8007 int element = MovingOrBlocked2Element(x, y);
8008 struct ElementInfo *ei = &element_info[element];
8009 struct ElementChangeInfo *change = &ei->change_page[page];
8012 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8015 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8016 x, y, element, element_info[element].token_name);
8017 printf("HandleElementChange(): This should never happen!\n");
8022 /* this can happen with classic bombs on walkable, changing elements */
8023 if (!CAN_CHANGE(element))
8026 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8027 ChangeDelay[x][y] = 0;
8033 if (ChangeDelay[x][y] == 0) /* initialize element change */
8035 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8037 ResetGfxAnimation(x, y);
8038 ResetRandomAnimationValue(x, y);
8040 if (change->pre_change_function)
8041 change->pre_change_function(x, y);
8044 ChangeDelay[x][y]--;
8046 if (ChangeDelay[x][y] != 0) /* continue element change */
8048 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8050 if (IS_ANIMATED(graphic))
8051 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8053 if (change->change_function)
8054 change->change_function(x, y);
8056 else /* finish element change */
8058 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8060 page = ChangePage[x][y];
8061 ChangePage[x][y] = -1;
8063 change = &ei->change_page[page];
8066 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8068 ChangeDelay[x][y] = 1; /* try change after next move step */
8069 ChangePage[x][y] = page; /* remember page to use for change */
8074 if (ChangeElement(x, y, element, page))
8076 if (change->post_change_function)
8077 change->post_change_function(x, y);
8084 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8085 int trigger_element,
8091 boolean change_done_any = FALSE;
8092 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8095 if (!(trigger_events[trigger_element][trigger_event]))
8098 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8100 int element = EL_CUSTOM_START + i;
8101 boolean change_done = FALSE;
8104 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8105 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8108 for (p = 0; p < element_info[element].num_change_pages; p++)
8110 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8112 if (change->can_change_or_has_action &&
8113 change->has_event[trigger_event] &&
8114 change->trigger_side & trigger_side &&
8115 change->trigger_player & trigger_player &&
8116 change->trigger_page & trigger_page_bits &&
8117 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8119 change->actual_trigger_element = trigger_element;
8120 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8121 change->actual_trigger_side = trigger_side;
8122 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8124 if ((change->can_change && !change_done) || change->has_action)
8129 SCAN_PLAYFIELD(x, y)
8131 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8134 if (Feld[x][y] == element)
8136 if (change->can_change && !change_done)
8138 ChangeDelay[x][y] = 1;
8139 ChangeEvent[x][y] = trigger_event;
8141 HandleElementChange(x, y, p);
8143 #if USE_NEW_DELAYED_ACTION
8144 else if (change->has_action)
8146 ExecuteCustomElementAction(x, y, element, p);
8147 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8150 if (change->has_action)
8152 ExecuteCustomElementAction(x, y, element, p);
8153 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8159 if (change->can_change)
8162 change_done_any = TRUE;
8169 return change_done_any;
8172 static boolean CheckElementChangeExt(int x, int y,
8174 int trigger_element,
8179 boolean change_done = FALSE;
8182 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8183 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8186 if (Feld[x][y] == EL_BLOCKED)
8188 Blocked2Moving(x, y, &x, &y);
8189 element = Feld[x][y];
8192 if (Feld[x][y] != element) /* check if element has already changed */
8195 for (p = 0; p < element_info[element].num_change_pages; p++)
8197 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8199 boolean check_trigger_element =
8200 (trigger_event == CE_TOUCHING_X ||
8201 trigger_event == CE_HITTING_X ||
8202 trigger_event == CE_HIT_BY_X);
8204 if (change->can_change_or_has_action &&
8205 change->has_event[trigger_event] &&
8206 change->trigger_side & trigger_side &&
8207 change->trigger_player & trigger_player &&
8208 (!check_trigger_element ||
8209 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8211 change->actual_trigger_element = trigger_element;
8212 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8213 change->actual_trigger_side = trigger_side;
8214 change->actual_trigger_ce_value = CustomValue[x][y];
8216 /* special case: trigger element not at (x,y) position for some events */
8217 if (check_trigger_element)
8229 { 0, 0 }, { 0, 0 }, { 0, 0 },
8233 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8234 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8236 change->actual_trigger_ce_value = CustomValue[xx][yy];
8239 if (change->can_change && !change_done)
8241 ChangeDelay[x][y] = 1;
8242 ChangeEvent[x][y] = trigger_event;
8244 HandleElementChange(x, y, p);
8248 #if USE_NEW_DELAYED_ACTION
8249 else if (change->has_action)
8251 ExecuteCustomElementAction(x, y, element, p);
8252 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8255 if (change->has_action)
8257 ExecuteCustomElementAction(x, y, element, p);
8258 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8267 static void PlayPlayerSound(struct PlayerInfo *player)
8269 int jx = player->jx, jy = player->jy;
8270 int sound_element = player->artwork_element;
8271 int last_action = player->last_action_waiting;
8272 int action = player->action_waiting;
8274 if (player->is_waiting)
8276 if (action != last_action)
8277 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8279 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8283 if (action != last_action)
8284 StopSound(element_info[sound_element].sound[last_action]);
8286 if (last_action == ACTION_SLEEPING)
8287 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8291 static void PlayAllPlayersSound()
8295 for (i = 0; i < MAX_PLAYERS; i++)
8296 if (stored_player[i].active)
8297 PlayPlayerSound(&stored_player[i]);
8300 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8302 boolean last_waiting = player->is_waiting;
8303 int move_dir = player->MovDir;
8305 player->last_action_waiting = player->action_waiting;
8309 if (!last_waiting) /* not waiting -> waiting */
8311 player->is_waiting = TRUE;
8313 player->frame_counter_bored =
8315 game.player_boring_delay_fixed +
8316 SimpleRND(game.player_boring_delay_random);
8317 player->frame_counter_sleeping =
8319 game.player_sleeping_delay_fixed +
8320 SimpleRND(game.player_sleeping_delay_random);
8322 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8325 if (game.player_sleeping_delay_fixed +
8326 game.player_sleeping_delay_random > 0 &&
8327 player->anim_delay_counter == 0 &&
8328 player->post_delay_counter == 0 &&
8329 FrameCounter >= player->frame_counter_sleeping)
8330 player->is_sleeping = TRUE;
8331 else if (game.player_boring_delay_fixed +
8332 game.player_boring_delay_random > 0 &&
8333 FrameCounter >= player->frame_counter_bored)
8334 player->is_bored = TRUE;
8336 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8337 player->is_bored ? ACTION_BORING :
8340 if (player->is_sleeping)
8342 if (player->num_special_action_sleeping > 0)
8344 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8346 int last_special_action = player->special_action_sleeping;
8347 int num_special_action = player->num_special_action_sleeping;
8348 int special_action =
8349 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8350 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8351 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8352 last_special_action + 1 : ACTION_SLEEPING);
8353 int special_graphic =
8354 el_act_dir2img(player->artwork_element, special_action, move_dir);
8356 player->anim_delay_counter =
8357 graphic_info[special_graphic].anim_delay_fixed +
8358 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8359 player->post_delay_counter =
8360 graphic_info[special_graphic].post_delay_fixed +
8361 SimpleRND(graphic_info[special_graphic].post_delay_random);
8363 player->special_action_sleeping = special_action;
8366 if (player->anim_delay_counter > 0)
8368 player->action_waiting = player->special_action_sleeping;
8369 player->anim_delay_counter--;
8371 else if (player->post_delay_counter > 0)
8373 player->post_delay_counter--;
8377 else if (player->is_bored)
8379 if (player->num_special_action_bored > 0)
8381 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8383 int special_action =
8384 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8385 int special_graphic =
8386 el_act_dir2img(player->artwork_element, special_action, move_dir);
8388 player->anim_delay_counter =
8389 graphic_info[special_graphic].anim_delay_fixed +
8390 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8391 player->post_delay_counter =
8392 graphic_info[special_graphic].post_delay_fixed +
8393 SimpleRND(graphic_info[special_graphic].post_delay_random);
8395 player->special_action_bored = special_action;
8398 if (player->anim_delay_counter > 0)
8400 player->action_waiting = player->special_action_bored;
8401 player->anim_delay_counter--;
8403 else if (player->post_delay_counter > 0)
8405 player->post_delay_counter--;
8410 else if (last_waiting) /* waiting -> not waiting */
8412 player->is_waiting = FALSE;
8413 player->is_bored = FALSE;
8414 player->is_sleeping = FALSE;
8416 player->frame_counter_bored = -1;
8417 player->frame_counter_sleeping = -1;
8419 player->anim_delay_counter = 0;
8420 player->post_delay_counter = 0;
8422 player->action_waiting = ACTION_DEFAULT;
8424 player->special_action_bored = ACTION_DEFAULT;
8425 player->special_action_sleeping = ACTION_DEFAULT;
8429 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8431 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8432 int left = player_action & JOY_LEFT;
8433 int right = player_action & JOY_RIGHT;
8434 int up = player_action & JOY_UP;
8435 int down = player_action & JOY_DOWN;
8436 int button1 = player_action & JOY_BUTTON_1;
8437 int button2 = player_action & JOY_BUTTON_2;
8438 int dx = (left ? -1 : right ? 1 : 0);
8439 int dy = (up ? -1 : down ? 1 : 0);
8441 if (!player->active || tape.pausing)
8447 snapped = SnapField(player, dx, dy);
8451 dropped = DropElement(player);
8453 moved = MovePlayer(player, dx, dy);
8456 if (tape.single_step && tape.recording && !tape.pausing)
8458 if (button1 || (dropped && !moved))
8460 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8461 SnapField(player, 0, 0); /* stop snapping */
8465 SetPlayerWaiting(player, FALSE);
8467 return player_action;
8471 /* no actions for this player (no input at player's configured device) */
8473 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8474 SnapField(player, 0, 0);
8475 CheckGravityMovementWhenNotMoving(player);
8477 if (player->MovPos == 0)
8478 SetPlayerWaiting(player, TRUE);
8480 if (player->MovPos == 0) /* needed for tape.playing */
8481 player->is_moving = FALSE;
8483 player->is_dropping = FALSE;
8489 void AdvanceFrameAndPlayerCounters(int player_nr)
8493 /* advance frame counters (global frame counter and time frame counter) */
8497 /* advance player counters (counters for move delay, move animation etc.) */
8498 for (i = 0; i < MAX_PLAYERS; i++)
8500 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8501 int move_delay_value = stored_player[i].move_delay_value;
8502 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8504 if (!advance_player_counters) /* not all players may be affected */
8507 #if USE_NEW_PLAYER_ANIM
8508 if (move_frames == 0) /* less than one move per game frame */
8510 int stepsize = TILEX / move_delay_value;
8511 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8512 int count = (stored_player[i].is_moving ?
8513 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8515 if (count % delay == 0)
8520 stored_player[i].Frame += move_frames;
8522 if (stored_player[i].MovPos != 0)
8523 stored_player[i].StepFrame += move_frames;
8525 if (stored_player[i].move_delay > 0)
8526 stored_player[i].move_delay--;
8528 /* due to bugs in previous versions, counter must count up, not down */
8529 if (stored_player[i].push_delay != -1)
8530 stored_player[i].push_delay++;
8532 if (stored_player[i].drop_delay > 0)
8533 stored_player[i].drop_delay--;
8539 static unsigned long game_frame_delay = 0;
8540 unsigned long game_frame_delay_value;
8541 int magic_wall_x = 0, magic_wall_y = 0;
8542 int i, x, y, element, graphic;
8543 byte *recorded_player_action;
8544 byte summarized_player_action = 0;
8545 byte tape_action[MAX_PLAYERS];
8547 if (game_status != GAME_MODE_PLAYING)
8550 game_frame_delay_value =
8551 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8553 if (tape.playing && tape.warp_forward && !tape.pausing)
8554 game_frame_delay_value = 0;
8556 /* ---------- main game synchronization point ---------- */
8558 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8560 InitPlayfieldScanModeVars();
8562 #if USE_ONE_MORE_CHANGE_PER_FRAME
8563 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8565 SCAN_PLAYFIELD(x, y)
8567 ChangeCount[x][y] = 0;
8568 ChangeEvent[x][y] = -1;
8573 if (network_playing && !network_player_action_received)
8575 /* try to get network player actions in time */
8577 #if defined(NETWORK_AVALIABLE)
8578 /* last chance to get network player actions without main loop delay */
8582 /* game was quit by network peer */
8583 if (game_status != GAME_MODE_PLAYING)
8586 if (!network_player_action_received)
8587 return; /* failed to get network player actions in time */
8593 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8596 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8597 if (recorded_player_action == NULL && tape.pausing)
8601 for (i = 0; i < MAX_PLAYERS; i++)
8603 summarized_player_action |= stored_player[i].action;
8605 if (!network_playing)
8606 stored_player[i].effective_action = stored_player[i].action;
8609 #if defined(NETWORK_AVALIABLE)
8610 if (network_playing)
8611 SendToServer_MovePlayer(summarized_player_action);
8614 if (!options.network && !setup.team_mode)
8615 local_player->effective_action = summarized_player_action;
8617 if (recorded_player_action != NULL)
8618 for (i = 0; i < MAX_PLAYERS; i++)
8619 stored_player[i].effective_action = recorded_player_action[i];
8621 for (i = 0; i < MAX_PLAYERS; i++)
8623 tape_action[i] = stored_player[i].effective_action;
8625 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8626 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8629 /* only save actions from input devices, but not programmed actions */
8631 TapeRecordAction(tape_action);
8633 for (i = 0; i < MAX_PLAYERS; i++)
8635 int actual_player_action = stored_player[i].effective_action;
8638 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8639 - rnd_equinox_tetrachloride 048
8640 - rnd_equinox_tetrachloride_ii 096
8641 - rnd_emanuel_schmieg 002
8642 - doctor_sloan_ww 001, 020
8644 if (stored_player[i].MovPos == 0)
8645 CheckGravityMovement(&stored_player[i]);
8648 /* overwrite programmed action with tape action */
8649 if (stored_player[i].programmed_action)
8650 actual_player_action = stored_player[i].programmed_action;
8653 PlayerActions(&stored_player[i], actual_player_action);
8655 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8657 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8658 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8661 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8664 network_player_action_received = FALSE;
8666 ScrollScreen(NULL, SCROLL_GO_ON);
8668 /* for backwards compatibility, the following code emulates a fixed bug that
8669 occured when pushing elements (causing elements that just made their last
8670 pushing step to already (if possible) make their first falling step in the
8671 same game frame, which is bad); this code is also needed to use the famous
8672 "spring push bug" which is used in older levels and might be wanted to be
8673 used also in newer levels, but in this case the buggy pushing code is only
8674 affecting the "spring" element and no other elements */
8676 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8678 for (i = 0; i < MAX_PLAYERS; i++)
8680 struct PlayerInfo *player = &stored_player[i];
8684 if (player->active && player->is_pushing && player->is_moving &&
8686 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8687 Feld[x][y] == EL_SPRING))
8689 ContinueMoving(x, y);
8691 /* continue moving after pushing (this is actually a bug) */
8692 if (!IS_MOVING(x, y))
8701 SCAN_PLAYFIELD(x, y)
8703 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8706 ChangeCount[x][y] = 0;
8707 ChangeEvent[x][y] = -1;
8709 /* this must be handled before main playfield loop */
8710 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8713 if (MovDelay[x][y] <= 0)
8717 #if USE_NEW_SNAP_DELAY
8718 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8721 if (MovDelay[x][y] <= 0)
8724 DrawLevelField(x, y);
8726 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8732 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8734 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8735 printf("GameActions(): This should never happen!\n");
8737 ChangePage[x][y] = -1;
8742 if (WasJustMoving[x][y] > 0)
8743 WasJustMoving[x][y]--;
8744 if (WasJustFalling[x][y] > 0)
8745 WasJustFalling[x][y]--;
8746 if (CheckCollision[x][y] > 0)
8747 CheckCollision[x][y]--;
8751 /* reset finished pushing action (not done in ContinueMoving() to allow
8752 continuous pushing animation for elements with zero push delay) */
8753 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8755 ResetGfxAnimation(x, y);
8756 DrawLevelField(x, y);
8760 if (IS_BLOCKED(x, y))
8764 Blocked2Moving(x, y, &oldx, &oldy);
8765 if (!IS_MOVING(oldx, oldy))
8767 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8768 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8769 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8770 printf("GameActions(): This should never happen!\n");
8777 SCAN_PLAYFIELD(x, y)
8779 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8782 element = Feld[x][y];
8783 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8786 printf("::: %d,%d\n", x, y);
8788 if (element == EL_ROCK)
8789 printf("::: Yo man! Rocks can fall!\n");
8792 if (graphic_info[graphic].anim_global_sync)
8793 GfxFrame[x][y] = FrameCounter;
8794 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8796 int old_gfx_frame = GfxFrame[x][y];
8798 GfxFrame[x][y] = CustomValue[x][y];
8801 if (GfxFrame[x][y] != old_gfx_frame)
8803 DrawLevelGraphicAnimation(x, y, graphic);
8805 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8807 int old_gfx_frame = GfxFrame[x][y];
8809 GfxFrame[x][y] = element_info[element].collect_score;
8812 if (GfxFrame[x][y] != old_gfx_frame)
8814 DrawLevelGraphicAnimation(x, y, graphic);
8817 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8818 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8819 ResetRandomAnimationValue(x, y);
8821 SetRandomAnimationValue(x, y);
8823 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8825 if (IS_INACTIVE(element))
8827 if (IS_ANIMATED(graphic))
8828 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8833 /* this may take place after moving, so 'element' may have changed */
8834 if (IS_CHANGING(x, y) &&
8835 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8837 int page = element_info[element].event_page_nr[CE_DELAY];
8839 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8843 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8847 if (element == EL_CUSTOM_255)
8848 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8852 HandleElementChange(x, y, page);
8854 if (CAN_CHANGE(element))
8855 HandleElementChange(x, y, page);
8857 if (HAS_ACTION(element))
8858 ExecuteCustomElementAction(x, y, element, page);
8863 element = Feld[x][y];
8864 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8867 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8871 element = Feld[x][y];
8872 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8874 if (IS_ANIMATED(graphic) &&
8877 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8879 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8880 EdelsteinFunkeln(x, y);
8882 else if ((element == EL_ACID ||
8883 element == EL_EXIT_OPEN ||
8884 element == EL_SP_EXIT_OPEN ||
8885 element == EL_SP_TERMINAL ||
8886 element == EL_SP_TERMINAL_ACTIVE ||
8887 element == EL_EXTRA_TIME ||
8888 element == EL_SHIELD_NORMAL ||
8889 element == EL_SHIELD_DEADLY) &&
8890 IS_ANIMATED(graphic))
8891 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8892 else if (IS_MOVING(x, y))
8893 ContinueMoving(x, y);
8894 else if (IS_ACTIVE_BOMB(element))
8895 CheckDynamite(x, y);
8896 else if (element == EL_AMOEBA_GROWING)
8897 AmoebeWaechst(x, y);
8898 else if (element == EL_AMOEBA_SHRINKING)
8899 AmoebaDisappearing(x, y);
8901 #if !USE_NEW_AMOEBA_CODE
8902 else if (IS_AMOEBALIVE(element))
8903 AmoebeAbleger(x, y);
8906 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8908 else if (element == EL_EXIT_CLOSED)
8910 else if (element == EL_SP_EXIT_CLOSED)
8912 else if (element == EL_EXPANDABLE_WALL_GROWING)
8914 else if (element == EL_EXPANDABLE_WALL ||
8915 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8916 element == EL_EXPANDABLE_WALL_VERTICAL ||
8917 element == EL_EXPANDABLE_WALL_ANY)
8919 else if (element == EL_FLAMES)
8920 CheckForDragon(x, y);
8921 else if (element == EL_EXPLOSION)
8922 ; /* drawing of correct explosion animation is handled separately */
8923 else if (element == EL_ELEMENT_SNAPPING ||
8924 element == EL_DIAGONAL_SHRINKING ||
8925 element == EL_DIAGONAL_GROWING)
8928 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8930 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8933 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8934 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8937 if (element == EL_CUSTOM_255 ||
8938 element == EL_CUSTOM_256)
8939 DrawLevelGraphicAnimation(x, y, graphic);
8942 if (IS_BELT_ACTIVE(element))
8943 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8945 if (game.magic_wall_active)
8947 int jx = local_player->jx, jy = local_player->jy;
8949 /* play the element sound at the position nearest to the player */
8950 if ((element == EL_MAGIC_WALL_FULL ||
8951 element == EL_MAGIC_WALL_ACTIVE ||
8952 element == EL_MAGIC_WALL_EMPTYING ||
8953 element == EL_BD_MAGIC_WALL_FULL ||
8954 element == EL_BD_MAGIC_WALL_ACTIVE ||
8955 element == EL_BD_MAGIC_WALL_EMPTYING) &&
8956 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8964 #if USE_NEW_AMOEBA_CODE
8965 /* new experimental amoeba growth stuff */
8966 if (!(FrameCounter % 8))
8968 static unsigned long random = 1684108901;
8970 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8972 x = RND(lev_fieldx);
8973 y = RND(lev_fieldy);
8974 element = Feld[x][y];
8976 if (!IS_PLAYER(x,y) &&
8977 (element == EL_EMPTY ||
8978 CAN_GROW_INTO(element) ||
8979 element == EL_QUICKSAND_EMPTY ||
8980 element == EL_ACID_SPLASH_LEFT ||
8981 element == EL_ACID_SPLASH_RIGHT))
8983 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8984 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8985 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8986 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8987 Feld[x][y] = EL_AMOEBA_DROP;
8990 random = random * 129 + 1;
8996 if (game.explosions_delayed)
8999 game.explosions_delayed = FALSE;
9002 SCAN_PLAYFIELD(x, y)
9004 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9007 element = Feld[x][y];
9009 if (ExplodeField[x][y])
9010 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9011 else if (element == EL_EXPLOSION)
9012 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9014 ExplodeField[x][y] = EX_TYPE_NONE;
9017 game.explosions_delayed = TRUE;
9020 if (game.magic_wall_active)
9022 if (!(game.magic_wall_time_left % 4))
9024 int element = Feld[magic_wall_x][magic_wall_y];
9026 if (element == EL_BD_MAGIC_WALL_FULL ||
9027 element == EL_BD_MAGIC_WALL_ACTIVE ||
9028 element == EL_BD_MAGIC_WALL_EMPTYING)
9029 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9031 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9034 if (game.magic_wall_time_left > 0)
9036 game.magic_wall_time_left--;
9037 if (!game.magic_wall_time_left)
9040 SCAN_PLAYFIELD(x, y)
9042 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9045 element = Feld[x][y];
9047 if (element == EL_MAGIC_WALL_ACTIVE ||
9048 element == EL_MAGIC_WALL_FULL)
9050 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9051 DrawLevelField(x, y);
9053 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9054 element == EL_BD_MAGIC_WALL_FULL)
9056 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9057 DrawLevelField(x, y);
9061 game.magic_wall_active = FALSE;
9066 if (game.light_time_left > 0)
9068 game.light_time_left--;
9070 if (game.light_time_left == 0)
9071 RedrawAllLightSwitchesAndInvisibleElements();
9074 if (game.timegate_time_left > 0)
9076 game.timegate_time_left--;
9078 if (game.timegate_time_left == 0)
9079 CloseAllOpenTimegates();
9082 if (game.lenses_time_left > 0)
9084 game.lenses_time_left--;
9086 if (game.lenses_time_left == 0)
9087 RedrawAllInvisibleElementsForLenses();
9090 if (game.magnify_time_left > 0)
9092 game.magnify_time_left--;
9094 if (game.magnify_time_left == 0)
9095 RedrawAllInvisibleElementsForMagnifier();
9098 for (i = 0; i < MAX_PLAYERS; i++)
9100 struct PlayerInfo *player = &stored_player[i];
9102 if (SHIELD_ON(player))
9104 if (player->shield_deadly_time_left)
9105 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9106 else if (player->shield_normal_time_left)
9107 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9111 if (TimeFrames >= FRAMES_PER_SECOND)
9116 for (i = 0; i < MAX_PLAYERS; i++)
9118 struct PlayerInfo *player = &stored_player[i];
9120 if (SHIELD_ON(player))
9122 player->shield_normal_time_left--;
9124 if (player->shield_deadly_time_left > 0)
9125 player->shield_deadly_time_left--;
9129 if (!level.use_step_counter)
9137 if (TimeLeft <= 10 && setup.time_limit)
9138 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9140 DrawGameValue_Time(TimeLeft);
9142 if (!TimeLeft && setup.time_limit)
9143 for (i = 0; i < MAX_PLAYERS; i++)
9144 KillPlayer(&stored_player[i]);
9146 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9147 DrawGameValue_Time(TimePlayed);
9150 if (tape.recording || tape.playing)
9151 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9155 PlayAllPlayersSound();
9157 if (options.debug) /* calculate frames per second */
9159 static unsigned long fps_counter = 0;
9160 static int fps_frames = 0;
9161 unsigned long fps_delay_ms = Counter() - fps_counter;
9165 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9167 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9170 fps_counter = Counter();
9173 redraw_mask |= REDRAW_FPS;
9176 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9178 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9180 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9182 local_player->show_envelope = 0;
9185 /* use random number generator in every frame to make it less predictable */
9186 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9190 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9192 int min_x = x, min_y = y, max_x = x, max_y = y;
9195 for (i = 0; i < MAX_PLAYERS; i++)
9197 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9199 if (!stored_player[i].active || &stored_player[i] == player)
9202 min_x = MIN(min_x, jx);
9203 min_y = MIN(min_y, jy);
9204 max_x = MAX(max_x, jx);
9205 max_y = MAX(max_y, jy);
9208 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9211 static boolean AllPlayersInVisibleScreen()
9215 for (i = 0; i < MAX_PLAYERS; i++)
9217 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9219 if (!stored_player[i].active)
9222 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9229 void ScrollLevel(int dx, int dy)
9231 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9234 BlitBitmap(drawto_field, drawto_field,
9235 FX + TILEX * (dx == -1) - softscroll_offset,
9236 FY + TILEY * (dy == -1) - softscroll_offset,
9237 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9238 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9239 FX + TILEX * (dx == 1) - softscroll_offset,
9240 FY + TILEY * (dy == 1) - softscroll_offset);
9244 x = (dx == 1 ? BX1 : BX2);
9245 for (y = BY1; y <= BY2; y++)
9246 DrawScreenField(x, y);
9251 y = (dy == 1 ? BY1 : BY2);
9252 for (x = BX1; x <= BX2; x++)
9253 DrawScreenField(x, y);
9256 redraw_mask |= REDRAW_FIELD;
9259 static boolean canFallDown(struct PlayerInfo *player)
9261 int jx = player->jx, jy = player->jy;
9263 return (IN_LEV_FIELD(jx, jy + 1) &&
9264 (IS_FREE(jx, jy + 1) ||
9265 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9266 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9267 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9270 static boolean canPassField(int x, int y, int move_dir)
9272 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9273 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9274 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9277 int element = Feld[x][y];
9279 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9280 !CAN_MOVE(element) &&
9281 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9282 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9283 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9286 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9288 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9289 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9290 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9294 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9295 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9296 (IS_DIGGABLE(Feld[newx][newy]) ||
9297 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9298 canPassField(newx, newy, move_dir)));
9301 static void CheckGravityMovement(struct PlayerInfo *player)
9303 if (game.gravity && !player->programmed_action)
9305 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9306 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9307 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9308 int jx = player->jx, jy = player->jy;
9309 boolean player_is_moving_to_valid_field =
9310 (!player_is_snapping &&
9311 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9312 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9313 boolean player_can_fall_down = canFallDown(player);
9315 if (player_can_fall_down &&
9316 !player_is_moving_to_valid_field)
9317 player->programmed_action = MV_DOWN;
9321 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9323 return CheckGravityMovement(player);
9325 if (game.gravity && !player->programmed_action)
9327 int jx = player->jx, jy = player->jy;
9328 boolean field_under_player_is_free =
9329 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9330 boolean player_is_standing_on_valid_field =
9331 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9332 (IS_WALKABLE(Feld[jx][jy]) &&
9333 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9335 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9336 player->programmed_action = MV_DOWN;
9342 -----------------------------------------------------------------------------
9343 dx, dy: direction (non-diagonal) to try to move the player to
9344 real_dx, real_dy: direction as read from input device (can be diagonal)
9347 boolean MovePlayerOneStep(struct PlayerInfo *player,
9348 int dx, int dy, int real_dx, int real_dy)
9350 int jx = player->jx, jy = player->jy;
9351 int new_jx = jx + dx, new_jy = jy + dy;
9352 #if !USE_FIXED_DONT_RUN_INTO
9356 boolean player_can_move = !player->cannot_move;
9358 if (!player->active || (!dx && !dy))
9359 return MP_NO_ACTION;
9361 player->MovDir = (dx < 0 ? MV_LEFT :
9364 dy > 0 ? MV_DOWN : MV_NONE);
9366 if (!IN_LEV_FIELD(new_jx, new_jy))
9367 return MP_NO_ACTION;
9369 if (!player_can_move)
9372 if (player->MovPos == 0)
9374 player->is_moving = FALSE;
9375 player->is_digging = FALSE;
9376 player->is_collecting = FALSE;
9377 player->is_snapping = FALSE;
9378 player->is_pushing = FALSE;
9381 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9382 SnapField(player, 0, 0);
9386 return MP_NO_ACTION;
9390 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9391 return MP_NO_ACTION;
9393 #if !USE_FIXED_DONT_RUN_INTO
9394 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9396 /* (moved to DigField()) */
9397 if (player_can_move && DONT_RUN_INTO(element))
9399 if (element == EL_ACID && dx == 0 && dy == 1)
9401 SplashAcid(new_jx, new_jy);
9402 Feld[jx][jy] = EL_PLAYER_1;
9403 InitMovingField(jx, jy, MV_DOWN);
9404 Store[jx][jy] = EL_ACID;
9405 ContinueMoving(jx, jy);
9409 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9415 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9417 #if USE_FIXED_DONT_RUN_INTO
9418 if (can_move == MP_DONT_RUN_INTO)
9422 if (can_move != MP_MOVING)
9425 #if USE_FIXED_DONT_RUN_INTO
9428 /* check if DigField() has caused relocation of the player */
9429 if (player->jx != jx || player->jy != jy)
9430 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9432 StorePlayer[jx][jy] = 0;
9433 player->last_jx = jx;
9434 player->last_jy = jy;
9435 player->jx = new_jx;
9436 player->jy = new_jy;
9437 StorePlayer[new_jx][new_jy] = player->element_nr;
9439 if (player->move_delay_value_next != -1)
9441 player->move_delay_value = player->move_delay_value_next;
9442 player->move_delay_value_next = -1;
9446 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9448 player->step_counter++;
9450 PlayerVisit[jx][jy] = FrameCounter;
9452 ScrollPlayer(player, SCROLL_INIT);
9457 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9459 int jx = player->jx, jy = player->jy;
9460 int old_jx = jx, old_jy = jy;
9461 int moved = MP_NO_ACTION;
9463 if (!player->active)
9468 if (player->MovPos == 0)
9470 player->is_moving = FALSE;
9471 player->is_digging = FALSE;
9472 player->is_collecting = FALSE;
9473 player->is_snapping = FALSE;
9474 player->is_pushing = FALSE;
9480 if (player->move_delay > 0)
9483 player->move_delay = -1; /* set to "uninitialized" value */
9485 /* store if player is automatically moved to next field */
9486 player->is_auto_moving = (player->programmed_action != MV_NONE);
9488 /* remove the last programmed player action */
9489 player->programmed_action = 0;
9493 /* should only happen if pre-1.2 tape recordings are played */
9494 /* this is only for backward compatibility */
9496 int original_move_delay_value = player->move_delay_value;
9499 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9503 /* scroll remaining steps with finest movement resolution */
9504 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9506 while (player->MovPos)
9508 ScrollPlayer(player, SCROLL_GO_ON);
9509 ScrollScreen(NULL, SCROLL_GO_ON);
9511 AdvanceFrameAndPlayerCounters(player->index_nr);
9517 player->move_delay_value = original_move_delay_value;
9520 if (player->last_move_dir & MV_HORIZONTAL)
9522 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9523 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9527 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9528 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9534 if (moved & MP_MOVING && !ScreenMovPos &&
9535 (player == local_player || !options.network))
9537 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9538 int offset = (setup.scroll_delay ? 3 : 0);
9540 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9542 /* actual player has left the screen -- scroll in that direction */
9543 if (jx != old_jx) /* player has moved horizontally */
9544 scroll_x += (jx - old_jx);
9545 else /* player has moved vertically */
9546 scroll_y += (jy - old_jy);
9550 if (jx != old_jx) /* player has moved horizontally */
9552 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9553 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9554 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9556 /* don't scroll over playfield boundaries */
9557 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9558 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9560 /* don't scroll more than one field at a time */
9561 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9563 /* don't scroll against the player's moving direction */
9564 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9565 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9566 scroll_x = old_scroll_x;
9568 else /* player has moved vertically */
9570 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9571 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9572 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9574 /* don't scroll over playfield boundaries */
9575 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9576 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9578 /* don't scroll more than one field at a time */
9579 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9581 /* don't scroll against the player's moving direction */
9582 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9583 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9584 scroll_y = old_scroll_y;
9588 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9590 if (!options.network && !AllPlayersInVisibleScreen())
9592 scroll_x = old_scroll_x;
9593 scroll_y = old_scroll_y;
9597 ScrollScreen(player, SCROLL_INIT);
9598 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9603 player->StepFrame = 0;
9605 if (moved & MP_MOVING)
9607 if (old_jx != jx && old_jy == jy)
9608 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9609 else if (old_jx == jx && old_jy != jy)
9610 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9612 DrawLevelField(jx, jy); /* for "crumbled sand" */
9614 player->last_move_dir = player->MovDir;
9615 player->is_moving = TRUE;
9616 player->is_snapping = FALSE;
9617 player->is_switching = FALSE;
9618 player->is_dropping = FALSE;
9622 CheckGravityMovementWhenNotMoving(player);
9624 player->is_moving = FALSE;
9626 /* at this point, the player is allowed to move, but cannot move right now
9627 (e.g. because of something blocking the way) -- ensure that the player
9628 is also allowed to move in the next frame (in old versions before 3.1.1,
9629 the player was forced to wait again for eight frames before next try) */
9631 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9632 player->move_delay = 0; /* allow direct movement in the next frame */
9635 if (player->move_delay == -1) /* not yet initialized by DigField() */
9636 player->move_delay = player->move_delay_value;
9638 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9640 TestIfPlayerTouchesBadThing(jx, jy);
9641 TestIfPlayerTouchesCustomElement(jx, jy);
9644 if (!player->active)
9645 RemovePlayer(player);
9650 void ScrollPlayer(struct PlayerInfo *player, int mode)
9652 int jx = player->jx, jy = player->jy;
9653 int last_jx = player->last_jx, last_jy = player->last_jy;
9654 int move_stepsize = TILEX / player->move_delay_value;
9656 #if USE_NEW_PLAYER_SPEED
9657 if (!player->active)
9660 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9663 if (!player->active || player->MovPos == 0)
9667 if (mode == SCROLL_INIT)
9669 player->actual_frame_counter = FrameCounter;
9670 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9672 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9673 Feld[last_jx][last_jy] == EL_EMPTY)
9675 int last_field_block_delay = 0; /* start with no blocking at all */
9676 int block_delay_adjustment = player->block_delay_adjustment;
9678 /* if player blocks last field, add delay for exactly one move */
9679 if (player->block_last_field)
9681 last_field_block_delay += player->move_delay_value;
9683 /* when blocking enabled, prevent moving up despite gravity */
9684 if (game.gravity && player->MovDir == MV_UP)
9685 block_delay_adjustment = -1;
9688 /* add block delay adjustment (also possible when not blocking) */
9689 last_field_block_delay += block_delay_adjustment;
9691 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9692 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9695 #if USE_NEW_PLAYER_SPEED
9696 if (player->MovPos != 0) /* player has not yet reached destination */
9702 else if (!FrameReached(&player->actual_frame_counter, 1))
9706 printf("::: player->MovPos: %d -> %d\n",
9708 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9711 #if USE_NEW_PLAYER_SPEED
9712 if (player->MovPos != 0)
9714 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9715 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9717 /* before DrawPlayer() to draw correct player graphic for this case */
9718 if (player->MovPos == 0)
9719 CheckGravityMovement(player);
9722 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9723 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9725 /* before DrawPlayer() to draw correct player graphic for this case */
9726 if (player->MovPos == 0)
9727 CheckGravityMovement(player);
9730 if (player->MovPos == 0) /* player reached destination field */
9733 printf("::: player reached destination field\n");
9736 if (player->move_delay_reset_counter > 0)
9738 player->move_delay_reset_counter--;
9740 if (player->move_delay_reset_counter == 0)
9742 /* continue with normal speed after quickly moving through gate */
9743 HALVE_PLAYER_SPEED(player);
9745 /* be able to make the next move without delay */
9746 player->move_delay = 0;
9750 player->last_jx = jx;
9751 player->last_jy = jy;
9753 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9754 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9755 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9757 DrawPlayer(player); /* needed here only to cleanup last field */
9758 RemovePlayer(player);
9760 if (local_player->friends_still_needed == 0 ||
9761 IS_SP_ELEMENT(Feld[jx][jy]))
9762 player->LevelSolved = player->GameOver = TRUE;
9765 /* this breaks one level: "machine", level 000 */
9767 int move_direction = player->MovDir;
9768 int enter_side = MV_DIR_OPPOSITE(move_direction);
9769 int leave_side = move_direction;
9770 int old_jx = last_jx;
9771 int old_jy = last_jy;
9772 int old_element = Feld[old_jx][old_jy];
9773 int new_element = Feld[jx][jy];
9775 if (IS_CUSTOM_ELEMENT(old_element))
9776 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9778 player->index_bit, leave_side);
9780 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9782 player->index_bit, leave_side);
9784 if (IS_CUSTOM_ELEMENT(new_element))
9785 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9786 player->index_bit, enter_side);
9788 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9790 player->index_bit, enter_side);
9792 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9793 CE_MOVE_OF_X, move_direction);
9796 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9798 TestIfPlayerTouchesBadThing(jx, jy);
9799 TestIfPlayerTouchesCustomElement(jx, jy);
9801 /* needed because pushed element has not yet reached its destination,
9802 so it would trigger a change event at its previous field location */
9803 if (!player->is_pushing)
9804 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9806 if (!player->active)
9807 RemovePlayer(player);
9810 if (level.use_step_counter)
9820 if (TimeLeft <= 10 && setup.time_limit)
9821 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9823 DrawGameValue_Time(TimeLeft);
9825 if (!TimeLeft && setup.time_limit)
9826 for (i = 0; i < MAX_PLAYERS; i++)
9827 KillPlayer(&stored_player[i]);
9829 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9830 DrawGameValue_Time(TimePlayed);
9833 if (tape.single_step && tape.recording && !tape.pausing &&
9834 !player->programmed_action)
9835 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9839 void ScrollScreen(struct PlayerInfo *player, int mode)
9841 static unsigned long screen_frame_counter = 0;
9843 if (mode == SCROLL_INIT)
9845 /* set scrolling step size according to actual player's moving speed */
9846 ScrollStepSize = TILEX / player->move_delay_value;
9848 screen_frame_counter = FrameCounter;
9849 ScreenMovDir = player->MovDir;
9850 ScreenMovPos = player->MovPos;
9851 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9854 else if (!FrameReached(&screen_frame_counter, 1))
9859 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9860 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9861 redraw_mask |= REDRAW_FIELD;
9864 ScreenMovDir = MV_NONE;
9867 void TestIfPlayerTouchesCustomElement(int x, int y)
9869 static int xy[4][2] =
9876 static int trigger_sides[4][2] =
9878 /* center side border side */
9879 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9880 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9881 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9882 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9884 static int touch_dir[4] =
9891 int center_element = Feld[x][y]; /* should always be non-moving! */
9894 for (i = 0; i < NUM_DIRECTIONS; i++)
9896 int xx = x + xy[i][0];
9897 int yy = y + xy[i][1];
9898 int center_side = trigger_sides[i][0];
9899 int border_side = trigger_sides[i][1];
9902 if (!IN_LEV_FIELD(xx, yy))
9905 if (IS_PLAYER(x, y))
9907 struct PlayerInfo *player = PLAYERINFO(x, y);
9909 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9910 border_element = Feld[xx][yy]; /* may be moving! */
9911 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9912 border_element = Feld[xx][yy];
9913 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9914 border_element = MovingOrBlocked2Element(xx, yy);
9916 continue; /* center and border element do not touch */
9918 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9919 player->index_bit, border_side);
9920 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9921 CE_PLAYER_TOUCHES_X,
9922 player->index_bit, border_side);
9924 else if (IS_PLAYER(xx, yy))
9926 struct PlayerInfo *player = PLAYERINFO(xx, yy);
9928 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9930 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9931 continue; /* center and border element do not touch */
9934 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9935 player->index_bit, center_side);
9936 CheckTriggeredElementChangeByPlayer(x, y, center_element,
9937 CE_PLAYER_TOUCHES_X,
9938 player->index_bit, center_side);
9944 void TestIfElementTouchesCustomElement(int x, int y)
9946 static int xy[4][2] =
9953 static int trigger_sides[4][2] =
9955 /* center side border side */
9956 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
9957 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
9958 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
9959 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
9961 static int touch_dir[4] =
9968 boolean change_center_element = FALSE;
9969 int center_element = Feld[x][y]; /* should always be non-moving! */
9972 for (i = 0; i < NUM_DIRECTIONS; i++)
9974 int xx = x + xy[i][0];
9975 int yy = y + xy[i][1];
9976 int center_side = trigger_sides[i][0];
9977 int border_side = trigger_sides[i][1];
9980 if (!IN_LEV_FIELD(xx, yy))
9983 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9984 border_element = Feld[xx][yy]; /* may be moving! */
9985 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9986 border_element = Feld[xx][yy];
9987 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
9988 border_element = MovingOrBlocked2Element(xx, yy);
9990 continue; /* center and border element do not touch */
9992 /* check for change of center element (but change it only once) */
9993 if (!change_center_element)
9994 change_center_element =
9995 CheckElementChangeBySide(x, y, center_element, border_element,
9996 CE_TOUCHING_X, border_side);
9998 /* check for change of border element */
9999 CheckElementChangeBySide(xx, yy, border_element, center_element,
10000 CE_TOUCHING_X, center_side);
10004 void TestIfElementHitsCustomElement(int x, int y, int direction)
10006 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10007 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10008 int hitx = x + dx, hity = y + dy;
10009 int hitting_element = Feld[x][y];
10010 int touched_element;
10012 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10015 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10016 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10018 if (IN_LEV_FIELD(hitx, hity))
10020 int opposite_direction = MV_DIR_OPPOSITE(direction);
10021 int hitting_side = direction;
10022 int touched_side = opposite_direction;
10023 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10024 MovDir[hitx][hity] != direction ||
10025 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10031 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10032 CE_HITTING_X, touched_side);
10034 CheckElementChangeBySide(hitx, hity, touched_element,
10035 hitting_element, CE_HIT_BY_X, hitting_side);
10037 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10038 CE_HIT_BY_SOMETHING, opposite_direction);
10042 /* "hitting something" is also true when hitting the playfield border */
10043 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10044 CE_HITTING_SOMETHING, direction);
10048 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10050 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10051 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10052 int hitx = x + dx, hity = y + dy;
10053 int hitting_element = Feld[x][y];
10054 int touched_element;
10056 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10057 !IS_FREE(hitx, hity) &&
10058 (!IS_MOVING(hitx, hity) ||
10059 MovDir[hitx][hity] != direction ||
10060 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10063 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10067 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10071 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10072 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10074 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10075 EP_CAN_SMASH_EVERYTHING, direction);
10077 if (IN_LEV_FIELD(hitx, hity))
10079 int opposite_direction = MV_DIR_OPPOSITE(direction);
10080 int hitting_side = direction;
10081 int touched_side = opposite_direction;
10083 int touched_element = MovingOrBlocked2Element(hitx, hity);
10086 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10087 MovDir[hitx][hity] != direction ||
10088 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10097 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10098 CE_SMASHED_BY_SOMETHING, opposite_direction);
10100 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10101 CE_OTHER_IS_SMASHING, touched_side);
10103 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10104 CE_OTHER_GETS_SMASHED, hitting_side);
10110 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10112 int i, kill_x = -1, kill_y = -1;
10114 int bad_element = -1;
10115 static int test_xy[4][2] =
10122 static int test_dir[4] =
10130 for (i = 0; i < NUM_DIRECTIONS; i++)
10132 int test_x, test_y, test_move_dir, test_element;
10134 test_x = good_x + test_xy[i][0];
10135 test_y = good_y + test_xy[i][1];
10137 if (!IN_LEV_FIELD(test_x, test_y))
10141 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10143 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10145 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10146 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10148 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10149 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10153 bad_element = test_element;
10159 if (kill_x != -1 || kill_y != -1)
10161 if (IS_PLAYER(good_x, good_y))
10163 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10165 if (player->shield_deadly_time_left > 0 &&
10166 !IS_INDESTRUCTIBLE(bad_element))
10167 Bang(kill_x, kill_y);
10168 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10169 KillPlayer(player);
10172 Bang(good_x, good_y);
10176 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10178 int i, kill_x = -1, kill_y = -1;
10179 int bad_element = Feld[bad_x][bad_y];
10180 static int test_xy[4][2] =
10187 static int touch_dir[4] =
10189 MV_LEFT | MV_RIGHT,
10194 static int test_dir[4] =
10202 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10205 for (i = 0; i < NUM_DIRECTIONS; i++)
10207 int test_x, test_y, test_move_dir, test_element;
10209 test_x = bad_x + test_xy[i][0];
10210 test_y = bad_y + test_xy[i][1];
10211 if (!IN_LEV_FIELD(test_x, test_y))
10215 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10217 test_element = Feld[test_x][test_y];
10219 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10220 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10222 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10223 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10225 /* good thing is player or penguin that does not move away */
10226 if (IS_PLAYER(test_x, test_y))
10228 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10230 if (bad_element == EL_ROBOT && player->is_moving)
10231 continue; /* robot does not kill player if he is moving */
10233 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10235 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10236 continue; /* center and border element do not touch */
10243 else if (test_element == EL_PENGUIN)
10252 if (kill_x != -1 || kill_y != -1)
10254 if (IS_PLAYER(kill_x, kill_y))
10256 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10258 if (player->shield_deadly_time_left > 0 &&
10259 !IS_INDESTRUCTIBLE(bad_element))
10260 Bang(bad_x, bad_y);
10261 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10262 KillPlayer(player);
10265 Bang(kill_x, kill_y);
10269 void TestIfPlayerTouchesBadThing(int x, int y)
10271 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10274 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10276 TestIfGoodThingHitsBadThing(x, y, move_dir);
10279 void TestIfBadThingTouchesPlayer(int x, int y)
10281 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10284 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10286 TestIfBadThingHitsGoodThing(x, y, move_dir);
10289 void TestIfFriendTouchesBadThing(int x, int y)
10291 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10294 void TestIfBadThingTouchesFriend(int x, int y)
10296 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10299 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10301 int i, kill_x = bad_x, kill_y = bad_y;
10302 static int xy[4][2] =
10310 for (i = 0; i < NUM_DIRECTIONS; i++)
10314 x = bad_x + xy[i][0];
10315 y = bad_y + xy[i][1];
10316 if (!IN_LEV_FIELD(x, y))
10319 element = Feld[x][y];
10320 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10321 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10329 if (kill_x != bad_x || kill_y != bad_y)
10330 Bang(bad_x, bad_y);
10333 void KillPlayer(struct PlayerInfo *player)
10335 int jx = player->jx, jy = player->jy;
10337 if (!player->active)
10340 /* remove accessible field at the player's position */
10341 Feld[jx][jy] = EL_EMPTY;
10343 /* deactivate shield (else Bang()/Explode() would not work right) */
10344 player->shield_normal_time_left = 0;
10345 player->shield_deadly_time_left = 0;
10348 BuryPlayer(player);
10351 static void KillPlayerUnlessEnemyProtected(int x, int y)
10353 if (!PLAYER_ENEMY_PROTECTED(x, y))
10354 KillPlayer(PLAYERINFO(x, y));
10357 static void KillPlayerUnlessExplosionProtected(int x, int y)
10359 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10360 KillPlayer(PLAYERINFO(x, y));
10363 void BuryPlayer(struct PlayerInfo *player)
10365 int jx = player->jx, jy = player->jy;
10367 if (!player->active)
10370 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10371 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10373 player->GameOver = TRUE;
10374 RemovePlayer(player);
10377 void RemovePlayer(struct PlayerInfo *player)
10379 int jx = player->jx, jy = player->jy;
10380 int i, found = FALSE;
10382 player->present = FALSE;
10383 player->active = FALSE;
10385 if (!ExplodeField[jx][jy])
10386 StorePlayer[jx][jy] = 0;
10388 if (player->is_moving)
10389 DrawLevelField(player->last_jx, player->last_jy);
10391 for (i = 0; i < MAX_PLAYERS; i++)
10392 if (stored_player[i].active)
10396 AllPlayersGone = TRUE;
10402 #if USE_NEW_SNAP_DELAY
10403 static void setFieldForSnapping(int x, int y, int element, int direction)
10405 struct ElementInfo *ei = &element_info[element];
10406 int direction_bit = MV_DIR_TO_BIT(direction);
10407 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10408 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10409 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10411 Feld[x][y] = EL_ELEMENT_SNAPPING;
10412 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10414 ResetGfxAnimation(x, y);
10416 GfxElement[x][y] = element;
10417 GfxAction[x][y] = action;
10418 GfxDir[x][y] = direction;
10419 GfxFrame[x][y] = -1;
10424 =============================================================================
10425 checkDiagonalPushing()
10426 -----------------------------------------------------------------------------
10427 check if diagonal input device direction results in pushing of object
10428 (by checking if the alternative direction is walkable, diggable, ...)
10429 =============================================================================
10432 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10433 int x, int y, int real_dx, int real_dy)
10435 int jx, jy, dx, dy, xx, yy;
10437 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10440 /* diagonal direction: check alternative direction */
10445 xx = jx + (dx == 0 ? real_dx : 0);
10446 yy = jy + (dy == 0 ? real_dy : 0);
10448 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10452 =============================================================================
10454 -----------------------------------------------------------------------------
10455 x, y: field next to player (non-diagonal) to try to dig to
10456 real_dx, real_dy: direction as read from input device (can be diagonal)
10457 =============================================================================
10460 int DigField(struct PlayerInfo *player,
10461 int oldx, int oldy, int x, int y,
10462 int real_dx, int real_dy, int mode)
10464 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10465 boolean player_was_pushing = player->is_pushing;
10466 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10467 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10468 int jx = oldx, jy = oldy;
10469 int dx = x - jx, dy = y - jy;
10470 int nextx = x + dx, nexty = y + dy;
10471 int move_direction = (dx == -1 ? MV_LEFT :
10472 dx == +1 ? MV_RIGHT :
10474 dy == +1 ? MV_DOWN : MV_NONE);
10475 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10476 int dig_side = MV_DIR_OPPOSITE(move_direction);
10477 int old_element = Feld[jx][jy];
10478 #if USE_FIXED_DONT_RUN_INTO
10479 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10485 if (is_player) /* function can also be called by EL_PENGUIN */
10487 if (player->MovPos == 0)
10489 player->is_digging = FALSE;
10490 player->is_collecting = FALSE;
10493 if (player->MovPos == 0) /* last pushing move finished */
10494 player->is_pushing = FALSE;
10496 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10498 player->is_switching = FALSE;
10499 player->push_delay = -1;
10501 return MP_NO_ACTION;
10505 #if !USE_FIXED_DONT_RUN_INTO
10506 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10507 return MP_NO_ACTION;
10510 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10511 old_element = Back[jx][jy];
10513 /* in case of element dropped at player position, check background */
10514 else if (Back[jx][jy] != EL_EMPTY &&
10515 game.engine_version >= VERSION_IDENT(2,2,0,0))
10516 old_element = Back[jx][jy];
10519 #if USE_FIXED_DONT_RUN_INTO
10520 if (player_can_move && DONT_RUN_INTO(element))
10522 if (element == EL_ACID && dx == 0 && dy == 1)
10525 Feld[jx][jy] = EL_PLAYER_1;
10526 InitMovingField(jx, jy, MV_DOWN);
10527 Store[jx][jy] = EL_ACID;
10528 ContinueMoving(jx, jy);
10529 BuryPlayer(player);
10532 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10534 return MP_DONT_RUN_INTO;
10540 #if USE_FIXED_DONT_RUN_INTO
10541 if (player_can_move && DONT_RUN_INTO(element))
10543 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10545 return MP_DONT_RUN_INTO;
10550 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10551 return MP_NO_ACTION; /* field has no opening in this direction */
10553 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10554 return MP_NO_ACTION; /* field has no opening in this direction */
10557 #if USE_FIXED_DONT_RUN_INTO
10558 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10561 Feld[jx][jy] = EL_PLAYER_1;
10562 InitMovingField(jx, jy, MV_DOWN);
10563 Store[jx][jy] = EL_ACID;
10564 ContinueMoving(jx, jy);
10565 BuryPlayer(player);
10567 return MP_DONT_RUN_INTO;
10573 #if USE_FIXED_DONT_RUN_INTO
10574 if (player_can_move && DONT_RUN_INTO(element))
10576 if (element == EL_ACID && dx == 0 && dy == 1)
10579 Feld[jx][jy] = EL_PLAYER_1;
10580 InitMovingField(jx, jy, MV_DOWN);
10581 Store[jx][jy] = EL_ACID;
10582 ContinueMoving(jx, jy);
10583 BuryPlayer(player);
10586 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10588 return MP_DONT_RUN_INTO;
10593 #if USE_FIXED_DONT_RUN_INTO
10594 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10595 return MP_NO_ACTION;
10598 #if !USE_FIXED_DONT_RUN_INTO
10599 element = Feld[x][y];
10602 collect_count = element_info[element].collect_count_initial;
10604 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10605 return MP_NO_ACTION;
10607 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10608 player_can_move = player_can_move_or_snap;
10610 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10611 game.engine_version >= VERSION_IDENT(2,2,0,0))
10613 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10614 player->index_bit, dig_side);
10615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10616 player->index_bit, dig_side);
10618 if (Feld[x][y] != element) /* field changed by snapping */
10621 return MP_NO_ACTION;
10624 if (game.gravity && is_player && !player->is_auto_moving &&
10625 canFallDown(player) && move_direction != MV_DOWN &&
10626 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10627 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10629 if (player_can_move &&
10630 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10632 int sound_element = SND_ELEMENT(element);
10633 int sound_action = ACTION_WALKING;
10635 if (IS_RND_GATE(element))
10637 if (!player->key[RND_GATE_NR(element)])
10638 return MP_NO_ACTION;
10640 else if (IS_RND_GATE_GRAY(element))
10642 if (!player->key[RND_GATE_GRAY_NR(element)])
10643 return MP_NO_ACTION;
10645 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10647 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10648 return MP_NO_ACTION;
10650 else if (element == EL_EXIT_OPEN ||
10651 element == EL_SP_EXIT_OPEN ||
10652 element == EL_SP_EXIT_OPENING)
10654 sound_action = ACTION_PASSING; /* player is passing exit */
10656 else if (element == EL_EMPTY)
10658 sound_action = ACTION_MOVING; /* nothing to walk on */
10661 /* play sound from background or player, whatever is available */
10662 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10663 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10665 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10667 else if (player_can_move &&
10668 IS_PASSABLE(element) && canPassField(x, y, move_direction))
10670 if (!ACCESS_FROM(element, opposite_direction))
10671 return MP_NO_ACTION; /* field not accessible from this direction */
10673 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10674 return MP_NO_ACTION;
10676 if (IS_EM_GATE(element))
10678 if (!player->key[EM_GATE_NR(element)])
10679 return MP_NO_ACTION;
10681 else if (IS_EM_GATE_GRAY(element))
10683 if (!player->key[EM_GATE_GRAY_NR(element)])
10684 return MP_NO_ACTION;
10686 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10688 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10689 return MP_NO_ACTION;
10691 else if (IS_SP_PORT(element))
10693 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10694 element == EL_SP_GRAVITY_PORT_RIGHT ||
10695 element == EL_SP_GRAVITY_PORT_UP ||
10696 element == EL_SP_GRAVITY_PORT_DOWN)
10697 game.gravity = !game.gravity;
10698 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10699 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10700 element == EL_SP_GRAVITY_ON_PORT_UP ||
10701 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10702 game.gravity = TRUE;
10703 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10704 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10705 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10706 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10707 game.gravity = FALSE;
10710 /* automatically move to the next field with double speed */
10711 player->programmed_action = move_direction;
10713 if (player->move_delay_reset_counter == 0)
10715 player->move_delay_reset_counter = 2; /* two double speed steps */
10717 DOUBLE_PLAYER_SPEED(player);
10720 PlayLevelSoundAction(x, y, ACTION_PASSING);
10722 else if (player_can_move_or_snap && IS_DIGGABLE(element))
10726 if (mode != DF_SNAP)
10728 GfxElement[x][y] = GFX_ELEMENT(element);
10729 player->is_digging = TRUE;
10732 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10734 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10735 player->index_bit, dig_side);
10737 if (mode == DF_SNAP)
10739 #if USE_NEW_SNAP_DELAY
10740 if (level.block_snap_field)
10741 setFieldForSnapping(x, y, element, move_direction);
10743 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10745 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10748 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10749 player->index_bit, dig_side);
10752 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
10756 if (is_player && mode != DF_SNAP)
10758 GfxElement[x][y] = element;
10759 player->is_collecting = TRUE;
10762 if (element == EL_SPEED_PILL)
10764 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10766 else if (element == EL_EXTRA_TIME && level.time > 0)
10768 TimeLeft += level.extra_time;
10769 DrawGameValue_Time(TimeLeft);
10771 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10773 player->shield_normal_time_left += level.shield_normal_time;
10774 if (element == EL_SHIELD_DEADLY)
10775 player->shield_deadly_time_left += level.shield_deadly_time;
10777 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10779 if (player->inventory_size < MAX_INVENTORY_SIZE)
10780 player->inventory_element[player->inventory_size++] = element;
10782 DrawGameValue_Dynamite(local_player->inventory_size);
10784 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10786 player->dynabomb_count++;
10787 player->dynabombs_left++;
10789 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10791 player->dynabomb_size++;
10793 else if (element == EL_DYNABOMB_INCREASE_POWER)
10795 player->dynabomb_xl = TRUE;
10797 else if (IS_KEY(element))
10799 player->key[KEY_NR(element)] = TRUE;
10801 DrawGameValue_Keys(player->key);
10803 redraw_mask |= REDRAW_DOOR_1;
10805 else if (IS_ENVELOPE(element))
10807 player->show_envelope = element;
10809 else if (element == EL_EMC_LENSES)
10811 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10813 RedrawAllInvisibleElementsForLenses();
10815 else if (element == EL_EMC_MAGNIFIER)
10817 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10819 RedrawAllInvisibleElementsForMagnifier();
10821 else if (IS_DROPPABLE(element) ||
10822 IS_THROWABLE(element)) /* can be collected and dropped */
10826 if (collect_count == 0)
10827 player->inventory_infinite_element = element;
10829 for (i = 0; i < collect_count; i++)
10830 if (player->inventory_size < MAX_INVENTORY_SIZE)
10831 player->inventory_element[player->inventory_size++] = element;
10833 DrawGameValue_Dynamite(local_player->inventory_size);
10835 else if (collect_count > 0)
10837 local_player->gems_still_needed -= collect_count;
10838 if (local_player->gems_still_needed < 0)
10839 local_player->gems_still_needed = 0;
10841 DrawGameValue_Emeralds(local_player->gems_still_needed);
10844 RaiseScoreElement(element);
10845 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10848 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10849 player->index_bit, dig_side);
10851 if (mode == DF_SNAP)
10853 #if USE_NEW_SNAP_DELAY
10854 if (level.block_snap_field)
10855 setFieldForSnapping(x, y, element, move_direction);
10857 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10859 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10862 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10863 player->index_bit, dig_side);
10866 else if (player_can_move_or_snap && IS_PUSHABLE(element))
10868 if (mode == DF_SNAP && element != EL_BD_ROCK)
10869 return MP_NO_ACTION;
10871 if (CAN_FALL(element) && dy)
10872 return MP_NO_ACTION;
10874 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10875 !(element == EL_SPRING && level.use_spring_bug))
10876 return MP_NO_ACTION;
10878 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10879 ((move_direction & MV_VERTICAL &&
10880 ((element_info[element].move_pattern & MV_LEFT &&
10881 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10882 (element_info[element].move_pattern & MV_RIGHT &&
10883 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10884 (move_direction & MV_HORIZONTAL &&
10885 ((element_info[element].move_pattern & MV_UP &&
10886 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10887 (element_info[element].move_pattern & MV_DOWN &&
10888 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10889 return MP_NO_ACTION;
10891 /* do not push elements already moving away faster than player */
10892 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10893 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10894 return MP_NO_ACTION;
10896 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10898 if (player->push_delay_value == -1 || !player_was_pushing)
10899 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10901 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10903 if (player->push_delay_value == -1)
10904 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10906 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10908 if (!player->is_pushing)
10909 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10912 player->is_pushing = TRUE;
10914 if (!(IN_LEV_FIELD(nextx, nexty) &&
10915 (IS_FREE(nextx, nexty) ||
10916 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10917 IS_SB_ELEMENT(element)))))
10918 return MP_NO_ACTION;
10920 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10921 return MP_NO_ACTION;
10923 if (player->push_delay == -1) /* new pushing; restart delay */
10924 player->push_delay = 0;
10926 if (player->push_delay < player->push_delay_value &&
10927 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10928 element != EL_SPRING && element != EL_BALLOON)
10930 /* make sure that there is no move delay before next try to push */
10931 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10932 player->move_delay = 0;
10934 return MP_NO_ACTION;
10937 if (IS_SB_ELEMENT(element))
10939 if (element == EL_SOKOBAN_FIELD_FULL)
10941 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10942 local_player->sokobanfields_still_needed++;
10945 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10947 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10948 local_player->sokobanfields_still_needed--;
10951 Feld[x][y] = EL_SOKOBAN_OBJECT;
10953 if (Back[x][y] == Back[nextx][nexty])
10954 PlayLevelSoundAction(x, y, ACTION_PUSHING);
10955 else if (Back[x][y] != 0)
10956 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10959 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10962 if (local_player->sokobanfields_still_needed == 0 &&
10963 game.emulation == EMU_SOKOBAN)
10965 player->LevelSolved = player->GameOver = TRUE;
10966 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10970 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10972 InitMovingField(x, y, move_direction);
10973 GfxAction[x][y] = ACTION_PUSHING;
10975 if (mode == DF_SNAP)
10976 ContinueMoving(x, y);
10978 MovPos[x][y] = (dx != 0 ? dx : dy);
10980 Pushed[x][y] = TRUE;
10981 Pushed[nextx][nexty] = TRUE;
10983 if (game.engine_version < VERSION_IDENT(2,2,0,7))
10984 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10986 player->push_delay_value = -1; /* get new value later */
10988 /* check for element change _after_ element has been pushed */
10989 if (game.use_change_when_pushing_bug)
10991 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10992 player->index_bit, dig_side);
10993 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10994 player->index_bit, dig_side);
10997 else if (IS_SWITCHABLE(element))
10999 if (PLAYER_SWITCHING(player, x, y))
11001 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11002 player->index_bit, dig_side);
11007 player->is_switching = TRUE;
11008 player->switch_x = x;
11009 player->switch_y = y;
11011 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11013 if (element == EL_ROBOT_WHEEL)
11015 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11019 DrawLevelField(x, y);
11021 else if (element == EL_SP_TERMINAL)
11026 SCAN_PLAYFIELD(xx, yy)
11028 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11031 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11033 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11034 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11037 else if (IS_BELT_SWITCH(element))
11039 ToggleBeltSwitch(x, y);
11041 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11042 element == EL_SWITCHGATE_SWITCH_DOWN)
11044 ToggleSwitchgateSwitch(x, y);
11046 else if (element == EL_LIGHT_SWITCH ||
11047 element == EL_LIGHT_SWITCH_ACTIVE)
11049 ToggleLightSwitch(x, y);
11051 else if (element == EL_TIMEGATE_SWITCH)
11053 ActivateTimegateSwitch(x, y);
11055 else if (element == EL_BALLOON_SWITCH_LEFT ||
11056 element == EL_BALLOON_SWITCH_RIGHT ||
11057 element == EL_BALLOON_SWITCH_UP ||
11058 element == EL_BALLOON_SWITCH_DOWN ||
11059 element == EL_BALLOON_SWITCH_NONE ||
11060 element == EL_BALLOON_SWITCH_ANY)
11062 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11063 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11064 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11065 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11066 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11069 else if (element == EL_LAMP)
11071 Feld[x][y] = EL_LAMP_ACTIVE;
11072 local_player->lights_still_needed--;
11074 ResetGfxAnimation(x, y);
11075 DrawLevelField(x, y);
11077 else if (element == EL_TIME_ORB_FULL)
11079 Feld[x][y] = EL_TIME_ORB_EMPTY;
11081 if (level.time > 0 || level.use_time_orb_bug)
11083 TimeLeft += level.time_orb_time;
11084 DrawGameValue_Time(TimeLeft);
11087 ResetGfxAnimation(x, y);
11088 DrawLevelField(x, y);
11090 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11091 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11095 game.ball_state = !game.ball_state;
11098 SCAN_PLAYFIELD(xx, yy)
11100 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11103 int e = Feld[xx][yy];
11105 if (game.ball_state)
11107 if (e == EL_EMC_MAGIC_BALL)
11108 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11109 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11110 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11114 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11115 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11116 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11117 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11122 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11123 player->index_bit, dig_side);
11125 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11126 player->index_bit, dig_side);
11128 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11129 player->index_bit, dig_side);
11135 if (!PLAYER_SWITCHING(player, x, y))
11137 player->is_switching = TRUE;
11138 player->switch_x = x;
11139 player->switch_y = y;
11141 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11142 player->index_bit, dig_side);
11143 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11144 player->index_bit, dig_side);
11146 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11147 player->index_bit, dig_side);
11148 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11149 player->index_bit, dig_side);
11152 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11153 player->index_bit, dig_side);
11154 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11155 player->index_bit, dig_side);
11157 return MP_NO_ACTION;
11160 player->push_delay = -1;
11162 if (is_player) /* function can also be called by EL_PENGUIN */
11164 if (Feld[x][y] != element) /* really digged/collected something */
11165 player->is_collecting = !player->is_digging;
11171 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11173 int jx = player->jx, jy = player->jy;
11174 int x = jx + dx, y = jy + dy;
11175 int snap_direction = (dx == -1 ? MV_LEFT :
11176 dx == +1 ? MV_RIGHT :
11178 dy == +1 ? MV_DOWN : MV_NONE);
11180 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11183 if (!player->active || !IN_LEV_FIELD(x, y))
11191 if (player->MovPos == 0)
11192 player->is_pushing = FALSE;
11194 player->is_snapping = FALSE;
11196 if (player->MovPos == 0)
11198 player->is_moving = FALSE;
11199 player->is_digging = FALSE;
11200 player->is_collecting = FALSE;
11206 if (player->is_snapping)
11209 player->MovDir = snap_direction;
11211 if (player->MovPos == 0)
11213 player->is_moving = FALSE;
11214 player->is_digging = FALSE;
11215 player->is_collecting = FALSE;
11218 player->is_dropping = FALSE;
11220 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11223 player->is_snapping = TRUE;
11225 if (player->MovPos == 0)
11227 player->is_moving = FALSE;
11228 player->is_digging = FALSE;
11229 player->is_collecting = FALSE;
11232 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11233 DrawLevelField(player->last_jx, player->last_jy);
11235 DrawLevelField(x, y);
11240 boolean DropElement(struct PlayerInfo *player)
11242 int old_element, new_element;
11243 int dropx = player->jx, dropy = player->jy;
11244 int drop_direction = player->MovDir;
11245 int drop_side = drop_direction;
11246 int drop_element = (player->inventory_size > 0 ?
11247 player->inventory_element[player->inventory_size - 1] :
11248 player->inventory_infinite_element != EL_UNDEFINED ?
11249 player->inventory_infinite_element :
11250 player->dynabombs_left > 0 ?
11251 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11254 /* do not drop an element on top of another element; when holding drop key
11255 pressed without moving, dropped element must move away before the next
11256 element can be dropped (this is especially important if the next element
11257 is dynamite, which can be placed on background for historical reasons) */
11258 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11261 if (IS_THROWABLE(drop_element))
11263 dropx += GET_DX_FROM_DIR(drop_direction);
11264 dropy += GET_DY_FROM_DIR(drop_direction);
11266 if (!IN_LEV_FIELD(dropx, dropy))
11270 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11271 new_element = drop_element; /* default: no change when dropping */
11273 /* check if player is active, not moving and ready to drop */
11274 if (!player->active || player->MovPos || player->drop_delay > 0)
11277 /* check if player has anything that can be dropped */
11278 if (new_element == EL_UNDEFINED)
11281 /* check if anything can be dropped at the current position */
11282 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11285 /* collected custom elements can only be dropped on empty fields */
11286 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11289 if (old_element != EL_EMPTY)
11290 Back[dropx][dropy] = old_element; /* store old element on this field */
11292 ResetGfxAnimation(dropx, dropy);
11293 ResetRandomAnimationValue(dropx, dropy);
11295 if (player->inventory_size > 0 ||
11296 player->inventory_infinite_element != EL_UNDEFINED)
11298 if (player->inventory_size > 0)
11300 player->inventory_size--;
11302 DrawGameValue_Dynamite(local_player->inventory_size);
11304 if (new_element == EL_DYNAMITE)
11305 new_element = EL_DYNAMITE_ACTIVE;
11306 else if (new_element == EL_SP_DISK_RED)
11307 new_element = EL_SP_DISK_RED_ACTIVE;
11310 Feld[dropx][dropy] = new_element;
11312 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11313 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11314 el2img(Feld[dropx][dropy]), 0);
11316 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11318 /* needed if previous element just changed to "empty" in the last frame */
11319 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11321 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11322 player->index_bit, drop_side);
11323 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11325 player->index_bit, drop_side);
11327 TestIfElementTouchesCustomElement(dropx, dropy);
11329 else /* player is dropping a dyna bomb */
11331 player->dynabombs_left--;
11333 Feld[dropx][dropy] = new_element;
11335 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11336 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11337 el2img(Feld[dropx][dropy]), 0);
11339 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11342 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11343 InitField_WithBug1(dropx, dropy, FALSE);
11345 new_element = Feld[dropx][dropy]; /* element might have changed */
11347 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11348 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11350 int move_direction, nextx, nexty;
11352 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11353 MovDir[dropx][dropy] = drop_direction;
11355 move_direction = MovDir[dropx][dropy];
11356 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11357 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11359 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11360 CheckCollision[dropx][dropy] = 2;
11363 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11364 player->is_dropping = TRUE;
11366 player->drop_x = dropx;
11367 player->drop_y = dropy;
11372 /* ------------------------------------------------------------------------- */
11373 /* game sound playing functions */
11374 /* ------------------------------------------------------------------------- */
11376 static int *loop_sound_frame = NULL;
11377 static int *loop_sound_volume = NULL;
11379 void InitPlayLevelSound()
11381 int num_sounds = getSoundListSize();
11383 checked_free(loop_sound_frame);
11384 checked_free(loop_sound_volume);
11386 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11387 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11390 static void PlayLevelSound(int x, int y, int nr)
11392 int sx = SCREENX(x), sy = SCREENY(y);
11393 int volume, stereo_position;
11394 int max_distance = 8;
11395 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11397 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11398 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11401 if (!IN_LEV_FIELD(x, y) ||
11402 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11403 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11406 volume = SOUND_MAX_VOLUME;
11408 if (!IN_SCR_FIELD(sx, sy))
11410 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11411 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11413 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11416 stereo_position = (SOUND_MAX_LEFT +
11417 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11418 (SCR_FIELDX + 2 * max_distance));
11420 if (IS_LOOP_SOUND(nr))
11422 /* This assures that quieter loop sounds do not overwrite louder ones,
11423 while restarting sound volume comparison with each new game frame. */
11425 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11428 loop_sound_volume[nr] = volume;
11429 loop_sound_frame[nr] = FrameCounter;
11432 PlaySoundExt(nr, volume, stereo_position, type);
11435 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11437 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11438 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11439 y < LEVELY(BY1) ? LEVELY(BY1) :
11440 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11444 static void PlayLevelSoundAction(int x, int y, int action)
11446 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11449 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11451 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11453 if (sound_effect != SND_UNDEFINED)
11454 PlayLevelSound(x, y, sound_effect);
11457 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11460 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11462 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11463 PlayLevelSound(x, y, sound_effect);
11466 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11468 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11470 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11471 PlayLevelSound(x, y, sound_effect);
11474 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11476 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11478 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11479 StopSound(sound_effect);
11482 static void PlayLevelMusic()
11484 if (levelset.music[level_nr] != MUS_UNDEFINED)
11485 PlayMusic(levelset.music[level_nr]); /* from config file */
11487 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11490 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11492 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11497 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11501 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11505 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11509 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11513 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11517 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11521 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11524 case SAMPLE_android_clone:
11525 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11528 case SAMPLE_android_move:
11529 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11532 case SAMPLE_spring:
11533 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11537 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11541 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11544 case SAMPLE_eater_eat:
11545 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11549 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11552 case SAMPLE_collect:
11553 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11556 case SAMPLE_diamond:
11557 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11560 case SAMPLE_squash:
11561 /* !!! CHECK THIS !!! */
11563 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11565 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11569 case SAMPLE_wonderfall:
11570 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11574 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11578 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11582 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11586 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11590 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11594 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11597 case SAMPLE_wonder:
11598 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11602 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11605 case SAMPLE_exit_open:
11606 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11609 case SAMPLE_exit_leave:
11610 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11613 case SAMPLE_dynamite:
11614 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11618 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11622 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11626 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11630 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11634 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11638 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11642 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11647 void RaiseScore(int value)
11649 local_player->score += value;
11651 DrawGameValue_Score(local_player->score);
11654 void RaiseScoreElement(int element)
11659 case EL_BD_DIAMOND:
11660 case EL_EMERALD_YELLOW:
11661 case EL_EMERALD_RED:
11662 case EL_EMERALD_PURPLE:
11663 case EL_SP_INFOTRON:
11664 RaiseScore(level.score[SC_EMERALD]);
11667 RaiseScore(level.score[SC_DIAMOND]);
11670 RaiseScore(level.score[SC_CRYSTAL]);
11673 RaiseScore(level.score[SC_PEARL]);
11676 case EL_BD_BUTTERFLY:
11677 case EL_SP_ELECTRON:
11678 RaiseScore(level.score[SC_BUG]);
11681 case EL_BD_FIREFLY:
11682 case EL_SP_SNIKSNAK:
11683 RaiseScore(level.score[SC_SPACESHIP]);
11686 case EL_DARK_YAMYAM:
11687 RaiseScore(level.score[SC_YAMYAM]);
11690 RaiseScore(level.score[SC_ROBOT]);
11693 RaiseScore(level.score[SC_PACMAN]);
11696 RaiseScore(level.score[SC_NUT]);
11699 case EL_SP_DISK_RED:
11700 case EL_DYNABOMB_INCREASE_NUMBER:
11701 case EL_DYNABOMB_INCREASE_SIZE:
11702 case EL_DYNABOMB_INCREASE_POWER:
11703 RaiseScore(level.score[SC_DYNAMITE]);
11705 case EL_SHIELD_NORMAL:
11706 case EL_SHIELD_DEADLY:
11707 RaiseScore(level.score[SC_SHIELD]);
11709 case EL_EXTRA_TIME:
11710 RaiseScore(level.extra_time_score);
11724 RaiseScore(level.score[SC_KEY]);
11727 RaiseScore(element_info[element].collect_score);
11732 void RequestQuitGame(boolean ask_if_really_quit)
11734 if (AllPlayersGone ||
11735 !ask_if_really_quit ||
11736 level_editor_test_game ||
11737 Request("Do you really want to quit the game ?",
11738 REQ_ASK | REQ_STAY_CLOSED))
11740 #if defined(NETWORK_AVALIABLE)
11741 if (options.network)
11742 SendToServer_StopPlaying();
11746 game_status = GAME_MODE_MAIN;
11752 if (tape.playing && tape.deactivate_display)
11753 TapeDeactivateDisplayOff(TRUE);
11755 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11757 if (tape.playing && tape.deactivate_display)
11758 TapeDeactivateDisplayOn();
11763 /* ---------- new game button stuff ---------------------------------------- */
11765 /* graphic position values for game buttons */
11766 #define GAME_BUTTON_XSIZE 30
11767 #define GAME_BUTTON_YSIZE 30
11768 #define GAME_BUTTON_XPOS 5
11769 #define GAME_BUTTON_YPOS 215
11770 #define SOUND_BUTTON_XPOS 5
11771 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11773 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11774 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11775 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11776 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11777 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11778 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11785 } gamebutton_info[NUM_GAME_BUTTONS] =
11788 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11793 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11794 GAME_CTRL_ID_PAUSE,
11798 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11803 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11804 SOUND_CTRL_ID_MUSIC,
11805 "background music on/off"
11808 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11809 SOUND_CTRL_ID_LOOPS,
11810 "sound loops on/off"
11813 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11814 SOUND_CTRL_ID_SIMPLE,
11815 "normal sounds on/off"
11819 void CreateGameButtons()
11823 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11825 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11826 struct GadgetInfo *gi;
11829 unsigned long event_mask;
11830 int gd_xoffset, gd_yoffset;
11831 int gd_x1, gd_x2, gd_y1, gd_y2;
11834 gd_xoffset = gamebutton_info[i].x;
11835 gd_yoffset = gamebutton_info[i].y;
11836 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11837 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11839 if (id == GAME_CTRL_ID_STOP ||
11840 id == GAME_CTRL_ID_PAUSE ||
11841 id == GAME_CTRL_ID_PLAY)
11843 button_type = GD_TYPE_NORMAL_BUTTON;
11845 event_mask = GD_EVENT_RELEASED;
11846 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11847 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11851 button_type = GD_TYPE_CHECK_BUTTON;
11853 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11854 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11855 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11856 event_mask = GD_EVENT_PRESSED;
11857 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
11858 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11861 gi = CreateGadget(GDI_CUSTOM_ID, id,
11862 GDI_INFO_TEXT, gamebutton_info[i].infotext,
11863 GDI_X, DX + gd_xoffset,
11864 GDI_Y, DY + gd_yoffset,
11865 GDI_WIDTH, GAME_BUTTON_XSIZE,
11866 GDI_HEIGHT, GAME_BUTTON_YSIZE,
11867 GDI_TYPE, button_type,
11868 GDI_STATE, GD_BUTTON_UNPRESSED,
11869 GDI_CHECKED, checked,
11870 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11871 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11872 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11873 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11874 GDI_EVENT_MASK, event_mask,
11875 GDI_CALLBACK_ACTION, HandleGameButtons,
11879 Error(ERR_EXIT, "cannot create gadget");
11881 game_gadget[id] = gi;
11885 void FreeGameButtons()
11889 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11890 FreeGadget(game_gadget[i]);
11893 static void MapGameButtons()
11897 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11898 MapGadget(game_gadget[i]);
11901 void UnmapGameButtons()
11905 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11906 UnmapGadget(game_gadget[i]);
11909 static void HandleGameButtons(struct GadgetInfo *gi)
11911 int id = gi->custom_id;
11913 if (game_status != GAME_MODE_PLAYING)
11918 case GAME_CTRL_ID_STOP:
11919 RequestQuitGame(TRUE);
11922 case GAME_CTRL_ID_PAUSE:
11923 if (options.network)
11925 #if defined(NETWORK_AVALIABLE)
11927 SendToServer_ContinuePlaying();
11929 SendToServer_PausePlaying();
11933 TapeTogglePause(TAPE_TOGGLE_MANUAL);
11936 case GAME_CTRL_ID_PLAY:
11939 #if defined(NETWORK_AVALIABLE)
11940 if (options.network)
11941 SendToServer_ContinuePlaying();
11945 tape.pausing = FALSE;
11946 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11951 case SOUND_CTRL_ID_MUSIC:
11952 if (setup.sound_music)
11954 setup.sound_music = FALSE;
11957 else if (audio.music_available)
11959 setup.sound = setup.sound_music = TRUE;
11961 SetAudioMode(setup.sound);
11967 case SOUND_CTRL_ID_LOOPS:
11968 if (setup.sound_loops)
11969 setup.sound_loops = FALSE;
11970 else if (audio.loops_available)
11972 setup.sound = setup.sound_loops = TRUE;
11973 SetAudioMode(setup.sound);
11977 case SOUND_CTRL_ID_SIMPLE:
11978 if (setup.sound_simple)
11979 setup.sound_simple = FALSE;
11980 else if (audio.sound_available)
11982 setup.sound = setup.sound_simple = TRUE;
11983 SetAudioMode(setup.sound);