1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
45 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 /* for MovePlayer() */
53 #define MP_NO_ACTION 0
56 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
58 /* for ScrollPlayer() */
60 #define SCROLL_GO_ON 1
62 /* for Bang()/Explode() */
63 #define EX_PHASE_START 0
64 #define EX_TYPE_NONE 0
65 #define EX_TYPE_NORMAL (1 << 0)
66 #define EX_TYPE_CENTER (1 << 1)
67 #define EX_TYPE_BORDER (1 << 2)
68 #define EX_TYPE_CROSS (1 << 3)
69 #define EX_TYPE_DYNA (1 << 4)
70 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
72 /* special positions in the game control window (relative to control window) */
75 #define XX_EMERALDS 29
76 #define YY_EMERALDS 54
77 #define XX_DYNAMITE 29
78 #define YY_DYNAMITE 89
87 /* special positions in the game control window (relative to main window) */
88 #define DX_LEVEL (DX + XX_LEVEL)
89 #define DY_LEVEL (DY + YY_LEVEL)
90 #define DX_EMERALDS (DX + XX_EMERALDS)
91 #define DY_EMERALDS (DY + YY_EMERALDS)
92 #define DX_DYNAMITE (DX + XX_DYNAMITE)
93 #define DY_DYNAMITE (DY + YY_DYNAMITE)
94 #define DX_KEYS (DX + XX_KEYS)
95 #define DY_KEYS (DY + YY_KEYS)
96 #define DX_SCORE (DX + XX_SCORE)
97 #define DY_SCORE (DY + YY_SCORE)
98 #define DX_TIME1 (DX + XX_TIME1)
99 #define DX_TIME2 (DX + XX_TIME2)
100 #define DY_TIME (DY + YY_TIME)
102 /* values for delayed check of falling and moving elements and for collision */
103 #define CHECK_DELAY_MOVING 3
104 #define CHECK_DELAY_FALLING 3
105 #define CHECK_DELAY_COLLISION 2
107 /* values for initial player move delay (initial delay counter value) */
108 #define INITIAL_MOVE_DELAY_OFF -1
109 #define INITIAL_MOVE_DELAY_ON 0
111 /* values for player movement speed (which is in fact a delay value) */
112 #define MOVE_DELAY_MIN_SPEED 32
113 #define MOVE_DELAY_NORMAL_SPEED 8
114 #define MOVE_DELAY_HIGH_SPEED 4
115 #define MOVE_DELAY_MAX_SPEED 1
118 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
119 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
121 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
122 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
124 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
125 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
127 /* values for other actions */
128 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
129 #define MOVE_STEPSIZE_MIN (1)
130 #define MOVE_STEPSIZE_MAX (TILEX)
132 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
133 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
135 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
137 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
138 RND(element_info[e].push_delay_random))
139 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
140 RND(element_info[e].drop_delay_random))
141 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
142 RND(element_info[e].move_delay_random))
143 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
144 (element_info[e].move_delay_random))
145 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
146 RND(element_info[e].ce_value_random_initial))
147 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
148 RND((c)->delay_random * (c)->delay_frames))
149 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
150 RND((c)->delay_random))
153 #define GET_VALID_RUNTIME_ELEMENT(e) \
154 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
156 #define GET_VALID_FILE_ELEMENT(e) \
157 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
160 #define GET_TARGET_ELEMENT(e, ch) \
161 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
162 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
163 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
165 #define CAN_GROW_INTO(e) \
166 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
168 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
169 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
172 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
184 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
185 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(e) && \
188 Feld[x][y] == EL_ACID) || \
189 (DONT_COLLIDE_WITH(e) && \
191 !PLAYER_ENEMY_PROTECTED(x, y))))
193 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
196 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
199 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
202 #define ANDROID_CAN_CLONE_FIELD(x, y) \
203 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
204 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
206 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
215 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
223 IS_FOOD_PENGUIN(Feld[x][y])))
224 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
230 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
234 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
235 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
238 #define GROUP_NR(e) ((e) - EL_GROUP_START)
239 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
240 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
242 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
243 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
246 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
248 #define CE_ENTER_FIELD_COND(e, x, y) \
249 (!IS_PLAYER(x, y) && \
250 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
252 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
255 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
256 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
258 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
259 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
260 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
261 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
263 /* game button identifiers */
264 #define GAME_CTRL_ID_STOP 0
265 #define GAME_CTRL_ID_PAUSE 1
266 #define GAME_CTRL_ID_PLAY 2
267 #define SOUND_CTRL_ID_MUSIC 3
268 #define SOUND_CTRL_ID_LOOPS 4
269 #define SOUND_CTRL_ID_SIMPLE 5
271 #define NUM_GAME_BUTTONS 6
274 /* forward declaration for internal use */
276 static void CreateField(int, int, int);
278 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
279 static void AdvanceFrameAndPlayerCounters(int);
281 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
282 static boolean MovePlayer(struct PlayerInfo *, int, int);
283 static void ScrollPlayer(struct PlayerInfo *, int);
284 static void ScrollScreen(struct PlayerInfo *, int);
286 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
288 static void InitBeltMovement(void);
289 static void CloseAllOpenTimegates(void);
290 static void CheckGravityMovement(struct PlayerInfo *);
291 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
292 static void KillPlayerUnlessEnemyProtected(int, int);
293 static void KillPlayerUnlessExplosionProtected(int, int);
295 static void TestIfPlayerTouchesCustomElement(int, int);
296 static void TestIfElementTouchesCustomElement(int, int);
297 static void TestIfElementHitsCustomElement(int, int, int);
299 static void TestIfElementSmashesCustomElement(int, int, int);
302 static void HandleElementChange(int, int, int);
303 static void ExecuteCustomElementAction(int, int, int, int);
304 static boolean ChangeElement(int, int, int, int);
306 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
307 #define CheckTriggeredElementChange(x, y, e, ev) \
308 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
309 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
310 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
311 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
312 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
313 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
314 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
316 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
317 #define CheckElementChange(x, y, e, te, ev) \
318 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
319 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
321 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
322 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
324 static void PlayLevelSound(int, int, int);
325 static void PlayLevelSoundNearest(int, int, int);
326 static void PlayLevelSoundAction(int, int, int);
327 static void PlayLevelSoundElementAction(int, int, int, int);
328 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
329 static void PlayLevelSoundActionIfLoop(int, int, int);
330 static void StopLevelSoundActionIfLoop(int, int, int);
331 static void PlayLevelMusic();
333 static void MapGameButtons();
334 static void HandleGameButtons(struct GadgetInfo *);
336 int AmoebeNachbarNr(int, int);
337 void AmoebeUmwandeln(int, int);
338 void ContinueMoving(int, int);
340 void InitMovDir(int, int);
341 void InitAmoebaNr(int, int);
342 int NewHiScore(void);
344 void TestIfGoodThingHitsBadThing(int, int, int);
345 void TestIfBadThingHitsGoodThing(int, int, int);
346 void TestIfPlayerTouchesBadThing(int, int);
347 void TestIfPlayerRunsIntoBadThing(int, int, int);
348 void TestIfBadThingTouchesPlayer(int, int);
349 void TestIfBadThingRunsIntoPlayer(int, int, int);
350 void TestIfFriendTouchesBadThing(int, int);
351 void TestIfBadThingTouchesFriend(int, int);
352 void TestIfBadThingTouchesOtherBadThing(int, int);
354 void KillPlayer(struct PlayerInfo *);
355 void BuryPlayer(struct PlayerInfo *);
356 void RemovePlayer(struct PlayerInfo *);
358 boolean SnapField(struct PlayerInfo *, int, int);
359 boolean DropElement(struct PlayerInfo *);
362 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
365 /* ------------------------------------------------------------------------- */
366 /* definition of elements that automatically change to other elements after */
367 /* a specified time, eventually calling a function when changing */
368 /* ------------------------------------------------------------------------- */
370 /* forward declaration for changer functions */
371 static void InitBuggyBase(int, int);
372 static void WarnBuggyBase(int, int);
374 static void InitTrap(int, int);
375 static void ActivateTrap(int, int);
376 static void ChangeActiveTrap(int, int);
378 static void InitRobotWheel(int, int);
379 static void RunRobotWheel(int, int);
380 static void StopRobotWheel(int, int);
382 static void InitTimegateWheel(int, int);
383 static void RunTimegateWheel(int, int);
385 static void InitMagicBallDelay(int, int);
386 static void ActivateMagicBall(int, int);
388 static void InitDiagonalMovingElement(int, int);
390 struct ChangingElementInfo
395 void (*pre_change_function)(int x, int y);
396 void (*change_function)(int x, int y);
397 void (*post_change_function)(int x, int y);
400 static struct ChangingElementInfo change_delay_list[] =
451 EL_SWITCHGATE_OPENING,
459 EL_SWITCHGATE_CLOSING,
460 EL_SWITCHGATE_CLOSED,
492 EL_ACID_SPLASH_RIGHT,
501 EL_SP_BUGGY_BASE_ACTIVATING,
508 EL_SP_BUGGY_BASE_ACTIVATING,
509 EL_SP_BUGGY_BASE_ACTIVE,
516 EL_SP_BUGGY_BASE_ACTIVE,
540 EL_ROBOT_WHEEL_ACTIVE,
548 EL_TIMEGATE_SWITCH_ACTIVE,
556 EL_EMC_MAGIC_BALL_ACTIVE,
557 EL_EMC_MAGIC_BALL_ACTIVE,
564 EL_EMC_SPRING_BUMPER_ACTIVE,
565 EL_EMC_SPRING_BUMPER,
572 EL_DIAGONAL_SHRINKING,
585 InitDiagonalMovingElement
601 int push_delay_fixed, push_delay_random;
606 { EL_BALLOON, 0, 0 },
608 { EL_SOKOBAN_OBJECT, 2, 0 },
609 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
610 { EL_SATELLITE, 2, 0 },
611 { EL_SP_DISK_YELLOW, 2, 0 },
613 { EL_UNDEFINED, 0, 0 },
621 move_stepsize_list[] =
623 { EL_AMOEBA_DROP, 2 },
624 { EL_AMOEBA_DROPPING, 2 },
625 { EL_QUICKSAND_FILLING, 1 },
626 { EL_QUICKSAND_EMPTYING, 1 },
627 { EL_MAGIC_WALL_FILLING, 2 },
628 { EL_BD_MAGIC_WALL_FILLING, 2 },
629 { EL_MAGIC_WALL_EMPTYING, 2 },
630 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
640 collect_count_list[] =
643 { EL_BD_DIAMOND, 1 },
644 { EL_EMERALD_YELLOW, 1 },
645 { EL_EMERALD_RED, 1 },
646 { EL_EMERALD_PURPLE, 1 },
648 { EL_SP_INFOTRON, 1 },
660 access_direction_list[] =
662 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
663 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
664 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
665 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
666 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
667 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
668 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
669 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
670 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
671 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
672 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
674 { EL_SP_PORT_LEFT, MV_RIGHT },
675 { EL_SP_PORT_RIGHT, MV_LEFT },
676 { EL_SP_PORT_UP, MV_DOWN },
677 { EL_SP_PORT_DOWN, MV_UP },
678 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
679 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
680 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
681 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
682 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
683 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
684 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
685 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
686 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
687 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
688 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
689 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
690 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
691 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
692 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
694 { EL_UNDEFINED, MV_NONE }
697 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
699 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
700 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
701 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
702 IS_JUST_CHANGING(x, y))
704 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
706 /* static variables for playfield scan mode (scanning forward or backward) */
707 static int playfield_scan_start_x = 0;
708 static int playfield_scan_start_y = 0;
709 static int playfield_scan_delta_x = 1;
710 static int playfield_scan_delta_y = 1;
712 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
713 (y) >= 0 && (y) <= lev_fieldy - 1; \
714 (y) += playfield_scan_delta_y) \
715 for ((x) = playfield_scan_start_x; \
716 (x) >= 0 && (x) <= lev_fieldx - 1; \
717 (x) += playfield_scan_delta_x) \
720 void DEBUG_SetMaximumDynamite()
724 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
725 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
726 local_player->inventory_element[local_player->inventory_size++] =
731 static void InitPlayfieldScanModeVars()
733 if (game.use_reverse_scan_direction)
735 playfield_scan_start_x = lev_fieldx - 1;
736 playfield_scan_start_y = lev_fieldy - 1;
738 playfield_scan_delta_x = -1;
739 playfield_scan_delta_y = -1;
743 playfield_scan_start_x = 0;
744 playfield_scan_start_y = 0;
746 playfield_scan_delta_x = 1;
747 playfield_scan_delta_y = 1;
751 static void InitPlayfieldScanMode(int mode)
753 game.use_reverse_scan_direction =
754 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
756 InitPlayfieldScanModeVars();
759 static int get_move_delay_from_stepsize(int move_stepsize)
762 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
764 /* make sure that stepsize value is always a power of 2 */
765 move_stepsize = (1 << log_2(move_stepsize));
767 return TILEX / move_stepsize;
770 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
773 int move_delay = get_move_delay_from_stepsize(move_stepsize);
774 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
776 /* do no immediately change move delay -- the player might just be moving */
777 player->move_delay_value_next = move_delay;
779 /* information if player can move must be set separately */
780 player->cannot_move = cannot_move;
784 player->move_delay = game.initial_move_delay;
785 player->move_delay_value = game.initial_move_delay_value;
787 player->move_delay_value_next = -1;
789 player->move_delay_reset_counter = 0;
793 void GetPlayerConfig()
795 if (!audio.sound_available)
796 setup.sound_simple = FALSE;
798 if (!audio.loops_available)
799 setup.sound_loops = FALSE;
801 if (!audio.music_available)
802 setup.sound_music = FALSE;
804 if (!video.fullscreen_available)
805 setup.fullscreen = FALSE;
807 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
809 SetAudioMode(setup.sound);
813 static int getBeltNrFromBeltElement(int element)
815 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
816 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
817 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
820 static int getBeltNrFromBeltActiveElement(int element)
822 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
823 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
824 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
827 static int getBeltNrFromBeltSwitchElement(int element)
829 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
830 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
831 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
834 static int getBeltDirNrFromBeltSwitchElement(int element)
836 static int belt_base_element[4] =
838 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
839 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
840 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
841 EL_CONVEYOR_BELT_4_SWITCH_LEFT
844 int belt_nr = getBeltNrFromBeltSwitchElement(element);
845 int belt_dir_nr = element - belt_base_element[belt_nr];
847 return (belt_dir_nr % 3);
850 static int getBeltDirFromBeltSwitchElement(int element)
852 static int belt_move_dir[3] =
859 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
861 return belt_move_dir[belt_dir_nr];
864 static void InitPlayerField(int x, int y, int element, boolean init_game)
866 if (element == EL_SP_MURPHY)
870 if (stored_player[0].present)
872 Feld[x][y] = EL_SP_MURPHY_CLONE;
878 stored_player[0].use_murphy = TRUE;
880 if (!level.use_artwork_element[0])
881 stored_player[0].artwork_element = EL_SP_MURPHY;
884 Feld[x][y] = EL_PLAYER_1;
890 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
891 int jx = player->jx, jy = player->jy;
893 player->present = TRUE;
895 player->block_last_field = (element == EL_SP_MURPHY ?
896 level.sp_block_last_field :
897 level.block_last_field);
899 /* ---------- initialize player's last field block delay --------------- */
901 /* always start with reliable default value (no adjustment needed) */
902 player->block_delay_adjustment = 0;
904 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
905 if (player->block_last_field && element == EL_SP_MURPHY)
906 player->block_delay_adjustment = 1;
908 /* special case 2: in game engines before 3.1.1, blocking was different */
909 if (game.use_block_last_field_bug)
910 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
912 if (!options.network || player->connected)
914 player->active = TRUE;
916 /* remove potentially duplicate players */
917 if (StorePlayer[jx][jy] == Feld[x][y])
918 StorePlayer[jx][jy] = 0;
920 StorePlayer[x][y] = Feld[x][y];
924 printf("Player %d activated.\n", player->element_nr);
925 printf("[Local player is %d and currently %s.]\n",
926 local_player->element_nr,
927 local_player->active ? "active" : "not active");
931 Feld[x][y] = EL_EMPTY;
933 player->jx = player->last_jx = x;
934 player->jy = player->last_jy = y;
938 static void InitField(int x, int y, boolean init_game)
940 int element = Feld[x][y];
949 InitPlayerField(x, y, element, init_game);
952 case EL_SOKOBAN_FIELD_PLAYER:
953 element = Feld[x][y] = EL_PLAYER_1;
954 InitField(x, y, init_game);
956 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
957 InitField(x, y, init_game);
960 case EL_SOKOBAN_FIELD_EMPTY:
961 local_player->sokobanfields_still_needed++;
965 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
966 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
967 else if (x > 0 && Feld[x-1][y] == EL_ACID)
968 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
969 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
970 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
971 else if (y > 0 && Feld[x][y-1] == EL_ACID)
972 Feld[x][y] = EL_ACID_POOL_BOTTOM;
973 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
974 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
983 case EL_SPACESHIP_RIGHT:
984 case EL_SPACESHIP_UP:
985 case EL_SPACESHIP_LEFT:
986 case EL_SPACESHIP_DOWN:
987 case EL_BD_BUTTERFLY:
988 case EL_BD_BUTTERFLY_RIGHT:
989 case EL_BD_BUTTERFLY_UP:
990 case EL_BD_BUTTERFLY_LEFT:
991 case EL_BD_BUTTERFLY_DOWN:
993 case EL_BD_FIREFLY_RIGHT:
994 case EL_BD_FIREFLY_UP:
995 case EL_BD_FIREFLY_LEFT:
996 case EL_BD_FIREFLY_DOWN:
997 case EL_PACMAN_RIGHT:
1000 case EL_PACMAN_DOWN:
1002 case EL_YAMYAM_LEFT:
1003 case EL_YAMYAM_RIGHT:
1005 case EL_YAMYAM_DOWN:
1006 case EL_DARK_YAMYAM:
1009 case EL_SP_SNIKSNAK:
1010 case EL_SP_ELECTRON:
1019 case EL_AMOEBA_FULL:
1024 case EL_AMOEBA_DROP:
1025 if (y == lev_fieldy - 1)
1027 Feld[x][y] = EL_AMOEBA_GROWING;
1028 Store[x][y] = EL_AMOEBA_WET;
1032 case EL_DYNAMITE_ACTIVE:
1033 case EL_SP_DISK_RED_ACTIVE:
1034 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1035 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1036 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1037 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1038 MovDelay[x][y] = 96;
1041 case EL_EM_DYNAMITE_ACTIVE:
1042 MovDelay[x][y] = 32;
1046 local_player->lights_still_needed++;
1050 local_player->friends_still_needed++;
1055 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1058 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1059 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1060 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1061 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1062 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1063 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1064 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1065 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1066 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1067 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1068 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1069 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1072 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1073 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1074 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1076 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1078 game.belt_dir[belt_nr] = belt_dir;
1079 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1081 else /* more than one switch -- set it like the first switch */
1083 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1088 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1090 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1093 case EL_LIGHT_SWITCH_ACTIVE:
1095 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1098 case EL_EMC_MAGIC_BALL:
1099 if (game.ball_state)
1100 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1103 case EL_EMC_MAGIC_BALL_SWITCH:
1104 if (game.ball_state)
1105 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1110 if (IS_CUSTOM_ELEMENT(element))
1112 if (CAN_MOVE(element))
1115 #if USE_NEW_CUSTOM_VALUE
1116 if (!element_info[element].use_last_ce_value || init_game)
1117 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1121 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1124 else if (IS_GROUP_ELEMENT(element))
1126 struct ElementGroupInfo *group = element_info[element].group;
1127 int last_anim_random_frame = gfx.anim_random_frame;
1130 if (group->choice_mode == ANIM_RANDOM)
1131 gfx.anim_random_frame = RND(group->num_elements_resolved);
1133 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1134 group->choice_mode, 0,
1137 if (group->choice_mode == ANIM_RANDOM)
1138 gfx.anim_random_frame = last_anim_random_frame;
1140 group->choice_pos++;
1142 Feld[x][y] = group->element_resolved[element_pos];
1144 InitField(x, y, init_game);
1151 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1156 #if USE_NEW_CUSTOM_VALUE
1159 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1161 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1169 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1171 InitField(x, y, init_game);
1173 /* not needed to call InitMovDir() -- already done by InitField()! */
1174 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1175 CAN_MOVE(Feld[x][y]))
1179 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1181 int old_element = Feld[x][y];
1183 InitField(x, y, init_game);
1185 /* not needed to call InitMovDir() -- already done by InitField()! */
1186 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1187 CAN_MOVE(old_element) &&
1188 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1191 /* this case is in fact a combination of not less than three bugs:
1192 first, it calls InitMovDir() for elements that can move, although this is
1193 already done by InitField(); then, it checks the element that was at this
1194 field _before_ the call to InitField() (which can change it); lastly, it
1195 was not called for "mole with direction" elements, which were treated as
1196 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1200 inline void DrawGameValue_Emeralds(int value)
1202 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1205 inline void DrawGameValue_Dynamite(int value)
1207 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1210 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1214 /* currently only 4 of 8 possible keys are displayed */
1215 for (i = 0; i < STD_NUM_KEYS; i++)
1218 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1219 el2edimg(EL_KEY_1 + i));
1221 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1222 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1223 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1227 inline void DrawGameValue_Score(int value)
1229 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1232 inline void DrawGameValue_Time(int value)
1235 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1237 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1240 inline void DrawGameValue_Level(int value)
1243 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1246 /* misuse area for displaying emeralds to draw bigger level number */
1247 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1248 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1250 /* now copy it to the area for displaying level number */
1251 BlitBitmap(drawto, drawto,
1252 DX_EMERALDS, DY_EMERALDS + 1,
1253 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1254 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1255 DX_LEVEL - 1, DY_LEVEL + 1);
1257 /* restore the area for displaying emeralds */
1258 DrawGameValue_Emeralds(local_player->gems_still_needed);
1260 /* yes, this is all really ugly :-) */
1264 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1267 int key[MAX_NUM_KEYS];
1270 for (i = 0; i < MAX_NUM_KEYS; i++)
1271 key[i] = key_bits & (1 << i);
1273 DrawGameValue_Level(level_nr);
1275 DrawGameValue_Emeralds(emeralds);
1276 DrawGameValue_Dynamite(dynamite);
1277 DrawGameValue_Score(score);
1278 DrawGameValue_Time(time);
1280 DrawGameValue_Keys(key);
1283 void DrawGameDoorValues()
1287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1289 DrawGameDoorValues_EM();
1294 DrawGameValue_Level(level_nr);
1296 DrawGameValue_Emeralds(local_player->gems_still_needed);
1297 DrawGameValue_Dynamite(local_player->inventory_size);
1298 DrawGameValue_Score(local_player->score);
1299 DrawGameValue_Time(TimeLeft);
1301 for (i = 0; i < MAX_PLAYERS; i++)
1302 DrawGameValue_Keys(stored_player[i].key);
1306 static void resolve_group_element(int group_element, int recursion_depth)
1308 static int group_nr;
1309 static struct ElementGroupInfo *group;
1310 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1313 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1315 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1316 group_element - EL_GROUP_START + 1);
1318 /* replace element which caused too deep recursion by question mark */
1319 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1324 if (recursion_depth == 0) /* initialization */
1326 group = element_info[group_element].group;
1327 group_nr = group_element - EL_GROUP_START;
1329 group->num_elements_resolved = 0;
1330 group->choice_pos = 0;
1333 for (i = 0; i < actual_group->num_elements; i++)
1335 int element = actual_group->element[i];
1337 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1340 if (IS_GROUP_ELEMENT(element))
1341 resolve_group_element(element, recursion_depth + 1);
1344 group->element_resolved[group->num_elements_resolved++] = element;
1345 element_info[element].in_group[group_nr] = TRUE;
1352 =============================================================================
1354 -----------------------------------------------------------------------------
1355 initialize game engine due to level / tape version number
1356 =============================================================================
1359 static void InitGameEngine()
1361 int i, j, k, l, x, y;
1363 /* set game engine from tape file when re-playing, else from level file */
1364 game.engine_version = (tape.playing ? tape.engine_version :
1365 level.game_version);
1367 /* ---------------------------------------------------------------------- */
1368 /* set flags for bugs and changes according to active game engine version */
1369 /* ---------------------------------------------------------------------- */
1372 Summary of bugfix/change:
1373 Fixed handling for custom elements that change when pushed by the player.
1375 Fixed/changed in version:
1379 Before 3.1.0, custom elements that "change when pushing" changed directly
1380 after the player started pushing them (until then handled in "DigField()").
1381 Since 3.1.0, these custom elements are not changed until the "pushing"
1382 move of the element is finished (now handled in "ContinueMoving()").
1384 Affected levels/tapes:
1385 The first condition is generally needed for all levels/tapes before version
1386 3.1.0, which might use the old behaviour before it was changed; known tapes
1387 that are affected are some tapes from the level set "Walpurgis Gardens" by
1389 The second condition is an exception from the above case and is needed for
1390 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1391 above (including some development versions of 3.1.0), but before it was
1392 known that this change would break tapes like the above and was fixed in
1393 3.1.1, so that the changed behaviour was active although the engine version
1394 while recording maybe was before 3.1.0. There is at least one tape that is
1395 affected by this exception, which is the tape for the one-level set "Bug
1396 Machine" by Juergen Bonhagen.
1399 game.use_change_when_pushing_bug =
1400 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1402 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1403 tape.game_version < VERSION_IDENT(3,1,1,0)));
1406 Summary of bugfix/change:
1407 Fixed handling for blocking the field the player leaves when moving.
1409 Fixed/changed in version:
1413 Before 3.1.1, when "block last field when moving" was enabled, the field
1414 the player is leaving when moving was blocked for the time of the move,
1415 and was directly unblocked afterwards. This resulted in the last field
1416 being blocked for exactly one less than the number of frames of one player
1417 move. Additionally, even when blocking was disabled, the last field was
1418 blocked for exactly one frame.
1419 Since 3.1.1, due to changes in player movement handling, the last field
1420 is not blocked at all when blocking is disabled. When blocking is enabled,
1421 the last field is blocked for exactly the number of frames of one player
1422 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1423 last field is blocked for exactly one more than the number of frames of
1426 Affected levels/tapes:
1427 (!!! yet to be determined -- probably many !!!)
1430 game.use_block_last_field_bug =
1431 (game.engine_version < VERSION_IDENT(3,1,1,0));
1434 Summary of bugfix/change:
1435 Changed behaviour of CE changes with multiple changes per single frame.
1437 Fixed/changed in version:
1441 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1442 This resulted in race conditions where CEs seem to behave strange in some
1443 situations (where triggered CE changes were just skipped because there was
1444 already a CE change on that tile in the playfield in that engine frame).
1445 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1446 (The number of changes per frame must be limited in any case, because else
1447 it is easily possible to define CE changes that would result in an infinite
1448 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1449 should be set large enough so that it would only be reached in cases where
1450 the corresponding CE change conditions run into a loop. Therefore, it seems
1451 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1452 maximal number of change pages for custom elements.)
1454 Affected levels/tapes:
1458 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1459 game.max_num_changes_per_frame = 1;
1461 game.max_num_changes_per_frame =
1462 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1465 /* ---------------------------------------------------------------------- */
1467 /* default scan direction: scan playfield from top/left to bottom/right */
1468 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1470 /* dynamically adjust element properties according to game engine version */
1471 InitElementPropertiesEngine(game.engine_version);
1474 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1475 printf(" tape version == %06d [%s] [file: %06d]\n",
1476 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1478 printf(" => game.engine_version == %06d\n", game.engine_version);
1482 /* ---------- recursively resolve group elements ------------------------- */
1484 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1485 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1486 element_info[i].in_group[j] = FALSE;
1488 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1489 resolve_group_element(EL_GROUP_START + i, 0);
1492 /* ---------- initialize player's initial move delay --------------------- */
1495 /* dynamically adjust player properties according to level information */
1496 game.initial_move_delay_value =
1497 get_move_delay_from_stepsize(level.initial_player_stepsize);
1499 /* dynamically adjust player properties according to level information */
1500 game.initial_move_delay_value =
1501 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1504 /* dynamically adjust player properties according to game engine version */
1505 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1506 game.initial_move_delay_value : 0);
1508 /* ---------- initialize player's initial push delay --------------------- */
1510 /* dynamically adjust player properties according to game engine version */
1511 game.initial_push_delay_value =
1512 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1514 /* ---------- initialize changing elements ------------------------------- */
1516 /* initialize changing elements information */
1517 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1519 struct ElementInfo *ei = &element_info[i];
1521 /* this pointer might have been changed in the level editor */
1522 ei->change = &ei->change_page[0];
1524 if (!IS_CUSTOM_ELEMENT(i))
1526 ei->change->target_element = EL_EMPTY_SPACE;
1527 ei->change->delay_fixed = 0;
1528 ei->change->delay_random = 0;
1529 ei->change->delay_frames = 1;
1532 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1534 ei->has_change_event[j] = FALSE;
1536 ei->event_page_nr[j] = 0;
1537 ei->event_page[j] = &ei->change_page[0];
1541 /* add changing elements from pre-defined list */
1542 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1544 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1545 struct ElementInfo *ei = &element_info[ch_delay->element];
1547 ei->change->target_element = ch_delay->target_element;
1548 ei->change->delay_fixed = ch_delay->change_delay;
1550 ei->change->pre_change_function = ch_delay->pre_change_function;
1551 ei->change->change_function = ch_delay->change_function;
1552 ei->change->post_change_function = ch_delay->post_change_function;
1554 ei->change->can_change = TRUE;
1555 ei->change->can_change_or_has_action = TRUE;
1557 ei->has_change_event[CE_DELAY] = TRUE;
1559 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1560 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1563 /* ---------- initialize internal run-time variables ------------- */
1565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1567 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1569 for (j = 0; j < ei->num_change_pages; j++)
1571 ei->change_page[j].can_change_or_has_action =
1572 (ei->change_page[j].can_change |
1573 ei->change_page[j].has_action);
1577 /* add change events from custom element configuration */
1578 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1580 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1582 for (j = 0; j < ei->num_change_pages; j++)
1584 if (!ei->change_page[j].can_change_or_has_action)
1587 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1589 /* only add event page for the first page found with this event */
1590 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1592 ei->has_change_event[k] = TRUE;
1594 ei->event_page_nr[k] = j;
1595 ei->event_page[k] = &ei->change_page[j];
1601 /* ---------- initialize run-time trigger player and element ------------- */
1603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1605 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1607 for (j = 0; j < ei->num_change_pages; j++)
1609 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1610 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1611 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1612 ei->change_page[j].actual_trigger_ce_value = 0;
1616 /* ---------- initialize trigger events ---------------------------------- */
1618 /* initialize trigger events information */
1619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1621 trigger_events[i][j] = FALSE;
1623 /* add trigger events from element change event properties */
1624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1626 struct ElementInfo *ei = &element_info[i];
1628 for (j = 0; j < ei->num_change_pages; j++)
1630 if (!ei->change_page[j].can_change_or_has_action)
1633 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1635 int trigger_element = ei->change_page[j].trigger_element;
1637 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1639 if (ei->change_page[j].has_event[k])
1641 if (IS_GROUP_ELEMENT(trigger_element))
1643 struct ElementGroupInfo *group =
1644 element_info[trigger_element].group;
1646 for (l = 0; l < group->num_elements_resolved; l++)
1647 trigger_events[group->element_resolved[l]][k] = TRUE;
1650 trigger_events[trigger_element][k] = TRUE;
1657 /* ---------- initialize push delay -------------------------------------- */
1659 /* initialize push delay values to default */
1660 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1662 if (!IS_CUSTOM_ELEMENT(i))
1664 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1665 element_info[i].push_delay_random = game.default_push_delay_random;
1669 /* set push delay value for certain elements from pre-defined list */
1670 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1672 int e = push_delay_list[i].element;
1674 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1675 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1678 /* set push delay value for Supaplex elements for newer engine versions */
1679 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1681 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1683 if (IS_SP_ELEMENT(i))
1685 /* set SP push delay to just enough to push under a falling zonk */
1686 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1688 element_info[i].push_delay_fixed = delay;
1689 element_info[i].push_delay_random = 0;
1694 /* ---------- initialize move stepsize ----------------------------------- */
1696 /* initialize move stepsize values to default */
1697 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1698 if (!IS_CUSTOM_ELEMENT(i))
1699 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1701 /* set move stepsize value for certain elements from pre-defined list */
1702 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1704 int e = move_stepsize_list[i].element;
1706 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1709 /* ---------- initialize collect score ----------------------------------- */
1711 /* initialize collect score values for custom elements from initial value */
1712 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1713 if (IS_CUSTOM_ELEMENT(i))
1714 element_info[i].collect_score = element_info[i].collect_score_initial;
1716 /* ---------- initialize collect count ----------------------------------- */
1718 /* initialize collect count values for non-custom elements */
1719 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1720 if (!IS_CUSTOM_ELEMENT(i))
1721 element_info[i].collect_count_initial = 0;
1723 /* add collect count values for all elements from pre-defined list */
1724 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1725 element_info[collect_count_list[i].element].collect_count_initial =
1726 collect_count_list[i].count;
1728 /* ---------- initialize access direction -------------------------------- */
1730 /* initialize access direction values to default (access from every side) */
1731 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1732 if (!IS_CUSTOM_ELEMENT(i))
1733 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1735 /* set access direction value for certain elements from pre-defined list */
1736 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1737 element_info[access_direction_list[i].element].access_direction =
1738 access_direction_list[i].direction;
1740 /* ---------- initialize explosion content ------------------------------- */
1741 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1743 if (IS_CUSTOM_ELEMENT(i))
1746 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1748 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1750 element_info[i].content.e[x][y] =
1751 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1752 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1753 i == EL_PLAYER_3 ? EL_EMERALD :
1754 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1755 i == EL_MOLE ? EL_EMERALD_RED :
1756 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1757 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1758 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1759 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1760 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1761 i == EL_WALL_EMERALD ? EL_EMERALD :
1762 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1763 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1764 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1765 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1766 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1767 i == EL_WALL_PEARL ? EL_PEARL :
1768 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1774 int get_num_special_action(int element, int action_first, int action_last)
1776 int num_special_action = 0;
1779 for (i = action_first; i <= action_last; i++)
1781 boolean found = FALSE;
1783 for (j = 0; j < NUM_DIRECTIONS; j++)
1784 if (el_act_dir2img(element, i, j) !=
1785 el_act_dir2img(element, ACTION_DEFAULT, j))
1789 num_special_action++;
1795 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1798 return num_special_action;
1802 =============================================================================
1804 -----------------------------------------------------------------------------
1805 initialize and start new game
1806 =============================================================================
1811 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1812 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1813 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1818 /* don't play tapes over network */
1819 network_playing = (options.network && !tape.playing);
1821 for (i = 0; i < MAX_PLAYERS; i++)
1823 struct PlayerInfo *player = &stored_player[i];
1825 player->index_nr = i;
1826 player->index_bit = (1 << i);
1827 player->element_nr = EL_PLAYER_1 + i;
1829 player->present = FALSE;
1830 player->active = FALSE;
1833 player->effective_action = 0;
1834 player->programmed_action = 0;
1837 player->gems_still_needed = level.gems_needed;
1838 player->sokobanfields_still_needed = 0;
1839 player->lights_still_needed = 0;
1840 player->friends_still_needed = 0;
1842 for (j = 0; j < MAX_NUM_KEYS; j++)
1843 player->key[j] = FALSE;
1845 player->dynabomb_count = 0;
1846 player->dynabomb_size = 1;
1847 player->dynabombs_left = 0;
1848 player->dynabomb_xl = FALSE;
1850 player->MovDir = MV_NONE;
1853 player->GfxDir = MV_NONE;
1854 player->GfxAction = ACTION_DEFAULT;
1856 player->StepFrame = 0;
1858 player->use_murphy = FALSE;
1859 player->artwork_element =
1860 (level.use_artwork_element[i] ? level.artwork_element[i] :
1861 player->element_nr);
1863 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1864 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1866 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1868 player->actual_frame_counter = 0;
1870 player->step_counter = 0;
1872 player->last_move_dir = MV_NONE;
1874 player->is_waiting = FALSE;
1875 player->is_moving = FALSE;
1876 player->is_auto_moving = FALSE;
1877 player->is_digging = FALSE;
1878 player->is_snapping = FALSE;
1879 player->is_collecting = FALSE;
1880 player->is_pushing = FALSE;
1881 player->is_switching = FALSE;
1882 player->is_dropping = FALSE;
1883 player->is_dropping_pressed = FALSE;
1885 player->is_bored = FALSE;
1886 player->is_sleeping = FALSE;
1888 player->frame_counter_bored = -1;
1889 player->frame_counter_sleeping = -1;
1891 player->anim_delay_counter = 0;
1892 player->post_delay_counter = 0;
1894 player->dir_waiting = MV_NONE;
1895 player->action_waiting = ACTION_DEFAULT;
1896 player->last_action_waiting = ACTION_DEFAULT;
1897 player->special_action_bored = ACTION_DEFAULT;
1898 player->special_action_sleeping = ACTION_DEFAULT;
1901 /* cannot be set here -- could be modified in Init[Player]Field() below */
1903 /* set number of special actions for bored and sleeping animation */
1904 player->num_special_action_bored =
1905 get_num_special_action(player->artwork_element,
1906 ACTION_BORING_1, ACTION_BORING_LAST);
1907 player->num_special_action_sleeping =
1908 get_num_special_action(player->artwork_element,
1909 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1912 player->switch_x = -1;
1913 player->switch_y = -1;
1915 player->drop_x = -1;
1916 player->drop_y = -1;
1918 player->show_envelope = 0;
1921 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1923 player->move_delay = game.initial_move_delay;
1924 player->move_delay_value = game.initial_move_delay_value;
1926 player->move_delay_value_next = -1;
1928 player->move_delay_reset_counter = 0;
1930 player->cannot_move = FALSE;
1933 player->push_delay = -1; /* initialized when pushing starts */
1934 player->push_delay_value = game.initial_push_delay_value;
1936 player->drop_delay = 0;
1937 player->drop_pressed_delay = 0;
1939 player->last_jx = player->last_jy = 0;
1940 player->jx = player->jy = 0;
1942 player->shield_normal_time_left = 0;
1943 player->shield_deadly_time_left = 0;
1945 player->inventory_infinite_element = EL_UNDEFINED;
1946 player->inventory_size = 0;
1948 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1949 SnapField(player, 0, 0);
1951 player->LevelSolved = FALSE;
1952 player->GameOver = FALSE;
1955 network_player_action_received = FALSE;
1957 #if defined(NETWORK_AVALIABLE)
1958 /* initial null action */
1959 if (network_playing)
1960 SendToServer_MovePlayer(MV_NONE);
1969 TimeLeft = level.time;
1972 ScreenMovDir = MV_NONE;
1976 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1978 AllPlayersGone = FALSE;
1980 game.yamyam_content_nr = 0;
1981 game.magic_wall_active = FALSE;
1982 game.magic_wall_time_left = 0;
1983 game.light_time_left = 0;
1984 game.timegate_time_left = 0;
1985 game.switchgate_pos = 0;
1986 game.wind_direction = level.wind_direction_initial;
1987 game.gravity = level.initial_gravity;
1988 game.explosions_delayed = TRUE;
1990 game.lenses_time_left = 0;
1991 game.magnify_time_left = 0;
1993 game.ball_state = level.ball_state_initial;
1994 game.ball_content_nr = 0;
1996 game.envelope_active = FALSE;
1998 game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
1999 game.set_centered_player = FALSE;
2001 for (i = 0; i < NUM_BELTS; i++)
2003 game.belt_dir[i] = MV_NONE;
2004 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2007 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2008 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2011 SCAN_PLAYFIELD(x, y)
2013 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2016 Feld[x][y] = level.field[x][y];
2017 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2018 ChangeDelay[x][y] = 0;
2019 ChangePage[x][y] = -1;
2020 #if USE_NEW_CUSTOM_VALUE
2021 CustomValue[x][y] = 0; /* initialized in InitField() */
2023 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2025 WasJustMoving[x][y] = 0;
2026 WasJustFalling[x][y] = 0;
2027 CheckCollision[x][y] = 0;
2029 Pushed[x][y] = FALSE;
2031 ChangeCount[x][y] = 0;
2032 ChangeEvent[x][y] = -1;
2034 ExplodePhase[x][y] = 0;
2035 ExplodeDelay[x][y] = 0;
2036 ExplodeField[x][y] = EX_TYPE_NONE;
2038 RunnerVisit[x][y] = 0;
2039 PlayerVisit[x][y] = 0;
2042 GfxRandom[x][y] = INIT_GFX_RANDOM();
2043 GfxElement[x][y] = EL_UNDEFINED;
2044 GfxAction[x][y] = ACTION_DEFAULT;
2045 GfxDir[x][y] = MV_NONE;
2049 SCAN_PLAYFIELD(x, y)
2051 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2054 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2056 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2058 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2061 InitField(x, y, TRUE);
2066 for (i = 0; i < MAX_PLAYERS; i++)
2068 struct PlayerInfo *player = &stored_player[i];
2071 /* set number of special actions for bored and sleeping animation */
2072 player->num_special_action_bored =
2073 get_num_special_action(player->artwork_element,
2074 ACTION_BORING_1, ACTION_BORING_LAST);
2075 player->num_special_action_sleeping =
2076 get_num_special_action(player->artwork_element,
2077 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2082 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2083 emulate_sb ? EMU_SOKOBAN :
2084 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2086 #if USE_NEW_ALL_SLIPPERY
2087 /* initialize type of slippery elements */
2088 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2090 if (!IS_CUSTOM_ELEMENT(i))
2092 /* default: elements slip down either to the left or right randomly */
2093 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2095 /* SP style elements prefer to slip down on the left side */
2096 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2097 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2099 /* BD style elements prefer to slip down on the left side */
2100 if (game.emulation == EMU_BOULDERDASH)
2101 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2106 /* initialize explosion and ignition delay */
2107 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2109 if (!IS_CUSTOM_ELEMENT(i))
2112 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2113 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2114 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2115 int last_phase = (num_phase + 1) * delay;
2116 int half_phase = (num_phase / 2) * delay;
2118 element_info[i].explosion_delay = last_phase - 1;
2119 element_info[i].ignition_delay = half_phase;
2121 if (i == EL_BLACK_ORB)
2122 element_info[i].ignition_delay = 1;
2126 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2127 element_info[i].explosion_delay = 1;
2129 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2130 element_info[i].ignition_delay = 1;
2134 /* correct non-moving belts to start moving left */
2135 for (i = 0; i < NUM_BELTS; i++)
2136 if (game.belt_dir[i] == MV_NONE)
2137 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2139 /* check if any connected player was not found in playfield */
2140 for (i = 0; i < MAX_PLAYERS; i++)
2142 struct PlayerInfo *player = &stored_player[i];
2144 if (player->connected && !player->present)
2146 for (j = 0; j < MAX_PLAYERS; j++)
2148 struct PlayerInfo *some_player = &stored_player[j];
2149 int jx = some_player->jx, jy = some_player->jy;
2151 /* assign first free player found that is present in the playfield */
2152 if (some_player->present && !some_player->connected)
2154 player->present = TRUE;
2155 player->active = TRUE;
2157 some_player->present = FALSE;
2158 some_player->active = FALSE;
2161 player->element_nr = some_player->element_nr;
2164 player->artwork_element = some_player->artwork_element;
2166 player->block_last_field = some_player->block_last_field;
2167 player->block_delay_adjustment = some_player->block_delay_adjustment;
2169 StorePlayer[jx][jy] = player->element_nr;
2170 player->jx = player->last_jx = jx;
2171 player->jy = player->last_jy = jy;
2181 /* when playing a tape, eliminate all players who do not participate */
2183 for (i = 0; i < MAX_PLAYERS; i++)
2185 if (stored_player[i].active && !tape.player_participates[i])
2187 struct PlayerInfo *player = &stored_player[i];
2188 int jx = player->jx, jy = player->jy;
2190 player->active = FALSE;
2191 StorePlayer[jx][jy] = 0;
2192 Feld[jx][jy] = EL_EMPTY;
2196 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2198 /* when in single player mode, eliminate all but the first active player */
2200 for (i = 0; i < MAX_PLAYERS; i++)
2202 if (stored_player[i].active)
2204 for (j = i + 1; j < MAX_PLAYERS; j++)
2206 if (stored_player[j].active)
2208 struct PlayerInfo *player = &stored_player[j];
2209 int jx = player->jx, jy = player->jy;
2211 player->active = FALSE;
2212 player->present = FALSE;
2214 StorePlayer[jx][jy] = 0;
2215 Feld[jx][jy] = EL_EMPTY;
2222 /* when recording the game, store which players take part in the game */
2225 for (i = 0; i < MAX_PLAYERS; i++)
2226 if (stored_player[i].active)
2227 tape.player_participates[i] = TRUE;
2232 for (i = 0; i < MAX_PLAYERS; i++)
2234 struct PlayerInfo *player = &stored_player[i];
2236 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2241 if (local_player == player)
2242 printf("Player %d is local player.\n", i+1);
2246 if (BorderElement == EL_EMPTY)
2249 SBX_Right = lev_fieldx - SCR_FIELDX;
2251 SBY_Lower = lev_fieldy - SCR_FIELDY;
2256 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2258 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2261 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2262 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2264 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2265 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2267 /* if local player not found, look for custom element that might create
2268 the player (make some assumptions about the right custom element) */
2269 if (!local_player->present)
2271 int start_x = 0, start_y = 0;
2272 int found_rating = 0;
2273 int found_element = EL_UNDEFINED;
2274 int player_nr = local_player->index_nr;
2277 SCAN_PLAYFIELD(x, y)
2279 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2282 int element = Feld[x][y];
2287 if (level.use_start_element[player_nr] &&
2288 level.start_element[player_nr] == element &&
2295 found_element = element;
2298 if (!IS_CUSTOM_ELEMENT(element))
2301 if (CAN_CHANGE(element))
2303 for (i = 0; i < element_info[element].num_change_pages; i++)
2305 /* check for player created from custom element as single target */
2306 content = element_info[element].change_page[i].target_element;
2307 is_player = ELEM_IS_PLAYER(content);
2309 if (is_player && (found_rating < 3 || element < found_element))
2315 found_element = element;
2320 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2322 /* check for player created from custom element as explosion content */
2323 content = element_info[element].content.e[xx][yy];
2324 is_player = ELEM_IS_PLAYER(content);
2326 if (is_player && (found_rating < 2 || element < found_element))
2328 start_x = x + xx - 1;
2329 start_y = y + yy - 1;
2332 found_element = element;
2335 if (!CAN_CHANGE(element))
2338 for (i = 0; i < element_info[element].num_change_pages; i++)
2340 /* check for player created from custom element as extended target */
2342 element_info[element].change_page[i].target_content.e[xx][yy];
2344 is_player = ELEM_IS_PLAYER(content);
2346 if (is_player && (found_rating < 1 || element < found_element))
2348 start_x = x + xx - 1;
2349 start_y = y + yy - 1;
2352 found_element = element;
2358 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2359 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2362 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2363 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2368 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2369 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2370 local_player->jx - MIDPOSX);
2372 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2373 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2374 local_player->jy - MIDPOSY);
2377 if (!game.restart_level)
2378 CloseDoor(DOOR_CLOSE_1);
2380 /* !!! FIX THIS (START) !!! */
2381 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2383 InitGameEngine_EM();
2390 /* after drawing the level, correct some elements */
2391 if (game.timegate_time_left == 0)
2392 CloseAllOpenTimegates();
2394 if (setup.soft_scrolling)
2395 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2397 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2400 /* !!! FIX THIS (END) !!! */
2402 if (!game.restart_level)
2404 /* copy default game door content to main double buffer */
2405 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2406 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2409 DrawGameDoorValues();
2411 if (!game.restart_level)
2415 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2416 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2417 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2421 /* copy actual game door content to door double buffer for OpenDoor() */
2422 BlitBitmap(drawto, bitmap_db_door,
2423 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2425 OpenDoor(DOOR_OPEN_ALL);
2427 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2429 if (setup.sound_music)
2432 KeyboardAutoRepeatOffUnlessAutoplay();
2436 for (i = 0; i < MAX_PLAYERS; i++)
2437 printf("Player %d %sactive.\n",
2438 i + 1, (stored_player[i].active ? "" : "not "));
2442 game.restart_level = FALSE;
2445 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2447 /* this is used for non-R'n'D game engines to update certain engine values */
2449 /* needed to determine if sounds are played within the visible screen area */
2450 scroll_x = actual_scroll_x;
2451 scroll_y = actual_scroll_y;
2454 void InitMovDir(int x, int y)
2456 int i, element = Feld[x][y];
2457 static int xy[4][2] =
2464 static int direction[3][4] =
2466 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2467 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2468 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2477 Feld[x][y] = EL_BUG;
2478 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2481 case EL_SPACESHIP_RIGHT:
2482 case EL_SPACESHIP_UP:
2483 case EL_SPACESHIP_LEFT:
2484 case EL_SPACESHIP_DOWN:
2485 Feld[x][y] = EL_SPACESHIP;
2486 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2489 case EL_BD_BUTTERFLY_RIGHT:
2490 case EL_BD_BUTTERFLY_UP:
2491 case EL_BD_BUTTERFLY_LEFT:
2492 case EL_BD_BUTTERFLY_DOWN:
2493 Feld[x][y] = EL_BD_BUTTERFLY;
2494 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2497 case EL_BD_FIREFLY_RIGHT:
2498 case EL_BD_FIREFLY_UP:
2499 case EL_BD_FIREFLY_LEFT:
2500 case EL_BD_FIREFLY_DOWN:
2501 Feld[x][y] = EL_BD_FIREFLY;
2502 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2505 case EL_PACMAN_RIGHT:
2507 case EL_PACMAN_LEFT:
2508 case EL_PACMAN_DOWN:
2509 Feld[x][y] = EL_PACMAN;
2510 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2513 case EL_YAMYAM_LEFT:
2514 case EL_YAMYAM_RIGHT:
2516 case EL_YAMYAM_DOWN:
2517 Feld[x][y] = EL_YAMYAM;
2518 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2521 case EL_SP_SNIKSNAK:
2522 MovDir[x][y] = MV_UP;
2525 case EL_SP_ELECTRON:
2526 MovDir[x][y] = MV_LEFT;
2533 Feld[x][y] = EL_MOLE;
2534 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2538 if (IS_CUSTOM_ELEMENT(element))
2540 struct ElementInfo *ei = &element_info[element];
2541 int move_direction_initial = ei->move_direction_initial;
2542 int move_pattern = ei->move_pattern;
2544 if (move_direction_initial == MV_START_PREVIOUS)
2546 if (MovDir[x][y] != MV_NONE)
2549 move_direction_initial = MV_START_AUTOMATIC;
2552 if (move_direction_initial == MV_START_RANDOM)
2553 MovDir[x][y] = 1 << RND(4);
2554 else if (move_direction_initial & MV_ANY_DIRECTION)
2555 MovDir[x][y] = move_direction_initial;
2556 else if (move_pattern == MV_ALL_DIRECTIONS ||
2557 move_pattern == MV_TURNING_LEFT ||
2558 move_pattern == MV_TURNING_RIGHT ||
2559 move_pattern == MV_TURNING_LEFT_RIGHT ||
2560 move_pattern == MV_TURNING_RIGHT_LEFT ||
2561 move_pattern == MV_TURNING_RANDOM)
2562 MovDir[x][y] = 1 << RND(4);
2563 else if (move_pattern == MV_HORIZONTAL)
2564 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2565 else if (move_pattern == MV_VERTICAL)
2566 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2567 else if (move_pattern & MV_ANY_DIRECTION)
2568 MovDir[x][y] = element_info[element].move_pattern;
2569 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2570 move_pattern == MV_ALONG_RIGHT_SIDE)
2572 /* use random direction as default start direction */
2573 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2574 MovDir[x][y] = 1 << RND(4);
2576 for (i = 0; i < NUM_DIRECTIONS; i++)
2578 int x1 = x + xy[i][0];
2579 int y1 = y + xy[i][1];
2581 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2583 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2584 MovDir[x][y] = direction[0][i];
2586 MovDir[x][y] = direction[1][i];
2595 MovDir[x][y] = 1 << RND(4);
2597 if (element != EL_BUG &&
2598 element != EL_SPACESHIP &&
2599 element != EL_BD_BUTTERFLY &&
2600 element != EL_BD_FIREFLY)
2603 for (i = 0; i < NUM_DIRECTIONS; i++)
2605 int x1 = x + xy[i][0];
2606 int y1 = y + xy[i][1];
2608 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2610 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2612 MovDir[x][y] = direction[0][i];
2615 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2616 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2618 MovDir[x][y] = direction[1][i];
2627 GfxDir[x][y] = MovDir[x][y];
2630 void InitAmoebaNr(int x, int y)
2633 int group_nr = AmoebeNachbarNr(x, y);
2637 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2639 if (AmoebaCnt[i] == 0)
2647 AmoebaNr[x][y] = group_nr;
2648 AmoebaCnt[group_nr]++;
2649 AmoebaCnt2[group_nr]++;
2655 boolean raise_level = FALSE;
2657 if (local_player->MovPos)
2660 if (tape.auto_play) /* tape might already be stopped here */
2661 tape.auto_play_level_solved = TRUE;
2663 local_player->LevelSolved = FALSE;
2665 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2669 if (!tape.playing && setup.sound_loops)
2670 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2671 SND_CTRL_PLAY_LOOP);
2673 while (TimeLeft > 0)
2675 if (!tape.playing && !setup.sound_loops)
2676 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2678 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2681 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2686 RaiseScore(level.score[SC_TIME_BONUS]);
2689 DrawGameValue_Time(TimeLeft);
2697 if (!tape.playing && setup.sound_loops)
2698 StopSound(SND_GAME_LEVELTIME_BONUS);
2700 else if (level.time == 0) /* level without time limit */
2702 if (!tape.playing && setup.sound_loops)
2703 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2704 SND_CTRL_PLAY_LOOP);
2706 while (TimePlayed < 999)
2708 if (!tape.playing && !setup.sound_loops)
2709 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2711 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2714 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2719 RaiseScore(level.score[SC_TIME_BONUS]);
2722 DrawGameValue_Time(TimePlayed);
2730 if (!tape.playing && setup.sound_loops)
2731 StopSound(SND_GAME_LEVELTIME_BONUS);
2734 /* close exit door after last player */
2735 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2736 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2737 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2739 int element = Feld[ExitX][ExitY];
2741 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2742 EL_SP_EXIT_CLOSING);
2744 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2747 /* player disappears */
2748 if (ExitX >= 0 && ExitY >= 0)
2749 DrawLevelField(ExitX, ExitY);
2755 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2757 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2763 CloseDoor(DOOR_CLOSE_1);
2768 SaveTape(tape.level_nr); /* Ask to save tape */
2771 if (level_nr == leveldir_current->handicap_level)
2773 leveldir_current->handicap_level++;
2774 SaveLevelSetup_SeriesInfo();
2777 if (level_editor_test_game)
2778 local_player->score = -1; /* no highscore when playing from editor */
2779 else if (level_nr < leveldir_current->last_level)
2780 raise_level = TRUE; /* advance to next level */
2782 if ((hi_pos = NewHiScore()) >= 0)
2784 game_status = GAME_MODE_SCORES;
2785 DrawHallOfFame(hi_pos);
2794 game_status = GAME_MODE_MAIN;
2811 LoadScore(level_nr);
2813 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2814 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2817 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2819 if (local_player->score > highscore[k].Score)
2821 /* player has made it to the hall of fame */
2823 if (k < MAX_SCORE_ENTRIES - 1)
2825 int m = MAX_SCORE_ENTRIES - 1;
2828 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2829 if (!strcmp(setup.player_name, highscore[l].Name))
2831 if (m == k) /* player's new highscore overwrites his old one */
2835 for (l = m; l > k; l--)
2837 strcpy(highscore[l].Name, highscore[l - 1].Name);
2838 highscore[l].Score = highscore[l - 1].Score;
2845 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2846 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2847 highscore[k].Score = local_player->score;
2853 else if (!strncmp(setup.player_name, highscore[k].Name,
2854 MAX_PLAYER_NAME_LEN))
2855 break; /* player already there with a higher score */
2861 SaveScore(level_nr);
2866 inline static int getElementMoveStepsize(int x, int y)
2868 int element = Feld[x][y];
2869 int direction = MovDir[x][y];
2870 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2871 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2872 int horiz_move = (dx != 0);
2873 int sign = (horiz_move ? dx : dy);
2874 int step = sign * element_info[element].move_stepsize;
2876 /* special values for move stepsize for spring and things on conveyor belt */
2880 if (element == EL_SPRING)
2881 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2882 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2883 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2884 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2886 if (CAN_FALL(element) &&
2887 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2888 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2889 else if (element == EL_SPRING)
2890 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2897 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2899 if (player->GfxAction != action || player->GfxDir != dir)
2902 printf("Player frame reset! (%d => %d, %d => %d)\n",
2903 player->GfxAction, action, player->GfxDir, dir);
2906 player->GfxAction = action;
2907 player->GfxDir = dir;
2909 player->StepFrame = 0;
2913 static void ResetRandomAnimationValue(int x, int y)
2915 GfxRandom[x][y] = INIT_GFX_RANDOM();
2918 static void ResetGfxAnimation(int x, int y)
2921 int element, graphic;
2925 GfxAction[x][y] = ACTION_DEFAULT;
2926 GfxDir[x][y] = MovDir[x][y];
2929 element = Feld[x][y];
2930 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2932 if (graphic_info[graphic].anim_global_sync)
2933 GfxFrame[x][y] = FrameCounter;
2934 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2935 GfxFrame[x][y] = CustomValue[x][y];
2936 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2937 GfxFrame[x][y] = element_info[element].collect_score;
2941 void InitMovingField(int x, int y, int direction)
2943 int element = Feld[x][y];
2947 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2948 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2952 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2953 ResetGfxAnimation(x, y);
2955 MovDir[x][y] = direction;
2956 GfxDir[x][y] = direction;
2957 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2958 ACTION_FALLING : ACTION_MOVING);
2961 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2963 if (graphic_info[graphic].anim_global_sync)
2964 GfxFrame[x][y] = FrameCounter;
2965 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2966 GfxFrame[x][y] = CustomValue[x][y];
2967 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2968 GfxFrame[x][y] = element_info[element].collect_score;
2971 /* this is needed for CEs with property "can move" / "not moving" */
2973 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2975 if (Feld[newx][newy] == EL_EMPTY)
2976 Feld[newx][newy] = EL_BLOCKED;
2978 MovDir[newx][newy] = MovDir[x][y];
2980 #if USE_NEW_CUSTOM_VALUE
2981 CustomValue[newx][newy] = CustomValue[x][y];
2984 GfxFrame[newx][newy] = GfxFrame[x][y];
2985 GfxRandom[newx][newy] = GfxRandom[x][y];
2986 GfxAction[newx][newy] = GfxAction[x][y];
2987 GfxDir[newx][newy] = GfxDir[x][y];
2991 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2993 int direction = MovDir[x][y];
2995 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2996 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2998 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2999 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3006 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3008 int oldx = x, oldy = y;
3009 int direction = MovDir[x][y];
3011 if (direction == MV_LEFT)
3013 else if (direction == MV_RIGHT)
3015 else if (direction == MV_UP)
3017 else if (direction == MV_DOWN)
3020 *comes_from_x = oldx;
3021 *comes_from_y = oldy;
3024 int MovingOrBlocked2Element(int x, int y)
3026 int element = Feld[x][y];
3028 if (element == EL_BLOCKED)
3032 Blocked2Moving(x, y, &oldx, &oldy);
3033 return Feld[oldx][oldy];
3039 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3041 /* like MovingOrBlocked2Element(), but if element is moving
3042 and (x,y) is the field the moving element is just leaving,
3043 return EL_BLOCKED instead of the element value */
3044 int element = Feld[x][y];
3046 if (IS_MOVING(x, y))
3048 if (element == EL_BLOCKED)
3052 Blocked2Moving(x, y, &oldx, &oldy);
3053 return Feld[oldx][oldy];
3062 static void RemoveField(int x, int y)
3064 Feld[x][y] = EL_EMPTY;
3070 #if USE_NEW_CUSTOM_VALUE
3071 CustomValue[x][y] = 0;
3075 ChangeDelay[x][y] = 0;
3076 ChangePage[x][y] = -1;
3077 Pushed[x][y] = FALSE;
3080 ExplodeField[x][y] = EX_TYPE_NONE;
3083 GfxElement[x][y] = EL_UNDEFINED;
3084 GfxAction[x][y] = ACTION_DEFAULT;
3085 GfxDir[x][y] = MV_NONE;
3088 void RemoveMovingField(int x, int y)
3090 int oldx = x, oldy = y, newx = x, newy = y;
3091 int element = Feld[x][y];
3092 int next_element = EL_UNDEFINED;
3094 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3097 if (IS_MOVING(x, y))
3099 Moving2Blocked(x, y, &newx, &newy);
3101 if (Feld[newx][newy] != EL_BLOCKED)
3103 /* element is moving, but target field is not free (blocked), but
3104 already occupied by something different (example: acid pool);
3105 in this case, only remove the moving field, but not the target */
3107 RemoveField(oldx, oldy);
3109 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3111 DrawLevelField(oldx, oldy);
3116 else if (element == EL_BLOCKED)
3118 Blocked2Moving(x, y, &oldx, &oldy);
3119 if (!IS_MOVING(oldx, oldy))
3123 if (element == EL_BLOCKED &&
3124 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3125 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3126 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3127 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3128 next_element = get_next_element(Feld[oldx][oldy]);
3130 RemoveField(oldx, oldy);
3131 RemoveField(newx, newy);
3133 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3135 if (next_element != EL_UNDEFINED)
3136 Feld[oldx][oldy] = next_element;
3138 DrawLevelField(oldx, oldy);
3139 DrawLevelField(newx, newy);
3142 void DrawDynamite(int x, int y)
3144 int sx = SCREENX(x), sy = SCREENY(y);
3145 int graphic = el2img(Feld[x][y]);
3148 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3151 if (IS_WALKABLE_INSIDE(Back[x][y]))
3155 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3156 else if (Store[x][y])
3157 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3159 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3161 if (Back[x][y] || Store[x][y])
3162 DrawGraphicThruMask(sx, sy, graphic, frame);
3164 DrawGraphic(sx, sy, graphic, frame);
3167 void CheckDynamite(int x, int y)
3169 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3173 if (MovDelay[x][y] != 0)
3176 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3182 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3189 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3191 boolean num_checked_players = 0;
3194 for (i = 0; i < MAX_PLAYERS; i++)
3196 if (stored_player[i].active)
3198 int sx = stored_player[i].jx;
3199 int sy = stored_player[i].jy;
3201 if (num_checked_players == 0)
3208 *sx1 = MIN(*sx1, sx);
3209 *sy1 = MIN(*sy1, sy);
3210 *sx2 = MAX(*sx2, sx);
3211 *sy2 = MAX(*sy2, sy);
3214 num_checked_players++;
3219 static boolean checkIfAllPlayersFitToScreen_RND()
3221 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3223 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3225 return (sx2 - sx1 < SCR_FIELDX &&
3226 sy2 - sy1 < SCR_FIELDY);
3229 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3231 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3233 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3235 *sx = (sx1 + sx2) / 2;
3236 *sy = (sy1 + sy2) / 2;
3240 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3241 int center_x, int center_y)
3243 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3245 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3247 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3248 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3251 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3255 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3257 return (max_dx <= SCR_FIELDX / 2 &&
3258 max_dy <= SCR_FIELDY / 2);
3266 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3267 boolean quick_relocation)
3269 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3270 boolean no_delay = (tape.warp_forward);
3271 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3272 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3274 if (quick_relocation)
3276 int offset = (setup.scroll_delay ? 3 : 0);
3283 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3285 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3286 x > SBX_Right + MIDPOSX ? SBX_Right :
3289 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3290 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3295 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3296 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3297 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3299 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3300 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3301 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3303 /* don't scroll over playfield boundaries */
3304 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3305 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3307 /* don't scroll over playfield boundaries */
3308 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3309 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3312 RedrawPlayfield(TRUE, 0,0,0,0);
3316 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3317 x > SBX_Right + MIDPOSX ? SBX_Right :
3320 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3321 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3324 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3326 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3329 int fx = FX, fy = FY;
3331 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3332 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3334 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3340 fx += dx * TILEX / 2;
3341 fy += dy * TILEY / 2;
3343 ScrollLevel(dx, dy);
3346 /* scroll in two steps of half tile size to make things smoother */
3347 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3349 Delay(wait_delay_value);
3351 /* scroll second step to align at full tile size */
3353 Delay(wait_delay_value);
3358 Delay(wait_delay_value);
3364 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3366 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3367 boolean no_delay = (tape.warp_forward);
3368 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3369 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3370 int jx = player->jx;
3371 int jy = player->jy;
3373 if (quick_relocation)
3375 int offset = (setup.scroll_delay ? 3 : 0);
3377 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3379 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3380 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3381 player->jx - MIDPOSX);
3383 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3384 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3385 player->jy - MIDPOSY);
3389 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3390 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3391 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3393 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3394 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3395 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3397 /* don't scroll over playfield boundaries */
3398 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3399 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3401 /* don't scroll over playfield boundaries */
3402 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3403 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3406 RedrawPlayfield(TRUE, 0,0,0,0);
3410 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3411 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3412 player->jx - MIDPOSX);
3414 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3415 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3416 player->jy - MIDPOSY);
3418 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3420 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3423 int fx = FX, fy = FY;
3425 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3426 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3428 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3434 fx += dx * TILEX / 2;
3435 fy += dy * TILEY / 2;
3437 ScrollLevel(dx, dy);
3440 /* scroll in two steps of half tile size to make things smoother */
3441 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3443 Delay(wait_delay_value);
3445 /* scroll second step to align at full tile size */
3447 Delay(wait_delay_value);
3452 Delay(wait_delay_value);
3458 void RelocatePlayer(int jx, int jy, int el_player_raw)
3460 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3461 int player_nr = GET_PLAYER_NR(el_player);
3462 struct PlayerInfo *player = &stored_player[player_nr];
3463 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3464 boolean no_delay = (tape.warp_forward);
3465 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3466 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3467 int old_jx = player->jx;
3468 int old_jy = player->jy;
3469 int old_element = Feld[old_jx][old_jy];
3470 int element = Feld[jx][jy];
3471 boolean player_relocated = (old_jx != jx || old_jy != jy);
3473 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3474 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3475 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3476 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3477 int leave_side_horiz = move_dir_horiz;
3478 int leave_side_vert = move_dir_vert;
3479 int enter_side = enter_side_horiz | enter_side_vert;
3480 int leave_side = leave_side_horiz | leave_side_vert;
3482 if (player->GameOver) /* do not reanimate dead player */
3485 if (!player_relocated) /* no need to relocate the player */
3488 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3490 RemoveField(jx, jy); /* temporarily remove newly placed player */
3491 DrawLevelField(jx, jy);
3494 if (player->present)
3496 while (player->MovPos)
3498 ScrollPlayer(player, SCROLL_GO_ON);
3499 ScrollScreen(NULL, SCROLL_GO_ON);
3501 AdvanceFrameAndPlayerCounters(player->index_nr);
3506 Delay(wait_delay_value);
3509 DrawPlayer(player); /* needed here only to cleanup last field */
3510 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3512 player->is_moving = FALSE;
3515 if (IS_CUSTOM_ELEMENT(old_element))
3516 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3518 player->index_bit, leave_side);
3520 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3522 player->index_bit, leave_side);
3524 Feld[jx][jy] = el_player;
3525 InitPlayerField(jx, jy, el_player, TRUE);
3527 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3529 Feld[jx][jy] = element;
3530 InitField(jx, jy, FALSE);
3534 /* only visually relocate centered player */
3536 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3537 level.instant_relocation);
3539 if (player->index_nr == game.centered_player_nr)
3540 DrawRelocatePlayer(player, level.instant_relocation);
3543 if (player == local_player) /* only visually relocate local player */
3544 DrawRelocatePlayer(player, level.instant_relocation);
3547 TestIfPlayerTouchesBadThing(jx, jy);
3548 TestIfPlayerTouchesCustomElement(jx, jy);
3550 if (IS_CUSTOM_ELEMENT(element))
3551 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3552 player->index_bit, enter_side);
3554 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3555 player->index_bit, enter_side);
3558 void Explode(int ex, int ey, int phase, int mode)
3564 /* !!! eliminate this variable !!! */
3565 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3567 if (game.explosions_delayed)
3569 ExplodeField[ex][ey] = mode;
3573 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3575 int center_element = Feld[ex][ey];
3576 int artwork_element, explosion_element; /* set these values later */
3579 /* --- This is only really needed (and now handled) in "Impact()". --- */
3580 /* do not explode moving elements that left the explode field in time */
3581 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3582 center_element == EL_EMPTY &&
3583 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3588 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3589 if (mode == EX_TYPE_NORMAL ||
3590 mode == EX_TYPE_CENTER ||
3591 mode == EX_TYPE_CROSS)
3592 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3595 /* remove things displayed in background while burning dynamite */
3596 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3599 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3601 /* put moving element to center field (and let it explode there) */
3602 center_element = MovingOrBlocked2Element(ex, ey);
3603 RemoveMovingField(ex, ey);
3604 Feld[ex][ey] = center_element;
3607 /* now "center_element" is finally determined -- set related values now */
3608 artwork_element = center_element; /* for custom player artwork */
3609 explosion_element = center_element; /* for custom player artwork */
3611 if (IS_PLAYER(ex, ey))
3613 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3615 artwork_element = stored_player[player_nr].artwork_element;
3617 if (level.use_explosion_element[player_nr])
3619 explosion_element = level.explosion_element[player_nr];
3620 artwork_element = explosion_element;
3625 if (mode == EX_TYPE_NORMAL ||
3626 mode == EX_TYPE_CENTER ||
3627 mode == EX_TYPE_CROSS)
3628 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3632 last_phase = element_info[explosion_element].explosion_delay + 1;
3634 last_phase = element_info[center_element].explosion_delay + 1;
3637 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3639 int xx = x - ex + 1;
3640 int yy = y - ey + 1;
3643 if (!IN_LEV_FIELD(x, y) ||
3644 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3645 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3648 element = Feld[x][y];
3650 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3652 element = MovingOrBlocked2Element(x, y);
3654 if (!IS_EXPLOSION_PROOF(element))
3655 RemoveMovingField(x, y);
3658 /* indestructible elements can only explode in center (but not flames) */
3659 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3660 mode == EX_TYPE_BORDER)) ||
3661 element == EL_FLAMES)
3664 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3665 behaviour, for example when touching a yamyam that explodes to rocks
3666 with active deadly shield, a rock is created under the player !!! */
3667 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3669 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3670 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3671 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3673 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3676 if (IS_ACTIVE_BOMB(element))
3678 /* re-activate things under the bomb like gate or penguin */
3679 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3686 /* save walkable background elements while explosion on same tile */
3687 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3688 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3689 Back[x][y] = element;
3691 /* ignite explodable elements reached by other explosion */
3692 if (element == EL_EXPLOSION)
3693 element = Store2[x][y];
3695 if (AmoebaNr[x][y] &&
3696 (element == EL_AMOEBA_FULL ||
3697 element == EL_BD_AMOEBA ||
3698 element == EL_AMOEBA_GROWING))
3700 AmoebaCnt[AmoebaNr[x][y]]--;
3701 AmoebaCnt2[AmoebaNr[x][y]]--;
3706 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3709 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3711 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3713 switch(StorePlayer[ex][ey])
3716 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3719 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3722 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3726 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3731 if (PLAYERINFO(ex, ey)->use_murphy)
3732 Store[x][y] = EL_EMPTY;
3735 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3736 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3737 else if (ELEM_IS_PLAYER(center_element))
3738 Store[x][y] = EL_EMPTY;
3739 else if (center_element == EL_YAMYAM)
3740 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3741 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3742 Store[x][y] = element_info[center_element].content.e[xx][yy];
3744 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3745 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3746 otherwise) -- FIX THIS !!! */
3747 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3748 Store[x][y] = element_info[element].content.e[1][1];
3750 else if (!CAN_EXPLODE(element))
3751 Store[x][y] = element_info[element].content.e[1][1];
3754 Store[x][y] = EL_EMPTY;
3756 else if (center_element == EL_MOLE)
3757 Store[x][y] = EL_EMERALD_RED;
3758 else if (center_element == EL_PENGUIN)
3759 Store[x][y] = EL_EMERALD_PURPLE;
3760 else if (center_element == EL_BUG)
3761 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3762 else if (center_element == EL_BD_BUTTERFLY)
3763 Store[x][y] = EL_BD_DIAMOND;
3764 else if (center_element == EL_SP_ELECTRON)
3765 Store[x][y] = EL_SP_INFOTRON;
3766 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3767 Store[x][y] = level.amoeba_content;
3768 else if (center_element == EL_YAMYAM)
3769 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3770 else if (IS_CUSTOM_ELEMENT(center_element) &&
3771 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3772 Store[x][y] = element_info[center_element].content.e[xx][yy];
3773 else if (element == EL_WALL_EMERALD)
3774 Store[x][y] = EL_EMERALD;
3775 else if (element == EL_WALL_DIAMOND)
3776 Store[x][y] = EL_DIAMOND;
3777 else if (element == EL_WALL_BD_DIAMOND)
3778 Store[x][y] = EL_BD_DIAMOND;
3779 else if (element == EL_WALL_EMERALD_YELLOW)
3780 Store[x][y] = EL_EMERALD_YELLOW;
3781 else if (element == EL_WALL_EMERALD_RED)
3782 Store[x][y] = EL_EMERALD_RED;
3783 else if (element == EL_WALL_EMERALD_PURPLE)
3784 Store[x][y] = EL_EMERALD_PURPLE;
3785 else if (element == EL_WALL_PEARL)
3786 Store[x][y] = EL_PEARL;
3787 else if (element == EL_WALL_CRYSTAL)
3788 Store[x][y] = EL_CRYSTAL;
3789 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3790 Store[x][y] = element_info[element].content.e[1][1];
3792 Store[x][y] = EL_EMPTY;
3795 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3796 center_element == EL_AMOEBA_TO_DIAMOND)
3797 Store2[x][y] = element;
3799 Feld[x][y] = EL_EXPLOSION;
3800 GfxElement[x][y] = artwork_element;
3803 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3804 x, y, artwork_element, EL_NAME(artwork_element));
3807 ExplodePhase[x][y] = 1;
3808 ExplodeDelay[x][y] = last_phase;
3813 if (center_element == EL_YAMYAM)
3814 game.yamyam_content_nr =
3815 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3827 GfxFrame[x][y] = 0; /* restart explosion animation */
3829 last_phase = ExplodeDelay[x][y];
3831 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3835 /* activate this even in non-DEBUG version until cause for crash in
3836 getGraphicAnimationFrame() (see below) is found and eliminated */
3842 /* this can happen if the player leaves an explosion just in time */
3843 if (GfxElement[x][y] == EL_UNDEFINED)
3844 GfxElement[x][y] = EL_EMPTY;
3846 if (GfxElement[x][y] == EL_UNDEFINED)
3849 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3850 printf("Explode(): This should never happen!\n");
3853 GfxElement[x][y] = EL_EMPTY;
3859 border_element = Store2[x][y];
3860 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3861 border_element = StorePlayer[x][y];
3863 if (phase == element_info[border_element].ignition_delay ||
3864 phase == last_phase)
3866 boolean border_explosion = FALSE;
3868 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3869 !PLAYER_EXPLOSION_PROTECTED(x, y))
3871 KillPlayerUnlessExplosionProtected(x, y);
3872 border_explosion = TRUE;
3874 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3876 Feld[x][y] = Store2[x][y];
3879 border_explosion = TRUE;
3881 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3883 AmoebeUmwandeln(x, y);
3885 border_explosion = TRUE;
3888 /* if an element just explodes due to another explosion (chain-reaction),
3889 do not immediately end the new explosion when it was the last frame of
3890 the explosion (as it would be done in the following "if"-statement!) */
3891 if (border_explosion && phase == last_phase)
3895 if (phase == last_phase)
3899 element = Feld[x][y] = Store[x][y];
3900 Store[x][y] = Store2[x][y] = 0;
3901 GfxElement[x][y] = EL_UNDEFINED;
3903 /* player can escape from explosions and might therefore be still alive */
3904 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3905 element <= EL_PLAYER_IS_EXPLODING_4)
3907 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3908 int explosion_element = EL_PLAYER_1 + player_nr;
3909 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3910 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3912 if (level.use_explosion_element[player_nr])
3913 explosion_element = level.explosion_element[player_nr];
3915 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3916 element_info[explosion_element].content.e[xx][yy]);
3919 /* restore probably existing indestructible background element */
3920 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3921 element = Feld[x][y] = Back[x][y];
3924 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3925 GfxDir[x][y] = MV_NONE;
3926 ChangeDelay[x][y] = 0;
3927 ChangePage[x][y] = -1;
3929 #if USE_NEW_CUSTOM_VALUE
3930 CustomValue[x][y] = 0;
3933 InitField_WithBug2(x, y, FALSE);
3935 DrawLevelField(x, y);
3937 TestIfElementTouchesCustomElement(x, y);
3939 if (GFX_CRUMBLED(element))
3940 DrawLevelFieldCrumbledSandNeighbours(x, y);
3942 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3943 StorePlayer[x][y] = 0;
3945 if (ELEM_IS_PLAYER(element))
3946 RelocatePlayer(x, y, element);
3948 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3950 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3951 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3954 DrawLevelFieldCrumbledSand(x, y);
3956 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3958 DrawLevelElement(x, y, Back[x][y]);
3959 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3961 else if (IS_WALKABLE_UNDER(Back[x][y]))
3963 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3964 DrawLevelElementThruMask(x, y, Back[x][y]);
3966 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3967 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3971 void DynaExplode(int ex, int ey)
3974 int dynabomb_element = Feld[ex][ey];
3975 int dynabomb_size = 1;
3976 boolean dynabomb_xl = FALSE;
3977 struct PlayerInfo *player;
3978 static int xy[4][2] =
3986 if (IS_ACTIVE_BOMB(dynabomb_element))
3988 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3989 dynabomb_size = player->dynabomb_size;
3990 dynabomb_xl = player->dynabomb_xl;
3991 player->dynabombs_left++;
3994 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3996 for (i = 0; i < NUM_DIRECTIONS; i++)
3998 for (j = 1; j <= dynabomb_size; j++)
4000 int x = ex + j * xy[i][0];
4001 int y = ey + j * xy[i][1];
4004 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4007 element = Feld[x][y];
4009 /* do not restart explosions of fields with active bombs */
4010 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4013 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4015 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4016 !IS_DIGGABLE(element) && !dynabomb_xl)
4022 void Bang(int x, int y)
4024 int element = MovingOrBlocked2Element(x, y);
4025 int explosion_type = EX_TYPE_NORMAL;
4027 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4029 struct PlayerInfo *player = PLAYERINFO(x, y);
4031 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4032 player->element_nr);
4034 if (level.use_explosion_element[player->index_nr])
4036 int explosion_element = level.explosion_element[player->index_nr];
4038 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4039 explosion_type = EX_TYPE_CROSS;
4040 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4041 explosion_type = EX_TYPE_CENTER;
4049 case EL_BD_BUTTERFLY:
4052 case EL_DARK_YAMYAM:
4056 RaiseScoreElement(element);
4059 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4060 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4061 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4062 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4063 case EL_DYNABOMB_INCREASE_NUMBER:
4064 case EL_DYNABOMB_INCREASE_SIZE:
4065 case EL_DYNABOMB_INCREASE_POWER:
4066 explosion_type = EX_TYPE_DYNA;
4071 case EL_LAMP_ACTIVE:
4072 case EL_AMOEBA_TO_DIAMOND:
4073 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4074 explosion_type = EX_TYPE_CENTER;
4078 if (element_info[element].explosion_type == EXPLODES_CROSS)
4079 explosion_type = EX_TYPE_CROSS;
4080 else if (element_info[element].explosion_type == EXPLODES_1X1)
4081 explosion_type = EX_TYPE_CENTER;
4085 if (explosion_type == EX_TYPE_DYNA)
4088 Explode(x, y, EX_PHASE_START, explosion_type);
4090 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4093 void SplashAcid(int x, int y)
4095 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4096 (!IN_LEV_FIELD(x - 1, y - 2) ||
4097 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4098 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4100 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4101 (!IN_LEV_FIELD(x + 1, y - 2) ||
4102 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4103 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4105 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4108 static void InitBeltMovement()
4110 static int belt_base_element[4] =
4112 EL_CONVEYOR_BELT_1_LEFT,
4113 EL_CONVEYOR_BELT_2_LEFT,
4114 EL_CONVEYOR_BELT_3_LEFT,
4115 EL_CONVEYOR_BELT_4_LEFT
4117 static int belt_base_active_element[4] =
4119 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4120 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4121 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4122 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4127 /* set frame order for belt animation graphic according to belt direction */
4128 for (i = 0; i < NUM_BELTS; i++)
4132 for (j = 0; j < NUM_BELT_PARTS; j++)
4134 int element = belt_base_active_element[belt_nr] + j;
4135 int graphic = el2img(element);
4137 if (game.belt_dir[i] == MV_LEFT)
4138 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4140 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4145 SCAN_PLAYFIELD(x, y)
4147 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4150 int element = Feld[x][y];
4152 for (i = 0; i < NUM_BELTS; i++)
4154 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4156 int e_belt_nr = getBeltNrFromBeltElement(element);
4159 if (e_belt_nr == belt_nr)
4161 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4163 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4170 static void ToggleBeltSwitch(int x, int y)
4172 static int belt_base_element[4] =
4174 EL_CONVEYOR_BELT_1_LEFT,
4175 EL_CONVEYOR_BELT_2_LEFT,
4176 EL_CONVEYOR_BELT_3_LEFT,
4177 EL_CONVEYOR_BELT_4_LEFT
4179 static int belt_base_active_element[4] =
4181 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4182 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4183 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4184 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4186 static int belt_base_switch_element[4] =
4188 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4189 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4190 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4191 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4193 static int belt_move_dir[4] =
4201 int element = Feld[x][y];
4202 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4203 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4204 int belt_dir = belt_move_dir[belt_dir_nr];
4207 if (!IS_BELT_SWITCH(element))
4210 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4211 game.belt_dir[belt_nr] = belt_dir;
4213 if (belt_dir_nr == 3)
4216 /* set frame order for belt animation graphic according to belt direction */
4217 for (i = 0; i < NUM_BELT_PARTS; i++)
4219 int element = belt_base_active_element[belt_nr] + i;
4220 int graphic = el2img(element);
4222 if (belt_dir == MV_LEFT)
4223 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4225 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4229 SCAN_PLAYFIELD(xx, yy)
4231 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4234 int element = Feld[xx][yy];
4236 if (IS_BELT_SWITCH(element))
4238 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4240 if (e_belt_nr == belt_nr)
4242 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4243 DrawLevelField(xx, yy);
4246 else if (IS_BELT(element) && belt_dir != MV_NONE)
4248 int e_belt_nr = getBeltNrFromBeltElement(element);
4250 if (e_belt_nr == belt_nr)
4252 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4254 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4255 DrawLevelField(xx, yy);
4258 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4260 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4262 if (e_belt_nr == belt_nr)
4264 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4266 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4267 DrawLevelField(xx, yy);
4273 static void ToggleSwitchgateSwitch(int x, int y)
4277 game.switchgate_pos = !game.switchgate_pos;
4280 SCAN_PLAYFIELD(xx, yy)
4282 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4285 int element = Feld[xx][yy];
4287 if (element == EL_SWITCHGATE_SWITCH_UP ||
4288 element == EL_SWITCHGATE_SWITCH_DOWN)
4290 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4291 DrawLevelField(xx, yy);
4293 else if (element == EL_SWITCHGATE_OPEN ||
4294 element == EL_SWITCHGATE_OPENING)
4296 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4298 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4300 else if (element == EL_SWITCHGATE_CLOSED ||
4301 element == EL_SWITCHGATE_CLOSING)
4303 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4305 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4310 static int getInvisibleActiveFromInvisibleElement(int element)
4312 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4313 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4314 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4318 static int getInvisibleFromInvisibleActiveElement(int element)
4320 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4321 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4322 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4326 static void RedrawAllLightSwitchesAndInvisibleElements()
4331 SCAN_PLAYFIELD(x, y)
4333 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4336 int element = Feld[x][y];
4338 if (element == EL_LIGHT_SWITCH &&
4339 game.light_time_left > 0)
4341 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4342 DrawLevelField(x, y);
4344 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4345 game.light_time_left == 0)
4347 Feld[x][y] = EL_LIGHT_SWITCH;
4348 DrawLevelField(x, y);
4350 else if (element == EL_EMC_DRIPPER &&
4351 game.light_time_left > 0)
4353 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4354 DrawLevelField(x, y);
4356 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4357 game.light_time_left == 0)
4359 Feld[x][y] = EL_EMC_DRIPPER;
4360 DrawLevelField(x, y);
4362 else if (element == EL_INVISIBLE_STEELWALL ||
4363 element == EL_INVISIBLE_WALL ||
4364 element == EL_INVISIBLE_SAND)
4366 if (game.light_time_left > 0)
4367 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4369 DrawLevelField(x, y);
4371 /* uncrumble neighbour fields, if needed */
4372 if (element == EL_INVISIBLE_SAND)
4373 DrawLevelFieldCrumbledSandNeighbours(x, y);
4375 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4376 element == EL_INVISIBLE_WALL_ACTIVE ||
4377 element == EL_INVISIBLE_SAND_ACTIVE)
4379 if (game.light_time_left == 0)
4380 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4382 DrawLevelField(x, y);
4384 /* re-crumble neighbour fields, if needed */
4385 if (element == EL_INVISIBLE_SAND)
4386 DrawLevelFieldCrumbledSandNeighbours(x, y);
4391 static void RedrawAllInvisibleElementsForLenses()
4396 SCAN_PLAYFIELD(x, y)
4398 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4401 int element = Feld[x][y];
4403 if (element == EL_EMC_DRIPPER &&
4404 game.lenses_time_left > 0)
4406 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4407 DrawLevelField(x, y);
4409 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4410 game.lenses_time_left == 0)
4412 Feld[x][y] = EL_EMC_DRIPPER;
4413 DrawLevelField(x, y);
4415 else if (element == EL_INVISIBLE_STEELWALL ||
4416 element == EL_INVISIBLE_WALL ||
4417 element == EL_INVISIBLE_SAND)
4419 if (game.lenses_time_left > 0)
4420 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4422 DrawLevelField(x, y);
4424 /* uncrumble neighbour fields, if needed */
4425 if (element == EL_INVISIBLE_SAND)
4426 DrawLevelFieldCrumbledSandNeighbours(x, y);
4428 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4429 element == EL_INVISIBLE_WALL_ACTIVE ||
4430 element == EL_INVISIBLE_SAND_ACTIVE)
4432 if (game.lenses_time_left == 0)
4433 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4435 DrawLevelField(x, y);
4437 /* re-crumble neighbour fields, if needed */
4438 if (element == EL_INVISIBLE_SAND)
4439 DrawLevelFieldCrumbledSandNeighbours(x, y);
4444 static void RedrawAllInvisibleElementsForMagnifier()
4449 SCAN_PLAYFIELD(x, y)
4451 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4454 int element = Feld[x][y];
4456 if (element == EL_EMC_FAKE_GRASS &&
4457 game.magnify_time_left > 0)
4459 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4460 DrawLevelField(x, y);
4462 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4463 game.magnify_time_left == 0)
4465 Feld[x][y] = EL_EMC_FAKE_GRASS;
4466 DrawLevelField(x, y);
4468 else if (IS_GATE_GRAY(element) &&
4469 game.magnify_time_left > 0)
4471 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4472 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4473 IS_EM_GATE_GRAY(element) ?
4474 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4475 IS_EMC_GATE_GRAY(element) ?
4476 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4478 DrawLevelField(x, y);
4480 else if (IS_GATE_GRAY_ACTIVE(element) &&
4481 game.magnify_time_left == 0)
4483 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4484 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4485 IS_EM_GATE_GRAY_ACTIVE(element) ?
4486 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4487 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4488 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4490 DrawLevelField(x, y);
4495 static void ToggleLightSwitch(int x, int y)
4497 int element = Feld[x][y];
4499 game.light_time_left =
4500 (element == EL_LIGHT_SWITCH ?
4501 level.time_light * FRAMES_PER_SECOND : 0);
4503 RedrawAllLightSwitchesAndInvisibleElements();
4506 static void ActivateTimegateSwitch(int x, int y)
4510 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4513 SCAN_PLAYFIELD(xx, yy)
4515 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4518 int element = Feld[xx][yy];
4520 if (element == EL_TIMEGATE_CLOSED ||
4521 element == EL_TIMEGATE_CLOSING)
4523 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4524 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4528 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4530 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4531 DrawLevelField(xx, yy);
4537 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4540 void Impact(int x, int y)
4542 boolean last_line = (y == lev_fieldy - 1);
4543 boolean object_hit = FALSE;
4544 boolean impact = (last_line || object_hit);
4545 int element = Feld[x][y];
4546 int smashed = EL_STEELWALL;
4548 if (!last_line) /* check if element below was hit */
4550 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4553 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4554 MovDir[x][y + 1] != MV_DOWN ||
4555 MovPos[x][y + 1] <= TILEY / 2));
4557 /* do not smash moving elements that left the smashed field in time */
4558 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4559 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4562 #if USE_QUICKSAND_IMPACT_BUGFIX
4563 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4565 RemoveMovingField(x, y + 1);
4566 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4567 Feld[x][y + 2] = EL_ROCK;
4568 DrawLevelField(x, y + 2);
4575 smashed = MovingOrBlocked2Element(x, y + 1);
4577 impact = (last_line || object_hit);
4580 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4582 SplashAcid(x, y + 1);
4586 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4587 /* only reset graphic animation if graphic really changes after impact */
4589 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4591 ResetGfxAnimation(x, y);
4592 DrawLevelField(x, y);
4595 if (impact && CAN_EXPLODE_IMPACT(element))
4600 else if (impact && element == EL_PEARL)
4602 ResetGfxAnimation(x, y);
4604 Feld[x][y] = EL_PEARL_BREAKING;
4605 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4608 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4610 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4615 if (impact && element == EL_AMOEBA_DROP)
4617 if (object_hit && IS_PLAYER(x, y + 1))
4618 KillPlayerUnlessEnemyProtected(x, y + 1);
4619 else if (object_hit && smashed == EL_PENGUIN)
4623 Feld[x][y] = EL_AMOEBA_GROWING;
4624 Store[x][y] = EL_AMOEBA_WET;
4626 ResetRandomAnimationValue(x, y);
4631 if (object_hit) /* check which object was hit */
4633 if (CAN_PASS_MAGIC_WALL(element) &&
4634 (smashed == EL_MAGIC_WALL ||
4635 smashed == EL_BD_MAGIC_WALL))
4638 int activated_magic_wall =
4639 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4640 EL_BD_MAGIC_WALL_ACTIVE);
4642 /* activate magic wall / mill */
4644 SCAN_PLAYFIELD(xx, yy)
4646 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4648 if (Feld[xx][yy] == smashed)
4649 Feld[xx][yy] = activated_magic_wall;
4651 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4652 game.magic_wall_active = TRUE;
4654 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4655 SND_MAGIC_WALL_ACTIVATING :
4656 SND_BD_MAGIC_WALL_ACTIVATING));
4659 if (IS_PLAYER(x, y + 1))
4661 if (CAN_SMASH_PLAYER(element))
4663 KillPlayerUnlessEnemyProtected(x, y + 1);
4667 else if (smashed == EL_PENGUIN)
4669 if (CAN_SMASH_PLAYER(element))
4675 else if (element == EL_BD_DIAMOND)
4677 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4683 else if (((element == EL_SP_INFOTRON ||
4684 element == EL_SP_ZONK) &&
4685 (smashed == EL_SP_SNIKSNAK ||
4686 smashed == EL_SP_ELECTRON ||
4687 smashed == EL_SP_DISK_ORANGE)) ||
4688 (element == EL_SP_INFOTRON &&
4689 smashed == EL_SP_DISK_YELLOW))
4694 else if (CAN_SMASH_EVERYTHING(element))
4696 if (IS_CLASSIC_ENEMY(smashed) ||
4697 CAN_EXPLODE_SMASHED(smashed))
4702 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4704 if (smashed == EL_LAMP ||
4705 smashed == EL_LAMP_ACTIVE)
4710 else if (smashed == EL_NUT)
4712 Feld[x][y + 1] = EL_NUT_BREAKING;
4713 PlayLevelSound(x, y, SND_NUT_BREAKING);
4714 RaiseScoreElement(EL_NUT);
4717 else if (smashed == EL_PEARL)
4719 ResetGfxAnimation(x, y);
4721 Feld[x][y + 1] = EL_PEARL_BREAKING;
4722 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4725 else if (smashed == EL_DIAMOND)
4727 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4728 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4731 else if (IS_BELT_SWITCH(smashed))
4733 ToggleBeltSwitch(x, y + 1);
4735 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4736 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4738 ToggleSwitchgateSwitch(x, y + 1);
4740 else if (smashed == EL_LIGHT_SWITCH ||
4741 smashed == EL_LIGHT_SWITCH_ACTIVE)
4743 ToggleLightSwitch(x, y + 1);
4748 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4751 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4753 CheckElementChangeBySide(x, y + 1, smashed, element,
4754 CE_SWITCHED, CH_SIDE_TOP);
4755 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4761 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4766 /* play sound of magic wall / mill */
4768 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4769 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4771 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4772 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4773 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4774 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4779 /* play sound of object that hits the ground */
4780 if (last_line || object_hit)
4781 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4784 inline static void TurnRoundExt(int x, int y)
4796 { 0, 0 }, { 0, 0 }, { 0, 0 },
4801 int left, right, back;
4805 { MV_DOWN, MV_UP, MV_RIGHT },
4806 { MV_UP, MV_DOWN, MV_LEFT },
4808 { MV_LEFT, MV_RIGHT, MV_DOWN },
4812 { MV_RIGHT, MV_LEFT, MV_UP }
4815 int element = Feld[x][y];
4816 int move_pattern = element_info[element].move_pattern;
4818 int old_move_dir = MovDir[x][y];
4819 int left_dir = turn[old_move_dir].left;
4820 int right_dir = turn[old_move_dir].right;
4821 int back_dir = turn[old_move_dir].back;
4823 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4824 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4825 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4826 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4828 int left_x = x + left_dx, left_y = y + left_dy;
4829 int right_x = x + right_dx, right_y = y + right_dy;
4830 int move_x = x + move_dx, move_y = y + move_dy;
4834 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4836 TestIfBadThingTouchesOtherBadThing(x, y);
4838 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4839 MovDir[x][y] = right_dir;
4840 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4841 MovDir[x][y] = left_dir;
4843 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4845 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4848 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4850 TestIfBadThingTouchesOtherBadThing(x, y);
4852 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4853 MovDir[x][y] = left_dir;
4854 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4855 MovDir[x][y] = right_dir;
4857 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4859 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4862 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4864 TestIfBadThingTouchesOtherBadThing(x, y);
4866 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4867 MovDir[x][y] = left_dir;
4868 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4869 MovDir[x][y] = right_dir;
4871 if (MovDir[x][y] != old_move_dir)
4874 else if (element == EL_YAMYAM)
4876 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4877 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4879 if (can_turn_left && can_turn_right)
4880 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4881 else if (can_turn_left)
4882 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4883 else if (can_turn_right)
4884 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4886 MovDir[x][y] = back_dir;
4888 MovDelay[x][y] = 16 + 16 * RND(3);
4890 else if (element == EL_DARK_YAMYAM)
4892 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4894 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4897 if (can_turn_left && can_turn_right)
4898 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4899 else if (can_turn_left)
4900 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4901 else if (can_turn_right)
4902 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4904 MovDir[x][y] = back_dir;
4906 MovDelay[x][y] = 16 + 16 * RND(3);
4908 else if (element == EL_PACMAN)
4910 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4911 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4913 if (can_turn_left && can_turn_right)
4914 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4915 else if (can_turn_left)
4916 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4917 else if (can_turn_right)
4918 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4920 MovDir[x][y] = back_dir;
4922 MovDelay[x][y] = 6 + RND(40);
4924 else if (element == EL_PIG)
4926 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4927 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4928 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4929 boolean should_turn_left, should_turn_right, should_move_on;
4931 int rnd = RND(rnd_value);
4933 should_turn_left = (can_turn_left &&
4935 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4936 y + back_dy + left_dy)));
4937 should_turn_right = (can_turn_right &&
4939 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4940 y + back_dy + right_dy)));
4941 should_move_on = (can_move_on &&
4944 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4945 y + move_dy + left_dy) ||
4946 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4947 y + move_dy + right_dy)));
4949 if (should_turn_left || should_turn_right || should_move_on)
4951 if (should_turn_left && should_turn_right && should_move_on)
4952 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4953 rnd < 2 * rnd_value / 3 ? right_dir :
4955 else if (should_turn_left && should_turn_right)
4956 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4957 else if (should_turn_left && should_move_on)
4958 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4959 else if (should_turn_right && should_move_on)
4960 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4961 else if (should_turn_left)
4962 MovDir[x][y] = left_dir;
4963 else if (should_turn_right)
4964 MovDir[x][y] = right_dir;
4965 else if (should_move_on)
4966 MovDir[x][y] = old_move_dir;
4968 else if (can_move_on && rnd > rnd_value / 8)
4969 MovDir[x][y] = old_move_dir;
4970 else if (can_turn_left && can_turn_right)
4971 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4972 else if (can_turn_left && rnd > rnd_value / 8)
4973 MovDir[x][y] = left_dir;
4974 else if (can_turn_right && rnd > rnd_value/8)
4975 MovDir[x][y] = right_dir;
4977 MovDir[x][y] = back_dir;
4979 xx = x + move_xy[MovDir[x][y]].dx;
4980 yy = y + move_xy[MovDir[x][y]].dy;
4982 if (!IN_LEV_FIELD(xx, yy) ||
4983 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4984 MovDir[x][y] = old_move_dir;
4988 else if (element == EL_DRAGON)
4990 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4991 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4992 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4994 int rnd = RND(rnd_value);
4996 if (can_move_on && rnd > rnd_value / 8)
4997 MovDir[x][y] = old_move_dir;
4998 else if (can_turn_left && can_turn_right)
4999 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5000 else if (can_turn_left && rnd > rnd_value / 8)
5001 MovDir[x][y] = left_dir;
5002 else if (can_turn_right && rnd > rnd_value / 8)
5003 MovDir[x][y] = right_dir;
5005 MovDir[x][y] = back_dir;
5007 xx = x + move_xy[MovDir[x][y]].dx;
5008 yy = y + move_xy[MovDir[x][y]].dy;
5010 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5011 MovDir[x][y] = old_move_dir;
5015 else if (element == EL_MOLE)
5017 boolean can_move_on =
5018 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5019 IS_AMOEBOID(Feld[move_x][move_y]) ||
5020 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5023 boolean can_turn_left =
5024 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5025 IS_AMOEBOID(Feld[left_x][left_y])));
5027 boolean can_turn_right =
5028 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5029 IS_AMOEBOID(Feld[right_x][right_y])));
5031 if (can_turn_left && can_turn_right)
5032 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5033 else if (can_turn_left)
5034 MovDir[x][y] = left_dir;
5036 MovDir[x][y] = right_dir;
5039 if (MovDir[x][y] != old_move_dir)
5042 else if (element == EL_BALLOON)
5044 MovDir[x][y] = game.wind_direction;
5047 else if (element == EL_SPRING)
5049 #if USE_NEW_SPRING_BUMPER
5050 if (MovDir[x][y] & MV_HORIZONTAL)
5052 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5053 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5055 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5056 ResetGfxAnimation(move_x, move_y);
5057 DrawLevelField(move_x, move_y);
5059 MovDir[x][y] = back_dir;
5061 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5062 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5063 MovDir[x][y] = MV_NONE;
5066 if (MovDir[x][y] & MV_HORIZONTAL &&
5067 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5068 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5069 MovDir[x][y] = MV_NONE;
5074 else if (element == EL_ROBOT ||
5075 element == EL_SATELLITE ||
5076 element == EL_PENGUIN ||
5077 element == EL_EMC_ANDROID)
5079 int attr_x = -1, attr_y = -1;
5090 for (i = 0; i < MAX_PLAYERS; i++)
5092 struct PlayerInfo *player = &stored_player[i];
5093 int jx = player->jx, jy = player->jy;
5095 if (!player->active)
5099 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5107 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5108 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5109 game.engine_version < VERSION_IDENT(3,1,0,0)))
5115 if (element == EL_PENGUIN)
5118 static int xy[4][2] =
5126 for (i = 0; i < NUM_DIRECTIONS; i++)
5128 int ex = x + xy[i][0];
5129 int ey = y + xy[i][1];
5131 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5140 MovDir[x][y] = MV_NONE;
5142 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5143 else if (attr_x > x)
5144 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5146 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5147 else if (attr_y > y)
5148 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5150 if (element == EL_ROBOT)
5154 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5155 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5156 Moving2Blocked(x, y, &newx, &newy);
5158 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5159 MovDelay[x][y] = 8 + 8 * !RND(3);
5161 MovDelay[x][y] = 16;
5163 else if (element == EL_PENGUIN)
5169 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5171 boolean first_horiz = RND(2);
5172 int new_move_dir = MovDir[x][y];
5175 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5176 Moving2Blocked(x, y, &newx, &newy);
5178 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5182 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5183 Moving2Blocked(x, y, &newx, &newy);
5185 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5188 MovDir[x][y] = old_move_dir;
5192 else if (element == EL_SATELLITE)
5198 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5200 boolean first_horiz = RND(2);
5201 int new_move_dir = MovDir[x][y];
5204 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5205 Moving2Blocked(x, y, &newx, &newy);
5207 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5211 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5212 Moving2Blocked(x, y, &newx, &newy);
5214 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5217 MovDir[x][y] = old_move_dir;
5221 else if (element == EL_EMC_ANDROID)
5223 static int check_pos[16] =
5225 -1, /* 0 => (invalid) */
5226 7, /* 1 => MV_LEFT */
5227 3, /* 2 => MV_RIGHT */
5228 -1, /* 3 => (invalid) */
5230 0, /* 5 => MV_LEFT | MV_UP */
5231 2, /* 6 => MV_RIGHT | MV_UP */
5232 -1, /* 7 => (invalid) */
5233 5, /* 8 => MV_DOWN */
5234 6, /* 9 => MV_LEFT | MV_DOWN */
5235 4, /* 10 => MV_RIGHT | MV_DOWN */
5236 -1, /* 11 => (invalid) */
5237 -1, /* 12 => (invalid) */
5238 -1, /* 13 => (invalid) */
5239 -1, /* 14 => (invalid) */
5240 -1, /* 15 => (invalid) */
5248 { -1, -1, MV_LEFT | MV_UP },
5250 { +1, -1, MV_RIGHT | MV_UP },
5251 { +1, 0, MV_RIGHT },
5252 { +1, +1, MV_RIGHT | MV_DOWN },
5254 { -1, +1, MV_LEFT | MV_DOWN },
5257 int start_pos, check_order;
5258 boolean can_clone = FALSE;
5261 /* check if there is any free field around current position */
5262 for (i = 0; i < 8; i++)
5264 int newx = x + check_xy[i].dx;
5265 int newy = y + check_xy[i].dy;
5267 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5275 if (can_clone) /* randomly find an element to clone */
5279 start_pos = check_pos[RND(8)];
5280 check_order = (RND(2) ? -1 : +1);
5282 for (i = 0; i < 8; i++)
5284 int pos_raw = start_pos + i * check_order;
5285 int pos = (pos_raw + 8) % 8;
5286 int newx = x + check_xy[pos].dx;
5287 int newy = y + check_xy[pos].dy;
5289 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5291 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5292 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5294 Store[x][y] = Feld[newx][newy];
5303 if (can_clone) /* randomly find a direction to move */
5307 start_pos = check_pos[RND(8)];
5308 check_order = (RND(2) ? -1 : +1);
5310 for (i = 0; i < 8; i++)
5312 int pos_raw = start_pos + i * check_order;
5313 int pos = (pos_raw + 8) % 8;
5314 int newx = x + check_xy[pos].dx;
5315 int newy = y + check_xy[pos].dy;
5316 int new_move_dir = check_xy[pos].dir;
5318 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5320 MovDir[x][y] = new_move_dir;
5321 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5330 if (can_clone) /* cloning and moving successful */
5333 /* cannot clone -- try to move towards player */
5335 start_pos = check_pos[MovDir[x][y] & 0x0f];
5336 check_order = (RND(2) ? -1 : +1);
5338 for (i = 0; i < 3; i++)
5340 /* first check start_pos, then previous/next or (next/previous) pos */
5341 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5342 int pos = (pos_raw + 8) % 8;
5343 int newx = x + check_xy[pos].dx;
5344 int newy = y + check_xy[pos].dy;
5345 int new_move_dir = check_xy[pos].dir;
5347 if (IS_PLAYER(newx, newy))
5350 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5352 MovDir[x][y] = new_move_dir;
5353 MovDelay[x][y] = level.android_move_time * 8 + 1;
5360 else if (move_pattern == MV_TURNING_LEFT ||
5361 move_pattern == MV_TURNING_RIGHT ||
5362 move_pattern == MV_TURNING_LEFT_RIGHT ||
5363 move_pattern == MV_TURNING_RIGHT_LEFT ||
5364 move_pattern == MV_TURNING_RANDOM ||
5365 move_pattern == MV_ALL_DIRECTIONS)
5367 boolean can_turn_left =
5368 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5369 boolean can_turn_right =
5370 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5372 if (element_info[element].move_stepsize == 0) /* "not moving" */
5375 if (move_pattern == MV_TURNING_LEFT)
5376 MovDir[x][y] = left_dir;
5377 else if (move_pattern == MV_TURNING_RIGHT)
5378 MovDir[x][y] = right_dir;
5379 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5380 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5381 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5382 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5383 else if (move_pattern == MV_TURNING_RANDOM)
5384 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5385 can_turn_right && !can_turn_left ? right_dir :
5386 RND(2) ? left_dir : right_dir);
5387 else if (can_turn_left && can_turn_right)
5388 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5389 else if (can_turn_left)
5390 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5391 else if (can_turn_right)
5392 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5394 MovDir[x][y] = back_dir;
5396 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5398 else if (move_pattern == MV_HORIZONTAL ||
5399 move_pattern == MV_VERTICAL)
5401 if (move_pattern & old_move_dir)
5402 MovDir[x][y] = back_dir;
5403 else if (move_pattern == MV_HORIZONTAL)
5404 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5405 else if (move_pattern == MV_VERTICAL)
5406 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5408 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5410 else if (move_pattern & MV_ANY_DIRECTION)
5412 MovDir[x][y] = move_pattern;
5413 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5415 else if (move_pattern & MV_WIND_DIRECTION)
5417 MovDir[x][y] = game.wind_direction;
5418 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5420 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5422 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5423 MovDir[x][y] = left_dir;
5424 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5425 MovDir[x][y] = right_dir;
5427 if (MovDir[x][y] != old_move_dir)
5428 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5430 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5432 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5433 MovDir[x][y] = right_dir;
5434 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5435 MovDir[x][y] = left_dir;
5437 if (MovDir[x][y] != old_move_dir)
5438 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5440 else if (move_pattern == MV_TOWARDS_PLAYER ||
5441 move_pattern == MV_AWAY_FROM_PLAYER)
5443 int attr_x = -1, attr_y = -1;
5445 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5456 for (i = 0; i < MAX_PLAYERS; i++)
5458 struct PlayerInfo *player = &stored_player[i];
5459 int jx = player->jx, jy = player->jy;
5461 if (!player->active)
5465 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5473 MovDir[x][y] = MV_NONE;
5475 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5476 else if (attr_x > x)
5477 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5479 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5480 else if (attr_y > y)
5481 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5483 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5485 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5487 boolean first_horiz = RND(2);
5488 int new_move_dir = MovDir[x][y];
5490 if (element_info[element].move_stepsize == 0) /* "not moving" */
5492 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5493 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5499 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5500 Moving2Blocked(x, y, &newx, &newy);
5502 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5506 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5507 Moving2Blocked(x, y, &newx, &newy);
5509 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5512 MovDir[x][y] = old_move_dir;
5515 else if (move_pattern == MV_WHEN_PUSHED ||
5516 move_pattern == MV_WHEN_DROPPED)
5518 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5519 MovDir[x][y] = MV_NONE;
5523 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5525 static int test_xy[7][2] =
5535 static int test_dir[7] =
5545 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5546 int move_preference = -1000000; /* start with very low preference */
5547 int new_move_dir = MV_NONE;
5548 int start_test = RND(4);
5551 for (i = 0; i < NUM_DIRECTIONS; i++)
5553 int move_dir = test_dir[start_test + i];
5554 int move_dir_preference;
5556 xx = x + test_xy[start_test + i][0];
5557 yy = y + test_xy[start_test + i][1];
5559 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5560 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5562 new_move_dir = move_dir;
5567 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5570 move_dir_preference = -1 * RunnerVisit[xx][yy];
5571 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5572 move_dir_preference = PlayerVisit[xx][yy];
5574 if (move_dir_preference > move_preference)
5576 /* prefer field that has not been visited for the longest time */
5577 move_preference = move_dir_preference;
5578 new_move_dir = move_dir;
5580 else if (move_dir_preference == move_preference &&
5581 move_dir == old_move_dir)
5583 /* prefer last direction when all directions are preferred equally */
5584 move_preference = move_dir_preference;
5585 new_move_dir = move_dir;
5589 MovDir[x][y] = new_move_dir;
5590 if (old_move_dir != new_move_dir)
5591 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5595 static void TurnRound(int x, int y)
5597 int direction = MovDir[x][y];
5599 int element, graphic;
5604 GfxDir[x][y] = MovDir[x][y];
5606 if (direction != MovDir[x][y])
5610 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5613 element = Feld[x][y];
5614 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5616 if (graphic_info[graphic].anim_global_sync)
5617 GfxFrame[x][y] = FrameCounter;
5618 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5619 GfxFrame[x][y] = CustomValue[x][y];
5620 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5621 GfxFrame[x][y] = element_info[element].collect_score;
5625 static boolean JustBeingPushed(int x, int y)
5629 for (i = 0; i < MAX_PLAYERS; i++)
5631 struct PlayerInfo *player = &stored_player[i];
5633 if (player->active && player->is_pushing && player->MovPos)
5635 int next_jx = player->jx + (player->jx - player->last_jx);
5636 int next_jy = player->jy + (player->jy - player->last_jy);
5638 if (x == next_jx && y == next_jy)
5646 void StartMoving(int x, int y)
5648 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5649 int element = Feld[x][y];
5654 if (MovDelay[x][y] == 0)
5655 GfxAction[x][y] = ACTION_DEFAULT;
5657 if (CAN_FALL(element) && y < lev_fieldy - 1)
5659 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5660 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5661 if (JustBeingPushed(x, y))
5664 if (element == EL_QUICKSAND_FULL)
5666 if (IS_FREE(x, y + 1))
5668 InitMovingField(x, y, MV_DOWN);
5669 started_moving = TRUE;
5671 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5672 Store[x][y] = EL_ROCK;
5674 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5676 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5678 if (!MovDelay[x][y])
5679 MovDelay[x][y] = TILEY + 1;
5688 Feld[x][y] = EL_QUICKSAND_EMPTY;
5689 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5690 Store[x][y + 1] = Store[x][y];
5693 PlayLevelSoundAction(x, y, ACTION_FILLING);
5696 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5697 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5699 InitMovingField(x, y, MV_DOWN);
5700 started_moving = TRUE;
5702 Feld[x][y] = EL_QUICKSAND_FILLING;
5703 Store[x][y] = element;
5705 PlayLevelSoundAction(x, y, ACTION_FILLING);
5707 else if (element == EL_MAGIC_WALL_FULL)
5709 if (IS_FREE(x, y + 1))
5711 InitMovingField(x, y, MV_DOWN);
5712 started_moving = TRUE;
5714 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5715 Store[x][y] = EL_CHANGED(Store[x][y]);
5717 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5719 if (!MovDelay[x][y])
5720 MovDelay[x][y] = TILEY/4 + 1;
5729 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5730 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5731 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5735 else if (element == EL_BD_MAGIC_WALL_FULL)
5737 if (IS_FREE(x, y + 1))
5739 InitMovingField(x, y, MV_DOWN);
5740 started_moving = TRUE;
5742 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5743 Store[x][y] = EL_CHANGED2(Store[x][y]);
5745 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5747 if (!MovDelay[x][y])
5748 MovDelay[x][y] = TILEY/4 + 1;
5757 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5758 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5759 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5763 else if (CAN_PASS_MAGIC_WALL(element) &&
5764 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5765 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5767 InitMovingField(x, y, MV_DOWN);
5768 started_moving = TRUE;
5771 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5772 EL_BD_MAGIC_WALL_FILLING);
5773 Store[x][y] = element;
5775 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5777 SplashAcid(x, y + 1);
5779 InitMovingField(x, y, MV_DOWN);
5780 started_moving = TRUE;
5782 Store[x][y] = EL_ACID;
5784 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5785 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5787 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5788 CAN_FALL(element) && WasJustFalling[x][y] &&
5789 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5791 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5792 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5793 (Feld[x][y + 1] == EL_BLOCKED)))
5795 /* this is needed for a special case not covered by calling "Impact()"
5796 from "ContinueMoving()": if an element moves to a tile directly below
5797 another element which was just falling on that tile (which was empty
5798 in the previous frame), the falling element above would just stop
5799 instead of smashing the element below (in previous version, the above
5800 element was just checked for "moving" instead of "falling", resulting
5801 in incorrect smashes caused by horizontal movement of the above
5802 element; also, the case of the player being the element to smash was
5803 simply not covered here... :-/ ) */
5805 CheckCollision[x][y] = 0;
5809 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5811 if (MovDir[x][y] == MV_NONE)
5813 InitMovingField(x, y, MV_DOWN);
5814 started_moving = TRUE;
5817 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5819 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5820 MovDir[x][y] = MV_DOWN;
5822 InitMovingField(x, y, MV_DOWN);
5823 started_moving = TRUE;
5825 else if (element == EL_AMOEBA_DROP)
5827 Feld[x][y] = EL_AMOEBA_GROWING;
5828 Store[x][y] = EL_AMOEBA_WET;
5830 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5831 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5832 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5833 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5835 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5836 (IS_FREE(x - 1, y + 1) ||
5837 Feld[x - 1][y + 1] == EL_ACID));
5838 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5839 (IS_FREE(x + 1, y + 1) ||
5840 Feld[x + 1][y + 1] == EL_ACID));
5841 boolean can_fall_any = (can_fall_left || can_fall_right);
5842 boolean can_fall_both = (can_fall_left && can_fall_right);
5843 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5845 #if USE_NEW_ALL_SLIPPERY
5846 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5848 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5849 can_fall_right = FALSE;
5850 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5851 can_fall_left = FALSE;
5852 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5853 can_fall_right = FALSE;
5854 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5855 can_fall_left = FALSE;
5857 can_fall_any = (can_fall_left || can_fall_right);
5858 can_fall_both = FALSE;
5861 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5863 if (slippery_type == SLIPPERY_ONLY_LEFT)
5864 can_fall_right = FALSE;
5865 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5866 can_fall_left = FALSE;
5867 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5868 can_fall_right = FALSE;
5869 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5870 can_fall_left = FALSE;
5872 can_fall_any = (can_fall_left || can_fall_right);
5873 can_fall_both = (can_fall_left && can_fall_right);
5877 #if USE_NEW_ALL_SLIPPERY
5879 #if USE_NEW_SP_SLIPPERY
5880 /* !!! better use the same properties as for custom elements here !!! */
5881 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5882 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5884 can_fall_right = FALSE; /* slip down on left side */
5885 can_fall_both = FALSE;
5890 #if USE_NEW_ALL_SLIPPERY
5893 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5894 can_fall_right = FALSE; /* slip down on left side */
5896 can_fall_left = !(can_fall_right = RND(2));
5898 can_fall_both = FALSE;
5903 if (game.emulation == EMU_BOULDERDASH ||
5904 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5905 can_fall_right = FALSE; /* slip down on left side */
5907 can_fall_left = !(can_fall_right = RND(2));
5909 can_fall_both = FALSE;
5915 /* if not determined otherwise, prefer left side for slipping down */
5916 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5917 started_moving = TRUE;
5921 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5923 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5926 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5927 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5928 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5929 int belt_dir = game.belt_dir[belt_nr];
5931 if ((belt_dir == MV_LEFT && left_is_free) ||
5932 (belt_dir == MV_RIGHT && right_is_free))
5934 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5936 InitMovingField(x, y, belt_dir);
5937 started_moving = TRUE;
5939 Pushed[x][y] = TRUE;
5940 Pushed[nextx][y] = TRUE;
5942 GfxAction[x][y] = ACTION_DEFAULT;
5946 MovDir[x][y] = 0; /* if element was moving, stop it */
5951 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5953 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5955 if (CAN_MOVE(element) && !started_moving)
5958 int move_pattern = element_info[element].move_pattern;
5963 if (MovDir[x][y] == MV_NONE)
5965 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5966 x, y, element, element_info[element].token_name);
5967 printf("StartMoving(): This should never happen!\n");
5972 Moving2Blocked(x, y, &newx, &newy);
5974 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5977 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5978 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5980 WasJustMoving[x][y] = 0;
5981 CheckCollision[x][y] = 0;
5983 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5985 if (Feld[x][y] != element) /* element has changed */
5989 if (!MovDelay[x][y]) /* start new movement phase */
5991 /* all objects that can change their move direction after each step
5992 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5994 if (element != EL_YAMYAM &&
5995 element != EL_DARK_YAMYAM &&
5996 element != EL_PACMAN &&
5997 !(move_pattern & MV_ANY_DIRECTION) &&
5998 move_pattern != MV_TURNING_LEFT &&
5999 move_pattern != MV_TURNING_RIGHT &&
6000 move_pattern != MV_TURNING_LEFT_RIGHT &&
6001 move_pattern != MV_TURNING_RIGHT_LEFT &&
6002 move_pattern != MV_TURNING_RANDOM)
6006 if (MovDelay[x][y] && (element == EL_BUG ||
6007 element == EL_SPACESHIP ||
6008 element == EL_SP_SNIKSNAK ||
6009 element == EL_SP_ELECTRON ||
6010 element == EL_MOLE))
6011 DrawLevelField(x, y);
6015 if (MovDelay[x][y]) /* wait some time before next movement */
6019 if (element == EL_ROBOT ||
6020 element == EL_YAMYAM ||
6021 element == EL_DARK_YAMYAM)
6023 DrawLevelElementAnimationIfNeeded(x, y, element);
6024 PlayLevelSoundAction(x, y, ACTION_WAITING);
6026 else if (element == EL_SP_ELECTRON)
6027 DrawLevelElementAnimationIfNeeded(x, y, element);
6028 else if (element == EL_DRAGON)
6031 int dir = MovDir[x][y];
6032 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6033 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6034 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6035 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6036 dir == MV_UP ? IMG_FLAMES_1_UP :
6037 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6038 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6040 GfxAction[x][y] = ACTION_ATTACKING;
6042 if (IS_PLAYER(x, y))
6043 DrawPlayerField(x, y);
6045 DrawLevelField(x, y);
6047 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6049 for (i = 1; i <= 3; i++)
6051 int xx = x + i * dx;
6052 int yy = y + i * dy;
6053 int sx = SCREENX(xx);
6054 int sy = SCREENY(yy);
6055 int flame_graphic = graphic + (i - 1);
6057 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6062 int flamed = MovingOrBlocked2Element(xx, yy);
6066 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6068 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6069 RemoveMovingField(xx, yy);
6071 RemoveField(xx, yy);
6073 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6076 RemoveMovingField(xx, yy);
6079 ChangeDelay[xx][yy] = 0;
6081 Feld[xx][yy] = EL_FLAMES;
6083 if (IN_SCR_FIELD(sx, sy))
6085 DrawLevelFieldCrumbledSand(xx, yy);
6086 DrawGraphic(sx, sy, flame_graphic, frame);
6091 if (Feld[xx][yy] == EL_FLAMES)
6092 Feld[xx][yy] = EL_EMPTY;
6093 DrawLevelField(xx, yy);
6098 if (MovDelay[x][y]) /* element still has to wait some time */
6100 PlayLevelSoundAction(x, y, ACTION_WAITING);
6106 /* now make next step */
6108 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6110 if (DONT_COLLIDE_WITH(element) &&
6111 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6112 !PLAYER_ENEMY_PROTECTED(newx, newy))
6114 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6119 else if (CAN_MOVE_INTO_ACID(element) &&
6120 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6121 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6122 (MovDir[x][y] == MV_DOWN ||
6123 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6125 SplashAcid(newx, newy);
6126 Store[x][y] = EL_ACID;
6128 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6130 if (Feld[newx][newy] == EL_EXIT_OPEN)
6133 DrawLevelField(x, y);
6135 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6136 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6137 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6139 local_player->friends_still_needed--;
6140 if (!local_player->friends_still_needed &&
6141 !local_player->GameOver && AllPlayersGone)
6142 local_player->LevelSolved = local_player->GameOver = TRUE;
6146 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6148 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6149 DrawLevelField(newx, newy);
6151 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6153 else if (!IS_FREE(newx, newy))
6155 GfxAction[x][y] = ACTION_WAITING;
6157 if (IS_PLAYER(x, y))
6158 DrawPlayerField(x, y);
6160 DrawLevelField(x, y);
6165 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6167 if (IS_FOOD_PIG(Feld[newx][newy]))
6169 if (IS_MOVING(newx, newy))
6170 RemoveMovingField(newx, newy);
6173 Feld[newx][newy] = EL_EMPTY;
6174 DrawLevelField(newx, newy);
6177 PlayLevelSound(x, y, SND_PIG_DIGGING);
6179 else if (!IS_FREE(newx, newy))
6181 if (IS_PLAYER(x, y))
6182 DrawPlayerField(x, y);
6184 DrawLevelField(x, y);
6189 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6191 if (Store[x][y] != EL_EMPTY)
6193 boolean can_clone = FALSE;
6196 /* check if element to clone is still there */
6197 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6199 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6207 /* cannot clone or target field not free anymore -- do not clone */
6208 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6209 Store[x][y] = EL_EMPTY;
6212 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6214 if (IS_MV_DIAGONAL(MovDir[x][y]))
6216 int diagonal_move_dir = MovDir[x][y];
6217 int stored = Store[x][y];
6218 int change_delay = 8;
6221 /* android is moving diagonally */
6223 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6225 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6226 GfxElement[x][y] = EL_EMC_ANDROID;
6227 GfxAction[x][y] = ACTION_SHRINKING;
6228 GfxDir[x][y] = diagonal_move_dir;
6229 ChangeDelay[x][y] = change_delay;
6231 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6234 DrawLevelGraphicAnimation(x, y, graphic);
6235 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6237 if (Feld[newx][newy] == EL_ACID)
6239 SplashAcid(newx, newy);
6244 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6246 Store[newx][newy] = EL_EMC_ANDROID;
6247 GfxElement[newx][newy] = EL_EMC_ANDROID;
6248 GfxAction[newx][newy] = ACTION_GROWING;
6249 GfxDir[newx][newy] = diagonal_move_dir;
6250 ChangeDelay[newx][newy] = change_delay;
6252 graphic = el_act_dir2img(GfxElement[newx][newy],
6253 GfxAction[newx][newy], GfxDir[newx][newy]);
6255 DrawLevelGraphicAnimation(newx, newy, graphic);
6256 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6262 Feld[newx][newy] = EL_EMPTY;
6263 DrawLevelField(newx, newy);
6265 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6268 else if (!IS_FREE(newx, newy))
6271 if (IS_PLAYER(x, y))
6272 DrawPlayerField(x, y);
6274 DrawLevelField(x, y);
6280 else if (IS_CUSTOM_ELEMENT(element) &&
6281 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6283 int new_element = Feld[newx][newy];
6285 if (!IS_FREE(newx, newy))
6287 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6288 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6291 /* no element can dig solid indestructible elements */
6292 if (IS_INDESTRUCTIBLE(new_element) &&
6293 !IS_DIGGABLE(new_element) &&
6294 !IS_COLLECTIBLE(new_element))
6297 if (AmoebaNr[newx][newy] &&
6298 (new_element == EL_AMOEBA_FULL ||
6299 new_element == EL_BD_AMOEBA ||
6300 new_element == EL_AMOEBA_GROWING))
6302 AmoebaCnt[AmoebaNr[newx][newy]]--;
6303 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6306 if (IS_MOVING(newx, newy))
6307 RemoveMovingField(newx, newy);
6310 RemoveField(newx, newy);
6311 DrawLevelField(newx, newy);
6314 /* if digged element was about to explode, prevent the explosion */
6315 ExplodeField[newx][newy] = EX_TYPE_NONE;
6317 PlayLevelSoundAction(x, y, action);
6320 Store[newx][newy] = EL_EMPTY;
6322 /* this makes it possible to leave the removed element again */
6323 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6324 Store[newx][newy] = new_element;
6326 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6328 int move_leave_element = element_info[element].move_leave_element;
6330 /* this makes it possible to leave the removed element again */
6331 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6332 new_element : move_leave_element);
6336 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6338 RunnerVisit[x][y] = FrameCounter;
6339 PlayerVisit[x][y] /= 8; /* expire player visit path */
6342 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6344 if (!IS_FREE(newx, newy))
6346 if (IS_PLAYER(x, y))
6347 DrawPlayerField(x, y);
6349 DrawLevelField(x, y);
6355 boolean wanna_flame = !RND(10);
6356 int dx = newx - x, dy = newy - y;
6357 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6358 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6359 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6360 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6361 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6362 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6365 IS_CLASSIC_ENEMY(element1) ||
6366 IS_CLASSIC_ENEMY(element2)) &&
6367 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6368 element1 != EL_FLAMES && element2 != EL_FLAMES)
6370 ResetGfxAnimation(x, y);
6371 GfxAction[x][y] = ACTION_ATTACKING;
6373 if (IS_PLAYER(x, y))
6374 DrawPlayerField(x, y);
6376 DrawLevelField(x, y);
6378 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6380 MovDelay[x][y] = 50;
6384 RemoveField(newx, newy);
6386 Feld[newx][newy] = EL_FLAMES;
6387 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6390 RemoveField(newx1, newy1);
6392 Feld[newx1][newy1] = EL_FLAMES;
6394 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6397 RemoveField(newx2, newy2);
6399 Feld[newx2][newy2] = EL_FLAMES;
6406 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6407 Feld[newx][newy] == EL_DIAMOND)
6409 if (IS_MOVING(newx, newy))
6410 RemoveMovingField(newx, newy);
6413 Feld[newx][newy] = EL_EMPTY;
6414 DrawLevelField(newx, newy);
6417 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6419 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6420 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6422 if (AmoebaNr[newx][newy])
6424 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6425 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6426 Feld[newx][newy] == EL_BD_AMOEBA)
6427 AmoebaCnt[AmoebaNr[newx][newy]]--;
6432 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6434 RemoveMovingField(newx, newy);
6437 if (IS_MOVING(newx, newy))
6439 RemoveMovingField(newx, newy);
6444 Feld[newx][newy] = EL_EMPTY;
6445 DrawLevelField(newx, newy);
6448 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6450 else if ((element == EL_PACMAN || element == EL_MOLE)
6451 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6453 if (AmoebaNr[newx][newy])
6455 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6456 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6457 Feld[newx][newy] == EL_BD_AMOEBA)
6458 AmoebaCnt[AmoebaNr[newx][newy]]--;
6461 if (element == EL_MOLE)
6463 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6464 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6466 ResetGfxAnimation(x, y);
6467 GfxAction[x][y] = ACTION_DIGGING;
6468 DrawLevelField(x, y);
6470 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6472 return; /* wait for shrinking amoeba */
6474 else /* element == EL_PACMAN */
6476 Feld[newx][newy] = EL_EMPTY;
6477 DrawLevelField(newx, newy);
6478 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6481 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6482 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6483 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6485 /* wait for shrinking amoeba to completely disappear */
6488 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6490 /* object was running against a wall */
6495 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6496 if (move_pattern & MV_ANY_DIRECTION &&
6497 move_pattern == MovDir[x][y])
6499 int blocking_element =
6500 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6502 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6505 element = Feld[x][y]; /* element might have changed */
6509 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6510 DrawLevelElementAnimation(x, y, element);
6512 if (DONT_TOUCH(element))
6513 TestIfBadThingTouchesPlayer(x, y);
6518 InitMovingField(x, y, MovDir[x][y]);
6520 PlayLevelSoundAction(x, y, ACTION_MOVING);
6524 ContinueMoving(x, y);
6527 void ContinueMoving(int x, int y)
6529 int element = Feld[x][y];
6530 struct ElementInfo *ei = &element_info[element];
6531 int direction = MovDir[x][y];
6532 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6533 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6534 int newx = x + dx, newy = y + dy;
6535 int stored = Store[x][y];
6536 int stored_new = Store[newx][newy];
6537 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6538 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6539 boolean last_line = (newy == lev_fieldy - 1);
6541 MovPos[x][y] += getElementMoveStepsize(x, y);
6543 if (pushed_by_player) /* special case: moving object pushed by player */
6544 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6546 if (ABS(MovPos[x][y]) < TILEX)
6548 DrawLevelField(x, y);
6550 return; /* element is still moving */
6553 /* element reached destination field */
6555 Feld[x][y] = EL_EMPTY;
6556 Feld[newx][newy] = element;
6557 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6559 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6561 element = Feld[newx][newy] = EL_ACID;
6563 else if (element == EL_MOLE)
6565 Feld[x][y] = EL_SAND;
6567 DrawLevelFieldCrumbledSandNeighbours(x, y);
6569 else if (element == EL_QUICKSAND_FILLING)
6571 element = Feld[newx][newy] = get_next_element(element);
6572 Store[newx][newy] = Store[x][y];
6574 else if (element == EL_QUICKSAND_EMPTYING)
6576 Feld[x][y] = get_next_element(element);
6577 element = Feld[newx][newy] = Store[x][y];
6579 else if (element == EL_MAGIC_WALL_FILLING)
6581 element = Feld[newx][newy] = get_next_element(element);
6582 if (!game.magic_wall_active)
6583 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6584 Store[newx][newy] = Store[x][y];
6586 else if (element == EL_MAGIC_WALL_EMPTYING)
6588 Feld[x][y] = get_next_element(element);
6589 if (!game.magic_wall_active)
6590 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6591 element = Feld[newx][newy] = Store[x][y];
6593 #if USE_NEW_CUSTOM_VALUE
6594 InitField(newx, newy, FALSE);
6597 else if (element == EL_BD_MAGIC_WALL_FILLING)
6599 element = Feld[newx][newy] = get_next_element(element);
6600 if (!game.magic_wall_active)
6601 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6602 Store[newx][newy] = Store[x][y];
6604 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6606 Feld[x][y] = get_next_element(element);
6607 if (!game.magic_wall_active)
6608 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6609 element = Feld[newx][newy] = Store[x][y];
6611 #if USE_NEW_CUSTOM_VALUE
6612 InitField(newx, newy, FALSE);
6615 else if (element == EL_AMOEBA_DROPPING)
6617 Feld[x][y] = get_next_element(element);
6618 element = Feld[newx][newy] = Store[x][y];
6620 else if (element == EL_SOKOBAN_OBJECT)
6623 Feld[x][y] = Back[x][y];
6625 if (Back[newx][newy])
6626 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6628 Back[x][y] = Back[newx][newy] = 0;
6631 Store[x][y] = EL_EMPTY;
6636 MovDelay[newx][newy] = 0;
6639 if (CAN_CHANGE_OR_HAS_ACTION(element))
6641 if (CAN_CHANGE(element))
6644 /* copy element change control values to new field */
6645 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6646 ChangePage[newx][newy] = ChangePage[x][y];
6647 ChangeCount[newx][newy] = ChangeCount[x][y];
6648 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6651 #if USE_NEW_CUSTOM_VALUE
6652 CustomValue[newx][newy] = CustomValue[x][y];
6658 #if USE_NEW_CUSTOM_VALUE
6659 CustomValue[newx][newy] = CustomValue[x][y];
6663 ChangeDelay[x][y] = 0;
6664 ChangePage[x][y] = -1;
6665 ChangeCount[x][y] = 0;
6666 ChangeEvent[x][y] = -1;
6668 #if USE_NEW_CUSTOM_VALUE
6669 CustomValue[x][y] = 0;
6672 /* copy animation control values to new field */
6673 GfxFrame[newx][newy] = GfxFrame[x][y];
6674 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6675 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6676 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6678 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6680 /* some elements can leave other elements behind after moving */
6682 if (ei->move_leave_element != EL_EMPTY &&
6683 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6684 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6686 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6687 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6688 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6691 int move_leave_element = ei->move_leave_element;
6695 /* this makes it possible to leave the removed element again */
6696 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6697 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6699 /* this makes it possible to leave the removed element again */
6700 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6701 move_leave_element = stored;
6704 /* this makes it possible to leave the removed element again */
6705 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6706 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6707 move_leave_element = stored;
6710 Feld[x][y] = move_leave_element;
6712 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6713 MovDir[x][y] = direction;
6715 InitField(x, y, FALSE);
6717 if (GFX_CRUMBLED(Feld[x][y]))
6718 DrawLevelFieldCrumbledSandNeighbours(x, y);
6720 if (ELEM_IS_PLAYER(move_leave_element))
6721 RelocatePlayer(x, y, move_leave_element);
6724 /* do this after checking for left-behind element */
6725 ResetGfxAnimation(x, y); /* reset animation values for old field */
6727 if (!CAN_MOVE(element) ||
6728 (CAN_FALL(element) && direction == MV_DOWN &&
6729 (element == EL_SPRING ||
6730 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6731 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6732 GfxDir[x][y] = MovDir[newx][newy] = 0;
6734 DrawLevelField(x, y);
6735 DrawLevelField(newx, newy);
6737 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6739 /* prevent pushed element from moving on in pushed direction */
6740 if (pushed_by_player && CAN_MOVE(element) &&
6741 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6742 !(element_info[element].move_pattern & direction))
6743 TurnRound(newx, newy);
6745 /* prevent elements on conveyor belt from moving on in last direction */
6746 if (pushed_by_conveyor && CAN_FALL(element) &&
6747 direction & MV_HORIZONTAL)
6748 MovDir[newx][newy] = 0;
6750 if (!pushed_by_player)
6752 int nextx = newx + dx, nexty = newy + dy;
6753 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6755 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6757 if (CAN_FALL(element) && direction == MV_DOWN)
6758 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6760 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6761 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6764 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6766 TestIfBadThingTouchesPlayer(newx, newy);
6767 TestIfBadThingTouchesFriend(newx, newy);
6769 if (!IS_CUSTOM_ELEMENT(element))
6770 TestIfBadThingTouchesOtherBadThing(newx, newy);
6772 else if (element == EL_PENGUIN)
6773 TestIfFriendTouchesBadThing(newx, newy);
6775 /* give the player one last chance (one more frame) to move away */
6776 if (CAN_FALL(element) && direction == MV_DOWN &&
6777 (last_line || (!IS_FREE(x, newy + 1) &&
6778 (!IS_PLAYER(x, newy + 1) ||
6779 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6782 if (pushed_by_player && !game.use_change_when_pushing_bug)
6784 int push_side = MV_DIR_OPPOSITE(direction);
6785 struct PlayerInfo *player = PLAYERINFO(x, y);
6787 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6788 player->index_bit, push_side);
6789 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6790 player->index_bit, push_side);
6793 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6794 MovDelay[newx][newy] = 1;
6796 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6798 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6801 if (ChangePage[newx][newy] != -1) /* delayed change */
6803 int page = ChangePage[newx][newy];
6804 struct ElementChangeInfo *change = &ei->change_page[page];
6806 ChangePage[newx][newy] = -1;
6808 if (change->can_change)
6810 if (ChangeElement(newx, newy, element, page))
6812 if (change->post_change_function)
6813 change->post_change_function(newx, newy);
6817 if (change->has_action)
6818 ExecuteCustomElementAction(newx, newy, element, page);
6822 TestIfElementHitsCustomElement(newx, newy, direction);
6823 TestIfPlayerTouchesCustomElement(newx, newy);
6824 TestIfElementTouchesCustomElement(newx, newy);
6827 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6828 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6829 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6830 MV_DIR_OPPOSITE(direction));
6834 int AmoebeNachbarNr(int ax, int ay)
6837 int element = Feld[ax][ay];
6839 static int xy[4][2] =
6847 for (i = 0; i < NUM_DIRECTIONS; i++)
6849 int x = ax + xy[i][0];
6850 int y = ay + xy[i][1];
6852 if (!IN_LEV_FIELD(x, y))
6855 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6856 group_nr = AmoebaNr[x][y];
6862 void AmoebenVereinigen(int ax, int ay)
6864 int i, x, y, xx, yy;
6865 int new_group_nr = AmoebaNr[ax][ay];
6866 static int xy[4][2] =
6874 if (new_group_nr == 0)
6877 for (i = 0; i < NUM_DIRECTIONS; i++)
6882 if (!IN_LEV_FIELD(x, y))
6885 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6886 Feld[x][y] == EL_BD_AMOEBA ||
6887 Feld[x][y] == EL_AMOEBA_DEAD) &&
6888 AmoebaNr[x][y] != new_group_nr)
6890 int old_group_nr = AmoebaNr[x][y];
6892 if (old_group_nr == 0)
6895 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6896 AmoebaCnt[old_group_nr] = 0;
6897 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6898 AmoebaCnt2[old_group_nr] = 0;
6901 SCAN_PLAYFIELD(xx, yy)
6903 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6906 if (AmoebaNr[xx][yy] == old_group_nr)
6907 AmoebaNr[xx][yy] = new_group_nr;
6913 void AmoebeUmwandeln(int ax, int ay)
6917 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6919 int group_nr = AmoebaNr[ax][ay];
6924 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6925 printf("AmoebeUmwandeln(): This should never happen!\n");
6931 SCAN_PLAYFIELD(x, y)
6933 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6936 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6939 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6943 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6944 SND_AMOEBA_TURNING_TO_GEM :
6945 SND_AMOEBA_TURNING_TO_ROCK));
6950 static int xy[4][2] =
6958 for (i = 0; i < NUM_DIRECTIONS; i++)
6963 if (!IN_LEV_FIELD(x, y))
6966 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6968 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6969 SND_AMOEBA_TURNING_TO_GEM :
6970 SND_AMOEBA_TURNING_TO_ROCK));
6977 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6980 int group_nr = AmoebaNr[ax][ay];
6981 boolean done = FALSE;
6986 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6987 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6993 SCAN_PLAYFIELD(x, y)
6995 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6998 if (AmoebaNr[x][y] == group_nr &&
6999 (Feld[x][y] == EL_AMOEBA_DEAD ||
7000 Feld[x][y] == EL_BD_AMOEBA ||
7001 Feld[x][y] == EL_AMOEBA_GROWING))
7004 Feld[x][y] = new_element;
7005 InitField(x, y, FALSE);
7006 DrawLevelField(x, y);
7012 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7013 SND_BD_AMOEBA_TURNING_TO_ROCK :
7014 SND_BD_AMOEBA_TURNING_TO_GEM));
7017 void AmoebeWaechst(int x, int y)
7019 static unsigned long sound_delay = 0;
7020 static unsigned long sound_delay_value = 0;
7022 if (!MovDelay[x][y]) /* start new growing cycle */
7026 if (DelayReached(&sound_delay, sound_delay_value))
7028 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7029 sound_delay_value = 30;
7033 if (MovDelay[x][y]) /* wait some time before growing bigger */
7036 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7038 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7039 6 - MovDelay[x][y]);
7041 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7044 if (!MovDelay[x][y])
7046 Feld[x][y] = Store[x][y];
7048 DrawLevelField(x, y);
7053 void AmoebaDisappearing(int x, int y)
7055 static unsigned long sound_delay = 0;
7056 static unsigned long sound_delay_value = 0;
7058 if (!MovDelay[x][y]) /* start new shrinking cycle */
7062 if (DelayReached(&sound_delay, sound_delay_value))
7063 sound_delay_value = 30;
7066 if (MovDelay[x][y]) /* wait some time before shrinking */
7069 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7071 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7072 6 - MovDelay[x][y]);
7074 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7077 if (!MovDelay[x][y])
7079 Feld[x][y] = EL_EMPTY;
7080 DrawLevelField(x, y);
7082 /* don't let mole enter this field in this cycle;
7083 (give priority to objects falling to this field from above) */
7089 void AmoebeAbleger(int ax, int ay)
7092 int element = Feld[ax][ay];
7093 int graphic = el2img(element);
7094 int newax = ax, neway = ay;
7095 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7096 static int xy[4][2] =
7104 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7106 Feld[ax][ay] = EL_AMOEBA_DEAD;
7107 DrawLevelField(ax, ay);
7111 if (IS_ANIMATED(graphic))
7112 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7114 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7115 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7117 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7120 if (MovDelay[ax][ay])
7124 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7127 int x = ax + xy[start][0];
7128 int y = ay + xy[start][1];
7130 if (!IN_LEV_FIELD(x, y))
7133 if (IS_FREE(x, y) ||
7134 CAN_GROW_INTO(Feld[x][y]) ||
7135 Feld[x][y] == EL_QUICKSAND_EMPTY)
7141 if (newax == ax && neway == ay)
7144 else /* normal or "filled" (BD style) amoeba */
7147 boolean waiting_for_player = FALSE;
7149 for (i = 0; i < NUM_DIRECTIONS; i++)
7151 int j = (start + i) % 4;
7152 int x = ax + xy[j][0];
7153 int y = ay + xy[j][1];
7155 if (!IN_LEV_FIELD(x, y))
7158 if (IS_FREE(x, y) ||
7159 CAN_GROW_INTO(Feld[x][y]) ||
7160 Feld[x][y] == EL_QUICKSAND_EMPTY)
7166 else if (IS_PLAYER(x, y))
7167 waiting_for_player = TRUE;
7170 if (newax == ax && neway == ay) /* amoeba cannot grow */
7172 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7174 Feld[ax][ay] = EL_AMOEBA_DEAD;
7175 DrawLevelField(ax, ay);
7176 AmoebaCnt[AmoebaNr[ax][ay]]--;
7178 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7180 if (element == EL_AMOEBA_FULL)
7181 AmoebeUmwandeln(ax, ay);
7182 else if (element == EL_BD_AMOEBA)
7183 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7188 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7190 /* amoeba gets larger by growing in some direction */
7192 int new_group_nr = AmoebaNr[ax][ay];
7195 if (new_group_nr == 0)
7197 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7198 printf("AmoebeAbleger(): This should never happen!\n");
7203 AmoebaNr[newax][neway] = new_group_nr;
7204 AmoebaCnt[new_group_nr]++;
7205 AmoebaCnt2[new_group_nr]++;
7207 /* if amoeba touches other amoeba(s) after growing, unify them */
7208 AmoebenVereinigen(newax, neway);
7210 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7212 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7218 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7219 (neway == lev_fieldy - 1 && newax != ax))
7221 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7222 Store[newax][neway] = element;
7224 else if (neway == ay || element == EL_EMC_DRIPPER)
7226 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7228 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7232 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7233 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7234 Store[ax][ay] = EL_AMOEBA_DROP;
7235 ContinueMoving(ax, ay);
7239 DrawLevelField(newax, neway);
7242 void Life(int ax, int ay)
7246 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7249 int element = Feld[ax][ay];
7250 int graphic = el2img(element);
7251 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7253 boolean changed = FALSE;
7255 if (IS_ANIMATED(graphic))
7256 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7261 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7262 MovDelay[ax][ay] = life_time;
7264 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7267 if (MovDelay[ax][ay])
7271 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7273 int xx = ax+x1, yy = ay+y1;
7276 if (!IN_LEV_FIELD(xx, yy))
7279 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7281 int x = xx+x2, y = yy+y2;
7283 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7286 if (((Feld[x][y] == element ||
7287 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7289 (IS_FREE(x, y) && Stop[x][y]))
7293 if (xx == ax && yy == ay) /* field in the middle */
7295 if (nachbarn < life_parameter[0] ||
7296 nachbarn > life_parameter[1])
7298 Feld[xx][yy] = EL_EMPTY;
7300 DrawLevelField(xx, yy);
7301 Stop[xx][yy] = TRUE;
7305 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7306 { /* free border field */
7307 if (nachbarn >= life_parameter[2] &&
7308 nachbarn <= life_parameter[3])
7310 Feld[xx][yy] = element;
7311 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7313 DrawLevelField(xx, yy);
7314 Stop[xx][yy] = TRUE;
7321 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7322 SND_GAME_OF_LIFE_GROWING);
7325 static void InitRobotWheel(int x, int y)
7327 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7330 static void RunRobotWheel(int x, int y)
7332 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7335 static void StopRobotWheel(int x, int y)
7337 if (ZX == x && ZY == y)
7341 static void InitTimegateWheel(int x, int y)
7343 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7346 static void RunTimegateWheel(int x, int y)
7348 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7351 static void InitMagicBallDelay(int x, int y)
7354 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7356 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7360 static void ActivateMagicBall(int bx, int by)
7364 if (level.ball_random)
7366 int pos_border = RND(8); /* select one of the eight border elements */
7367 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7368 int xx = pos_content % 3;
7369 int yy = pos_content / 3;
7374 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7375 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7379 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7381 int xx = x - bx + 1;
7382 int yy = y - by + 1;
7384 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7385 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7389 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7392 static void InitDiagonalMovingElement(int x, int y)
7395 MovDelay[x][y] = level.android_move_time;
7399 void CheckExit(int x, int y)
7401 if (local_player->gems_still_needed > 0 ||
7402 local_player->sokobanfields_still_needed > 0 ||
7403 local_player->lights_still_needed > 0)
7405 int element = Feld[x][y];
7406 int graphic = el2img(element);
7408 if (IS_ANIMATED(graphic))
7409 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7414 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7417 Feld[x][y] = EL_EXIT_OPENING;
7419 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7422 void CheckExitSP(int x, int y)
7424 if (local_player->gems_still_needed > 0)
7426 int element = Feld[x][y];
7427 int graphic = el2img(element);
7429 if (IS_ANIMATED(graphic))
7430 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7435 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7438 Feld[x][y] = EL_SP_EXIT_OPENING;
7440 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7443 static void CloseAllOpenTimegates()
7448 SCAN_PLAYFIELD(x, y)
7450 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7453 int element = Feld[x][y];
7455 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7457 Feld[x][y] = EL_TIMEGATE_CLOSING;
7459 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7464 void EdelsteinFunkeln(int x, int y)
7466 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7469 if (Feld[x][y] == EL_BD_DIAMOND)
7472 if (MovDelay[x][y] == 0) /* next animation frame */
7473 MovDelay[x][y] = 11 * !SimpleRND(500);
7475 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7479 if (setup.direct_draw && MovDelay[x][y])
7480 SetDrawtoField(DRAW_BUFFERED);
7482 DrawLevelElementAnimation(x, y, Feld[x][y]);
7484 if (MovDelay[x][y] != 0)
7486 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7487 10 - MovDelay[x][y]);
7489 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7491 if (setup.direct_draw)
7495 dest_x = FX + SCREENX(x) * TILEX;
7496 dest_y = FY + SCREENY(y) * TILEY;
7498 BlitBitmap(drawto_field, window,
7499 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7500 SetDrawtoField(DRAW_DIRECT);
7506 void MauerWaechst(int x, int y)
7510 if (!MovDelay[x][y]) /* next animation frame */
7511 MovDelay[x][y] = 3 * delay;
7513 if (MovDelay[x][y]) /* wait some time before next frame */
7517 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7519 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7520 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7522 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7525 if (!MovDelay[x][y])
7527 if (MovDir[x][y] == MV_LEFT)
7529 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7530 DrawLevelField(x - 1, y);
7532 else if (MovDir[x][y] == MV_RIGHT)
7534 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7535 DrawLevelField(x + 1, y);
7537 else if (MovDir[x][y] == MV_UP)
7539 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7540 DrawLevelField(x, y - 1);
7544 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7545 DrawLevelField(x, y + 1);
7548 Feld[x][y] = Store[x][y];
7550 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7551 DrawLevelField(x, y);
7556 void MauerAbleger(int ax, int ay)
7558 int element = Feld[ax][ay];
7559 int graphic = el2img(element);
7560 boolean oben_frei = FALSE, unten_frei = FALSE;
7561 boolean links_frei = FALSE, rechts_frei = FALSE;
7562 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7563 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7564 boolean new_wall = FALSE;
7566 if (IS_ANIMATED(graphic))
7567 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7569 if (!MovDelay[ax][ay]) /* start building new wall */
7570 MovDelay[ax][ay] = 6;
7572 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7575 if (MovDelay[ax][ay])
7579 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7581 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7583 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7585 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7588 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7589 element == EL_EXPANDABLE_WALL_ANY)
7593 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7594 Store[ax][ay-1] = element;
7595 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7596 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7597 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7598 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7603 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7604 Store[ax][ay+1] = element;
7605 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7606 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7607 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7608 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7613 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7614 element == EL_EXPANDABLE_WALL_ANY ||
7615 element == EL_EXPANDABLE_WALL)
7619 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7620 Store[ax-1][ay] = element;
7621 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7622 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7623 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7624 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7630 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7631 Store[ax+1][ay] = element;
7632 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7633 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7634 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7635 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7640 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7641 DrawLevelField(ax, ay);
7643 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7645 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7646 unten_massiv = TRUE;
7647 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7648 links_massiv = TRUE;
7649 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7650 rechts_massiv = TRUE;
7652 if (((oben_massiv && unten_massiv) ||
7653 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7654 element == EL_EXPANDABLE_WALL) &&
7655 ((links_massiv && rechts_massiv) ||
7656 element == EL_EXPANDABLE_WALL_VERTICAL))
7657 Feld[ax][ay] = EL_WALL;
7660 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7663 void CheckForDragon(int x, int y)
7666 boolean dragon_found = FALSE;
7667 static int xy[4][2] =
7675 for (i = 0; i < NUM_DIRECTIONS; i++)
7677 for (j = 0; j < 4; j++)
7679 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7681 if (IN_LEV_FIELD(xx, yy) &&
7682 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7684 if (Feld[xx][yy] == EL_DRAGON)
7685 dragon_found = TRUE;
7694 for (i = 0; i < NUM_DIRECTIONS; i++)
7696 for (j = 0; j < 3; j++)
7698 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7700 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7702 Feld[xx][yy] = EL_EMPTY;
7703 DrawLevelField(xx, yy);
7712 static void InitBuggyBase(int x, int y)
7714 int element = Feld[x][y];
7715 int activating_delay = FRAMES_PER_SECOND / 4;
7718 (element == EL_SP_BUGGY_BASE ?
7719 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7720 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7722 element == EL_SP_BUGGY_BASE_ACTIVE ?
7723 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7726 static void WarnBuggyBase(int x, int y)
7729 static int xy[4][2] =
7737 for (i = 0; i < NUM_DIRECTIONS; i++)
7739 int xx = x + xy[i][0];
7740 int yy = y + xy[i][1];
7742 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7744 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7751 static void InitTrap(int x, int y)
7753 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7756 static void ActivateTrap(int x, int y)
7758 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7761 static void ChangeActiveTrap(int x, int y)
7763 int graphic = IMG_TRAP_ACTIVE;
7765 /* if new animation frame was drawn, correct crumbled sand border */
7766 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7767 DrawLevelFieldCrumbledSand(x, y);
7770 static int getSpecialActionElement(int element, int number, int base_element)
7772 return (element != EL_EMPTY ? element :
7773 number != -1 ? base_element + number - 1 :
7777 static int getModifiedActionNumber(int value_old, int operator, int operand,
7778 int value_min, int value_max)
7780 int value_new = (operator == CA_MODE_SET ? operand :
7781 operator == CA_MODE_ADD ? value_old + operand :
7782 operator == CA_MODE_SUBTRACT ? value_old - operand :
7783 operator == CA_MODE_MULTIPLY ? value_old * operand :
7784 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7785 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7788 return (value_new < value_min ? value_min :
7789 value_new > value_max ? value_max :
7793 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7795 struct ElementInfo *ei = &element_info[element];
7796 struct ElementChangeInfo *change = &ei->change_page[page];
7797 int action_type = change->action_type;
7798 int action_mode = change->action_mode;
7799 int action_arg = change->action_arg;
7802 if (!change->has_action)
7805 /* ---------- determine action paramater values -------------------------- */
7807 int level_time_value =
7808 (level.time > 0 ? TimeLeft :
7811 int action_arg_element =
7812 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7813 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7814 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7817 int action_arg_direction =
7818 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7819 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7820 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7821 change->actual_trigger_side :
7822 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7823 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7826 int action_arg_number_min =
7827 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7830 int action_arg_number_max =
7831 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7832 action_type == CA_SET_LEVEL_GEMS ? 999 :
7833 action_type == CA_SET_LEVEL_TIME ? 9999 :
7834 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7835 action_type == CA_SET_CE_SCORE ? 9999 :
7836 action_type == CA_SET_CE_VALUE ? 9999 :
7839 int action_arg_number_reset =
7840 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7841 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7842 action_type == CA_SET_LEVEL_TIME ? level.time :
7843 action_type == CA_SET_LEVEL_SCORE ? 0 :
7844 action_type == CA_SET_CE_SCORE ? 0 :
7846 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7848 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7852 int action_arg_number =
7853 (action_arg <= CA_ARG_MAX ? action_arg :
7854 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7855 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7856 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7857 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7858 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7859 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7860 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7861 #if USE_NEW_CUSTOM_VALUE
7862 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7864 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7866 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7867 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7868 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7869 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7870 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7871 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7872 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7873 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7876 int action_arg_number_old =
7877 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7878 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7879 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7880 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7881 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7884 int action_arg_number_new =
7885 getModifiedActionNumber(action_arg_number_old,
7886 action_mode, action_arg_number,
7887 action_arg_number_min, action_arg_number_max);
7889 int trigger_player_bits =
7890 (change->actual_trigger_player >= EL_PLAYER_1 &&
7891 change->actual_trigger_player <= EL_PLAYER_4 ?
7892 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7895 int action_arg_player_bits =
7896 (action_arg >= CA_ARG_PLAYER_1 &&
7897 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7898 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7901 /* ---------- execute action -------------------------------------------- */
7910 /* ---------- level actions ------------------------------------------- */
7912 case CA_RESTART_LEVEL:
7914 game.restart_level = TRUE;
7919 case CA_SHOW_ENVELOPE:
7921 int element = getSpecialActionElement(action_arg_element,
7922 action_arg_number, EL_ENVELOPE_1);
7924 if (IS_ENVELOPE(element))
7925 local_player->show_envelope = element;
7930 case CA_SET_LEVEL_TIME:
7932 if (level.time > 0) /* only modify limited time value */
7934 TimeLeft = action_arg_number_new;
7936 DrawGameValue_Time(TimeLeft);
7938 if (!TimeLeft && setup.time_limit)
7939 for (i = 0; i < MAX_PLAYERS; i++)
7940 KillPlayer(&stored_player[i]);
7946 case CA_SET_LEVEL_SCORE:
7948 local_player->score = action_arg_number_new;
7950 DrawGameValue_Score(local_player->score);
7955 case CA_SET_LEVEL_GEMS:
7957 local_player->gems_still_needed = action_arg_number_new;
7959 DrawGameValue_Emeralds(local_player->gems_still_needed);
7964 case CA_SET_LEVEL_GRAVITY:
7966 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7967 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7968 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7973 case CA_SET_LEVEL_WIND:
7975 game.wind_direction = action_arg_direction;
7980 /* ---------- player actions ------------------------------------------ */
7982 case CA_MOVE_PLAYER:
7984 /* automatically move to the next field in specified direction */
7985 for (i = 0; i < MAX_PLAYERS; i++)
7986 if (trigger_player_bits & (1 << i))
7987 stored_player[i].programmed_action = action_arg_direction;
7992 case CA_EXIT_PLAYER:
7994 for (i = 0; i < MAX_PLAYERS; i++)
7995 if (action_arg_player_bits & (1 << i))
7996 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8001 case CA_KILL_PLAYER:
8003 for (i = 0; i < MAX_PLAYERS; i++)
8004 if (action_arg_player_bits & (1 << i))
8005 KillPlayer(&stored_player[i]);
8010 case CA_SET_PLAYER_KEYS:
8012 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8013 int element = getSpecialActionElement(action_arg_element,
8014 action_arg_number, EL_KEY_1);
8016 if (IS_KEY(element))
8018 for (i = 0; i < MAX_PLAYERS; i++)
8020 if (trigger_player_bits & (1 << i))
8022 stored_player[i].key[KEY_NR(element)] = key_state;
8024 DrawGameValue_Keys(stored_player[i].key);
8026 redraw_mask |= REDRAW_DOOR_1;
8034 case CA_SET_PLAYER_SPEED:
8036 for (i = 0; i < MAX_PLAYERS; i++)
8038 if (trigger_player_bits & (1 << i))
8040 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8042 if (action_arg == CA_ARG_SPEED_FASTER &&
8043 stored_player[i].cannot_move)
8045 action_arg_number = STEPSIZE_VERY_SLOW;
8047 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8048 action_arg == CA_ARG_SPEED_FASTER)
8050 action_arg_number = 2;
8051 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8056 getModifiedActionNumber(move_stepsize,
8059 action_arg_number_min,
8060 action_arg_number_max);
8063 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8065 /* make sure that value is power of 2 */
8066 move_stepsize = (1 << log_2(move_stepsize));
8068 /* do no immediately change -- the player might just be moving */
8069 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8071 stored_player[i].cannot_move =
8072 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8080 case CA_SET_PLAYER_SHIELD:
8082 for (i = 0; i < MAX_PLAYERS; i++)
8084 if (trigger_player_bits & (1 << i))
8086 if (action_arg == CA_ARG_SHIELD_OFF)
8088 stored_player[i].shield_normal_time_left = 0;
8089 stored_player[i].shield_deadly_time_left = 0;
8091 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8093 stored_player[i].shield_normal_time_left = 999999;
8095 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8097 stored_player[i].shield_normal_time_left = 999999;
8098 stored_player[i].shield_deadly_time_left = 999999;
8106 case CA_SET_PLAYER_ARTWORK:
8108 for (i = 0; i < MAX_PLAYERS; i++)
8110 if (trigger_player_bits & (1 << i))
8112 int artwork_element = action_arg_element;
8114 if (action_arg == CA_ARG_ELEMENT_RESET)
8116 (level.use_artwork_element[i] ? level.artwork_element[i] :
8117 stored_player[i].element_nr);
8119 stored_player[i].artwork_element = artwork_element;
8121 SetPlayerWaiting(&stored_player[i], FALSE);
8123 /* set number of special actions for bored and sleeping animation */
8124 stored_player[i].num_special_action_bored =
8125 get_num_special_action(artwork_element,
8126 ACTION_BORING_1, ACTION_BORING_LAST);
8127 stored_player[i].num_special_action_sleeping =
8128 get_num_special_action(artwork_element,
8129 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8136 /* ---------- CE actions ---------------------------------------------- */
8138 case CA_SET_CE_SCORE:
8140 ei->collect_score = action_arg_number_new;
8145 case CA_SET_CE_VALUE:
8147 #if USE_NEW_CUSTOM_VALUE
8148 int last_custom_value = CustomValue[x][y];
8150 CustomValue[x][y] = action_arg_number_new;
8153 printf("::: Count == %d\n", CustomValue[x][y]);
8156 if (CustomValue[x][y] == 0 && last_custom_value > 0)
8159 printf("::: CE_VALUE_GETS_ZERO\n");
8162 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8163 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8166 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8174 /* ---------- engine actions ------------------------------------------ */
8176 case CA_SET_ENGINE_SCAN_MODE:
8178 InitPlayfieldScanMode(action_arg);
8188 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8190 int previous_move_direction = MovDir[x][y];
8191 #if USE_NEW_CUSTOM_VALUE
8192 int last_ce_value = CustomValue[x][y];
8194 boolean add_player = (ELEM_IS_PLAYER(element) &&
8195 IS_WALKABLE(Feld[x][y]));
8197 /* check if element under player changes from accessible to unaccessible
8198 (needed for special case of dropping element which then changes) */
8199 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8200 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
8209 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8210 RemoveMovingField(x, y);
8214 Feld[x][y] = element;
8216 ResetGfxAnimation(x, y);
8217 ResetRandomAnimationValue(x, y);
8219 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8220 MovDir[x][y] = previous_move_direction;
8222 #if USE_NEW_CUSTOM_VALUE
8223 if (element_info[Feld[x][y]].use_last_ce_value)
8224 CustomValue[x][y] = last_ce_value;
8227 InitField_WithBug1(x, y, FALSE);
8229 DrawLevelField(x, y);
8231 if (GFX_CRUMBLED(Feld[x][y]))
8232 DrawLevelFieldCrumbledSandNeighbours(x, y);
8235 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8236 if (ELEM_IS_PLAYER(element))
8237 RelocatePlayer(x, y, element);
8240 ChangeCount[x][y]++; /* count number of changes in the same frame */
8242 TestIfBadThingTouchesPlayer(x, y);
8243 TestIfPlayerTouchesCustomElement(x, y);
8244 TestIfElementTouchesCustomElement(x, y);
8247 static void CreateField(int x, int y, int element)
8249 CreateFieldExt(x, y, element, FALSE);
8252 static void CreateElementFromChange(int x, int y, int element)
8254 element = GET_VALID_RUNTIME_ELEMENT(element);
8256 #if USE_STOP_CHANGED_ELEMENTS
8257 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8259 int old_element = Feld[x][y];
8261 /* prevent changed element from moving in same engine frame
8262 unless both old and new element can either fall or move */
8263 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8264 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8269 CreateFieldExt(x, y, element, TRUE);
8272 static boolean ChangeElement(int x, int y, int element, int page)
8274 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8276 int old_element = Feld[x][y];
8278 /* always use default change event to prevent running into a loop */
8279 if (ChangeEvent[x][y] == -1)
8280 ChangeEvent[x][y] = CE_DELAY;
8282 if (ChangeEvent[x][y] == CE_DELAY)
8284 /* reset actual trigger element, trigger player and action element */
8285 change->actual_trigger_element = EL_EMPTY;
8286 change->actual_trigger_player = EL_PLAYER_1;
8287 change->actual_trigger_side = CH_SIDE_NONE;
8288 change->actual_trigger_ce_value = 0;
8291 /* do not change elements more than a specified maximum number of changes */
8292 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8295 ChangeCount[x][y]++; /* count number of changes in the same frame */
8297 if (change->explode)
8304 if (change->use_target_content)
8306 boolean complete_replace = TRUE;
8307 boolean can_replace[3][3];
8310 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8313 boolean is_walkable;
8314 boolean is_diggable;
8315 boolean is_collectible;
8316 boolean is_removable;
8317 boolean is_destructible;
8318 int ex = x + xx - 1;
8319 int ey = y + yy - 1;
8320 int content_element = change->target_content.e[xx][yy];
8323 can_replace[xx][yy] = TRUE;
8325 if (ex == x && ey == y) /* do not check changing element itself */
8328 if (content_element == EL_EMPTY_SPACE)
8330 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8335 if (!IN_LEV_FIELD(ex, ey))
8337 can_replace[xx][yy] = FALSE;
8338 complete_replace = FALSE;
8345 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8346 e = MovingOrBlocked2Element(ex, ey);
8348 is_empty = (IS_FREE(ex, ey) ||
8349 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8351 is_walkable = (is_empty || IS_WALKABLE(e));
8352 is_diggable = (is_empty || IS_DIGGABLE(e));
8353 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8354 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8355 is_removable = (is_diggable || is_collectible);
8357 can_replace[xx][yy] =
8358 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8359 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8360 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8361 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8362 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8363 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8364 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8366 if (!can_replace[xx][yy])
8367 complete_replace = FALSE;
8370 if (!change->only_if_complete || complete_replace)
8372 boolean something_has_changed = FALSE;
8374 if (change->only_if_complete && change->use_random_replace &&
8375 RND(100) < change->random_percentage)
8378 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8380 int ex = x + xx - 1;
8381 int ey = y + yy - 1;
8382 int content_element;
8384 if (can_replace[xx][yy] && (!change->use_random_replace ||
8385 RND(100) < change->random_percentage))
8387 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8388 RemoveMovingField(ex, ey);
8390 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8392 content_element = change->target_content.e[xx][yy];
8393 target_element = GET_TARGET_ELEMENT(content_element, change);
8395 CreateElementFromChange(ex, ey, target_element);
8397 something_has_changed = TRUE;
8399 /* for symmetry reasons, freeze newly created border elements */
8400 if (ex != x || ey != y)
8401 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8405 if (something_has_changed)
8407 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8408 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8414 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8416 if (element == EL_DIAGONAL_GROWING ||
8417 element == EL_DIAGONAL_SHRINKING)
8419 target_element = Store[x][y];
8421 Store[x][y] = EL_EMPTY;
8424 CreateElementFromChange(x, y, target_element);
8426 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8427 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8430 /* this uses direct change before indirect change */
8431 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8436 #if USE_NEW_DELAYED_ACTION
8438 static void HandleElementChange(int x, int y, int page)
8440 int element = MovingOrBlocked2Element(x, y);
8441 struct ElementInfo *ei = &element_info[element];
8442 struct ElementChangeInfo *change = &ei->change_page[page];
8445 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8446 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8449 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8450 x, y, element, element_info[element].token_name);
8451 printf("HandleElementChange(): This should never happen!\n");
8456 /* this can happen with classic bombs on walkable, changing elements */
8457 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8460 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8461 ChangeDelay[x][y] = 0;
8467 if (ChangeDelay[x][y] == 0) /* initialize element change */
8469 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8471 if (change->can_change)
8473 ResetGfxAnimation(x, y);
8474 ResetRandomAnimationValue(x, y);
8476 if (change->pre_change_function)
8477 change->pre_change_function(x, y);
8481 ChangeDelay[x][y]--;
8483 if (ChangeDelay[x][y] != 0) /* continue element change */
8485 if (change->can_change)
8487 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8489 if (IS_ANIMATED(graphic))
8490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8492 if (change->change_function)
8493 change->change_function(x, y);
8496 else /* finish element change */
8498 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8500 page = ChangePage[x][y];
8501 ChangePage[x][y] = -1;
8503 change = &ei->change_page[page];
8506 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8508 ChangeDelay[x][y] = 1; /* try change after next move step */
8509 ChangePage[x][y] = page; /* remember page to use for change */
8514 if (change->can_change)
8516 if (ChangeElement(x, y, element, page))
8518 if (change->post_change_function)
8519 change->post_change_function(x, y);
8523 if (change->has_action)
8524 ExecuteCustomElementAction(x, y, element, page);
8530 static void HandleElementChange(int x, int y, int page)
8532 int element = MovingOrBlocked2Element(x, y);
8533 struct ElementInfo *ei = &element_info[element];
8534 struct ElementChangeInfo *change = &ei->change_page[page];
8537 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8540 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8541 x, y, element, element_info[element].token_name);
8542 printf("HandleElementChange(): This should never happen!\n");
8547 /* this can happen with classic bombs on walkable, changing elements */
8548 if (!CAN_CHANGE(element))
8551 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8552 ChangeDelay[x][y] = 0;
8558 if (ChangeDelay[x][y] == 0) /* initialize element change */
8560 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8562 ResetGfxAnimation(x, y);
8563 ResetRandomAnimationValue(x, y);
8565 if (change->pre_change_function)
8566 change->pre_change_function(x, y);
8569 ChangeDelay[x][y]--;
8571 if (ChangeDelay[x][y] != 0) /* continue element change */
8573 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8575 if (IS_ANIMATED(graphic))
8576 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8578 if (change->change_function)
8579 change->change_function(x, y);
8581 else /* finish element change */
8583 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8585 page = ChangePage[x][y];
8586 ChangePage[x][y] = -1;
8588 change = &ei->change_page[page];
8591 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8593 ChangeDelay[x][y] = 1; /* try change after next move step */
8594 ChangePage[x][y] = page; /* remember page to use for change */
8599 if (ChangeElement(x, y, element, page))
8601 if (change->post_change_function)
8602 change->post_change_function(x, y);
8609 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8610 int trigger_element,
8616 boolean change_done_any = FALSE;
8617 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8620 if (!(trigger_events[trigger_element][trigger_event]))
8623 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8625 int element = EL_CUSTOM_START + i;
8626 boolean change_done = FALSE;
8629 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8630 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8633 for (p = 0; p < element_info[element].num_change_pages; p++)
8635 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8637 if (change->can_change_or_has_action &&
8638 change->has_event[trigger_event] &&
8639 change->trigger_side & trigger_side &&
8640 change->trigger_player & trigger_player &&
8641 change->trigger_page & trigger_page_bits &&
8642 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8644 change->actual_trigger_element = trigger_element;
8645 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8646 change->actual_trigger_side = trigger_side;
8647 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8649 if ((change->can_change && !change_done) || change->has_action)
8654 SCAN_PLAYFIELD(x, y)
8656 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8659 if (Feld[x][y] == element)
8661 if (change->can_change && !change_done)
8663 ChangeDelay[x][y] = 1;
8664 ChangeEvent[x][y] = trigger_event;
8666 HandleElementChange(x, y, p);
8668 #if USE_NEW_DELAYED_ACTION
8669 else if (change->has_action)
8671 ExecuteCustomElementAction(x, y, element, p);
8672 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8675 if (change->has_action)
8677 ExecuteCustomElementAction(x, y, element, p);
8678 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8684 if (change->can_change)
8687 change_done_any = TRUE;
8694 return change_done_any;
8697 static boolean CheckElementChangeExt(int x, int y,
8699 int trigger_element,
8704 boolean change_done = FALSE;
8707 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8708 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8711 if (Feld[x][y] == EL_BLOCKED)
8713 Blocked2Moving(x, y, &x, &y);
8714 element = Feld[x][y];
8718 /* check if element has already changed */
8719 if (Feld[x][y] != element)
8722 /* check if element has already changed or is about to change after moving */
8723 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8724 Feld[x][y] != element) ||
8726 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8727 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8728 ChangePage[x][y] != -1)))
8732 for (p = 0; p < element_info[element].num_change_pages; p++)
8734 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8736 boolean check_trigger_element =
8737 (trigger_event == CE_TOUCHING_X ||
8738 trigger_event == CE_HITTING_X ||
8739 trigger_event == CE_HIT_BY_X);
8741 if (change->can_change_or_has_action &&
8742 change->has_event[trigger_event] &&
8743 change->trigger_side & trigger_side &&
8744 change->trigger_player & trigger_player &&
8745 (!check_trigger_element ||
8746 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8748 change->actual_trigger_element = trigger_element;
8749 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8750 change->actual_trigger_side = trigger_side;
8751 change->actual_trigger_ce_value = CustomValue[x][y];
8753 /* special case: trigger element not at (x,y) position for some events */
8754 if (check_trigger_element)
8766 { 0, 0 }, { 0, 0 }, { 0, 0 },
8770 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8771 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8773 change->actual_trigger_ce_value = CustomValue[xx][yy];
8776 if (change->can_change && !change_done)
8778 ChangeDelay[x][y] = 1;
8779 ChangeEvent[x][y] = trigger_event;
8781 HandleElementChange(x, y, p);
8785 #if USE_NEW_DELAYED_ACTION
8786 else if (change->has_action)
8788 ExecuteCustomElementAction(x, y, element, p);
8789 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8792 if (change->has_action)
8794 ExecuteCustomElementAction(x, y, element, p);
8795 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8804 static void PlayPlayerSound(struct PlayerInfo *player)
8806 int jx = player->jx, jy = player->jy;
8807 int sound_element = player->artwork_element;
8808 int last_action = player->last_action_waiting;
8809 int action = player->action_waiting;
8811 if (player->is_waiting)
8813 if (action != last_action)
8814 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8816 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8820 if (action != last_action)
8821 StopSound(element_info[sound_element].sound[last_action]);
8823 if (last_action == ACTION_SLEEPING)
8824 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8828 static void PlayAllPlayersSound()
8832 for (i = 0; i < MAX_PLAYERS; i++)
8833 if (stored_player[i].active)
8834 PlayPlayerSound(&stored_player[i]);
8837 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8839 boolean last_waiting = player->is_waiting;
8840 int move_dir = player->MovDir;
8842 player->dir_waiting = move_dir;
8843 player->last_action_waiting = player->action_waiting;
8847 if (!last_waiting) /* not waiting -> waiting */
8849 player->is_waiting = TRUE;
8851 player->frame_counter_bored =
8853 game.player_boring_delay_fixed +
8854 SimpleRND(game.player_boring_delay_random);
8855 player->frame_counter_sleeping =
8857 game.player_sleeping_delay_fixed +
8858 SimpleRND(game.player_sleeping_delay_random);
8861 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8863 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8867 if (game.player_sleeping_delay_fixed +
8868 game.player_sleeping_delay_random > 0 &&
8869 player->anim_delay_counter == 0 &&
8870 player->post_delay_counter == 0 &&
8871 FrameCounter >= player->frame_counter_sleeping)
8872 player->is_sleeping = TRUE;
8873 else if (game.player_boring_delay_fixed +
8874 game.player_boring_delay_random > 0 &&
8875 FrameCounter >= player->frame_counter_bored)
8876 player->is_bored = TRUE;
8878 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8879 player->is_bored ? ACTION_BORING :
8883 if (player->is_sleeping && player->use_murphy)
8885 /* special case for sleeping Murphy when leaning against non-free tile */
8887 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8888 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8889 !IS_MOVING(player->jx - 1, player->jy)))
8891 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8892 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8893 !IS_MOVING(player->jx + 1, player->jy)))
8894 move_dir = MV_RIGHT;
8896 player->is_sleeping = FALSE;
8898 player->dir_waiting = move_dir;
8902 if (player->is_sleeping)
8904 if (player->num_special_action_sleeping > 0)
8906 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8908 int last_special_action = player->special_action_sleeping;
8909 int num_special_action = player->num_special_action_sleeping;
8910 int special_action =
8911 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8912 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8913 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8914 last_special_action + 1 : ACTION_SLEEPING);
8915 int special_graphic =
8916 el_act_dir2img(player->artwork_element, special_action, move_dir);
8918 player->anim_delay_counter =
8919 graphic_info[special_graphic].anim_delay_fixed +
8920 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8921 player->post_delay_counter =
8922 graphic_info[special_graphic].post_delay_fixed +
8923 SimpleRND(graphic_info[special_graphic].post_delay_random);
8925 player->special_action_sleeping = special_action;
8928 if (player->anim_delay_counter > 0)
8930 player->action_waiting = player->special_action_sleeping;
8931 player->anim_delay_counter--;
8933 else if (player->post_delay_counter > 0)
8935 player->post_delay_counter--;
8939 else if (player->is_bored)
8941 if (player->num_special_action_bored > 0)
8943 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8945 int special_action =
8946 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8947 int special_graphic =
8948 el_act_dir2img(player->artwork_element, special_action, move_dir);
8950 player->anim_delay_counter =
8951 graphic_info[special_graphic].anim_delay_fixed +
8952 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8953 player->post_delay_counter =
8954 graphic_info[special_graphic].post_delay_fixed +
8955 SimpleRND(graphic_info[special_graphic].post_delay_random);
8957 player->special_action_bored = special_action;
8960 if (player->anim_delay_counter > 0)
8962 player->action_waiting = player->special_action_bored;
8963 player->anim_delay_counter--;
8965 else if (player->post_delay_counter > 0)
8967 player->post_delay_counter--;
8972 else if (last_waiting) /* waiting -> not waiting */
8974 player->is_waiting = FALSE;
8975 player->is_bored = FALSE;
8976 player->is_sleeping = FALSE;
8978 player->frame_counter_bored = -1;
8979 player->frame_counter_sleeping = -1;
8981 player->anim_delay_counter = 0;
8982 player->post_delay_counter = 0;
8984 player->dir_waiting = player->MovDir;
8985 player->action_waiting = ACTION_DEFAULT;
8987 player->special_action_bored = ACTION_DEFAULT;
8988 player->special_action_sleeping = ACTION_DEFAULT;
8992 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8994 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8995 int left = player_action & JOY_LEFT;
8996 int right = player_action & JOY_RIGHT;
8997 int up = player_action & JOY_UP;
8998 int down = player_action & JOY_DOWN;
8999 int button1 = player_action & JOY_BUTTON_1;
9000 int button2 = player_action & JOY_BUTTON_2;
9001 int dx = (left ? -1 : right ? 1 : 0);
9002 int dy = (up ? -1 : down ? 1 : 0);
9004 if (!player->active || tape.pausing)
9010 snapped = SnapField(player, dx, dy);
9014 dropped = DropElement(player);
9016 moved = MovePlayer(player, dx, dy);
9019 if (tape.single_step && tape.recording && !tape.pausing)
9021 if (button1 || (dropped && !moved))
9023 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9024 SnapField(player, 0, 0); /* stop snapping */
9028 SetPlayerWaiting(player, FALSE);
9030 return player_action;
9034 /* no actions for this player (no input at player's configured device) */
9036 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9037 SnapField(player, 0, 0);
9038 CheckGravityMovementWhenNotMoving(player);
9040 if (player->MovPos == 0)
9041 SetPlayerWaiting(player, TRUE);
9043 if (player->MovPos == 0) /* needed for tape.playing */
9044 player->is_moving = FALSE;
9046 player->is_dropping = FALSE;
9047 player->is_dropping_pressed = FALSE;
9048 player->drop_pressed_delay = 0;
9054 void AdvanceFrameAndPlayerCounters(int player_nr)
9058 /* advance frame counters (global frame counter and time frame counter) */
9062 /* advance player counters (counters for move delay, move animation etc.) */
9063 for (i = 0; i < MAX_PLAYERS; i++)
9065 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9066 int move_delay_value = stored_player[i].move_delay_value;
9067 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9069 if (!advance_player_counters) /* not all players may be affected */
9072 #if USE_NEW_PLAYER_ANIM
9073 if (move_frames == 0) /* less than one move per game frame */
9075 int stepsize = TILEX / move_delay_value;
9076 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9077 int count = (stored_player[i].is_moving ?
9078 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9080 if (count % delay == 0)
9085 stored_player[i].Frame += move_frames;
9087 if (stored_player[i].MovPos != 0)
9088 stored_player[i].StepFrame += move_frames;
9090 if (stored_player[i].move_delay > 0)
9091 stored_player[i].move_delay--;
9093 /* due to bugs in previous versions, counter must count up, not down */
9094 if (stored_player[i].push_delay != -1)
9095 stored_player[i].push_delay++;
9097 if (stored_player[i].drop_delay > 0)
9098 stored_player[i].drop_delay--;
9100 if (stored_player[i].is_dropping_pressed)
9101 stored_player[i].drop_pressed_delay++;
9107 static unsigned long game_frame_delay = 0;
9108 unsigned long game_frame_delay_value;
9109 int magic_wall_x = 0, magic_wall_y = 0;
9110 int i, x, y, element, graphic;
9111 byte *recorded_player_action;
9112 byte summarized_player_action = 0;
9113 byte tape_action[MAX_PLAYERS];
9115 if (game_status != GAME_MODE_PLAYING)
9118 game_frame_delay_value =
9119 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9121 if (tape.playing && tape.warp_forward && !tape.pausing)
9122 game_frame_delay_value = 0;
9124 /* ---------- main game synchronization point ---------- */
9126 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9128 InitPlayfieldScanModeVars();
9131 if (game.set_centered_player)
9133 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9135 /* switching to "all players" only possible if all players fit to screen */
9136 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9138 game.centered_player_nr_next = game.centered_player_nr;
9139 game.set_centered_player = FALSE;
9142 /* do not switch focus to non-existing (or non-active) player */
9143 if (game.centered_player_nr_next >= 0 &&
9144 !stored_player[game.centered_player_nr_next].active)
9146 game.centered_player_nr_next = game.centered_player_nr;
9147 game.set_centered_player = FALSE;
9151 if (game.set_centered_player &&
9152 ScreenMovPos == 0) /* screen currently aligned at tile position */
9155 struct PlayerInfo *player;
9156 int player_nr = game.centered_player_nr_next;
9160 if (game.centered_player_nr_next == -1)
9162 setScreenCenteredToAllPlayers(&sx, &sy);
9166 sx = stored_player[game.centered_player_nr_next].jx;
9167 sy = stored_player[game.centered_player_nr_next].jy;
9171 player = &stored_player[player_nr];
9173 if (!player->active)
9174 game.centered_player_nr_next = game.centered_player_nr;
9181 if (game.centered_player_nr != game.centered_player_nr_next)
9185 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9187 DrawRelocatePlayer(player, setup.quick_switch);
9190 game.centered_player_nr = game.centered_player_nr_next;
9193 game.set_centered_player = FALSE;
9197 #if USE_ONE_MORE_CHANGE_PER_FRAME
9198 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9200 SCAN_PLAYFIELD(x, y)
9202 ChangeCount[x][y] = 0;
9203 ChangeEvent[x][y] = -1;
9208 if (network_playing && !network_player_action_received)
9210 /* try to get network player actions in time */
9212 #if defined(NETWORK_AVALIABLE)
9213 /* last chance to get network player actions without main loop delay */
9217 /* game was quit by network peer */
9218 if (game_status != GAME_MODE_PLAYING)
9221 if (!network_player_action_received)
9222 return; /* failed to get network player actions in time */
9228 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9231 if (tape.set_centered_player)
9233 game.centered_player_nr_next = tape.centered_player_nr_next;
9234 game.set_centered_player = TRUE;
9237 if (game.set_centered_player)
9239 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9241 /* switching to "all players" only possible if all players fit to screen */
9242 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9244 game.centered_player_nr_next = game.centered_player_nr;
9245 game.set_centered_player = FALSE;
9248 /* do not switch focus to non-existing (or non-active) player */
9249 if (game.centered_player_nr_next >= 0 &&
9250 !stored_player[game.centered_player_nr_next].active)
9252 game.centered_player_nr_next = game.centered_player_nr;
9253 game.set_centered_player = FALSE;
9257 if (game.set_centered_player &&
9258 ScreenMovPos == 0) /* screen currently aligned at tile position */
9261 struct PlayerInfo *player;
9262 int player_nr = game.centered_player_nr_next;
9266 if (game.centered_player_nr_next == -1)
9268 setScreenCenteredToAllPlayers(&sx, &sy);
9272 sx = stored_player[game.centered_player_nr_next].jx;
9273 sy = stored_player[game.centered_player_nr_next].jy;
9277 player = &stored_player[player_nr];
9279 if (!player->active)
9280 game.centered_player_nr_next = game.centered_player_nr;
9287 if (game.centered_player_nr != game.centered_player_nr_next)
9291 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9293 DrawRelocatePlayer(player, setup.quick_switch);
9296 game.centered_player_nr = game.centered_player_nr_next;
9299 game.set_centered_player = FALSE;
9304 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
9305 if (recorded_player_action == NULL && tape.pausing)
9309 for (i = 0; i < MAX_PLAYERS; i++)
9311 summarized_player_action |= stored_player[i].action;
9313 if (!network_playing)
9314 stored_player[i].effective_action = stored_player[i].action;
9317 #if defined(NETWORK_AVALIABLE)
9318 if (network_playing)
9319 SendToServer_MovePlayer(summarized_player_action);
9322 if (!options.network && !setup.team_mode)
9323 local_player->effective_action = summarized_player_action;
9325 if (recorded_player_action != NULL)
9326 for (i = 0; i < MAX_PLAYERS; i++)
9327 stored_player[i].effective_action = recorded_player_action[i];
9329 for (i = 0; i < MAX_PLAYERS; i++)
9331 tape_action[i] = stored_player[i].effective_action;
9333 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9334 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9337 /* only save actions from input devices, but not programmed actions */
9339 TapeRecordAction(tape_action);
9341 for (i = 0; i < MAX_PLAYERS; i++)
9343 int actual_player_action = stored_player[i].effective_action;
9346 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9347 - rnd_equinox_tetrachloride 048
9348 - rnd_equinox_tetrachloride_ii 096
9349 - rnd_emanuel_schmieg 002
9350 - doctor_sloan_ww 001, 020
9352 if (stored_player[i].MovPos == 0)
9353 CheckGravityMovement(&stored_player[i]);
9356 /* overwrite programmed action with tape action */
9357 if (stored_player[i].programmed_action)
9358 actual_player_action = stored_player[i].programmed_action;
9361 PlayerActions(&stored_player[i], actual_player_action);
9363 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9365 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9366 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9369 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9372 network_player_action_received = FALSE;
9374 ScrollScreen(NULL, SCROLL_GO_ON);
9376 /* for backwards compatibility, the following code emulates a fixed bug that
9377 occured when pushing elements (causing elements that just made their last
9378 pushing step to already (if possible) make their first falling step in the
9379 same game frame, which is bad); this code is also needed to use the famous
9380 "spring push bug" which is used in older levels and might be wanted to be
9381 used also in newer levels, but in this case the buggy pushing code is only
9382 affecting the "spring" element and no other elements */
9384 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9386 for (i = 0; i < MAX_PLAYERS; i++)
9388 struct PlayerInfo *player = &stored_player[i];
9392 if (player->active && player->is_pushing && player->is_moving &&
9394 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9395 Feld[x][y] == EL_SPRING))
9397 ContinueMoving(x, y);
9399 /* continue moving after pushing (this is actually a bug) */
9400 if (!IS_MOVING(x, y))
9409 SCAN_PLAYFIELD(x, y)
9411 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9414 ChangeCount[x][y] = 0;
9415 ChangeEvent[x][y] = -1;
9417 /* this must be handled before main playfield loop */
9418 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9421 if (MovDelay[x][y] <= 0)
9425 #if USE_NEW_SNAP_DELAY
9426 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9429 if (MovDelay[x][y] <= 0)
9432 DrawLevelField(x, y);
9434 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9440 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9442 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9443 printf("GameActions(): This should never happen!\n");
9445 ChangePage[x][y] = -1;
9450 if (WasJustMoving[x][y] > 0)
9451 WasJustMoving[x][y]--;
9452 if (WasJustFalling[x][y] > 0)
9453 WasJustFalling[x][y]--;
9454 if (CheckCollision[x][y] > 0)
9455 CheckCollision[x][y]--;
9459 /* reset finished pushing action (not done in ContinueMoving() to allow
9460 continuous pushing animation for elements with zero push delay) */
9461 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9463 ResetGfxAnimation(x, y);
9464 DrawLevelField(x, y);
9468 if (IS_BLOCKED(x, y))
9472 Blocked2Moving(x, y, &oldx, &oldy);
9473 if (!IS_MOVING(oldx, oldy))
9475 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9476 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9477 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9478 printf("GameActions(): This should never happen!\n");
9485 SCAN_PLAYFIELD(x, y)
9487 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9490 element = Feld[x][y];
9491 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9494 printf("::: %d,%d\n", x, y);
9496 if (element == EL_ROCK)
9497 printf("::: Yo man! Rocks can fall!\n");
9500 if (graphic_info[graphic].anim_global_sync)
9501 GfxFrame[x][y] = FrameCounter;
9502 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9504 int old_gfx_frame = GfxFrame[x][y];
9506 GfxFrame[x][y] = CustomValue[x][y];
9509 if (GfxFrame[x][y] != old_gfx_frame)
9511 DrawLevelGraphicAnimation(x, y, graphic);
9513 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9515 int old_gfx_frame = GfxFrame[x][y];
9517 GfxFrame[x][y] = element_info[element].collect_score;
9520 if (GfxFrame[x][y] != old_gfx_frame)
9522 DrawLevelGraphicAnimation(x, y, graphic);
9525 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9526 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9527 ResetRandomAnimationValue(x, y);
9529 SetRandomAnimationValue(x, y);
9531 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9533 if (IS_INACTIVE(element))
9535 if (IS_ANIMATED(graphic))
9536 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9541 /* this may take place after moving, so 'element' may have changed */
9542 if (IS_CHANGING(x, y) &&
9543 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9545 int page = element_info[element].event_page_nr[CE_DELAY];
9547 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9551 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9555 if (element == EL_CUSTOM_255)
9556 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9560 HandleElementChange(x, y, page);
9562 if (CAN_CHANGE(element))
9563 HandleElementChange(x, y, page);
9565 if (HAS_ACTION(element))
9566 ExecuteCustomElementAction(x, y, element, page);
9571 element = Feld[x][y];
9572 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9575 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9579 element = Feld[x][y];
9580 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9582 if (IS_ANIMATED(graphic) &&
9585 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9587 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9588 EdelsteinFunkeln(x, y);
9590 else if ((element == EL_ACID ||
9591 element == EL_EXIT_OPEN ||
9592 element == EL_SP_EXIT_OPEN ||
9593 element == EL_SP_TERMINAL ||
9594 element == EL_SP_TERMINAL_ACTIVE ||
9595 element == EL_EXTRA_TIME ||
9596 element == EL_SHIELD_NORMAL ||
9597 element == EL_SHIELD_DEADLY) &&
9598 IS_ANIMATED(graphic))
9599 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9600 else if (IS_MOVING(x, y))
9601 ContinueMoving(x, y);
9602 else if (IS_ACTIVE_BOMB(element))
9603 CheckDynamite(x, y);
9604 else if (element == EL_AMOEBA_GROWING)
9605 AmoebeWaechst(x, y);
9606 else if (element == EL_AMOEBA_SHRINKING)
9607 AmoebaDisappearing(x, y);
9609 #if !USE_NEW_AMOEBA_CODE
9610 else if (IS_AMOEBALIVE(element))
9611 AmoebeAbleger(x, y);
9614 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9616 else if (element == EL_EXIT_CLOSED)
9618 else if (element == EL_SP_EXIT_CLOSED)
9620 else if (element == EL_EXPANDABLE_WALL_GROWING)
9622 else if (element == EL_EXPANDABLE_WALL ||
9623 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9624 element == EL_EXPANDABLE_WALL_VERTICAL ||
9625 element == EL_EXPANDABLE_WALL_ANY)
9627 else if (element == EL_FLAMES)
9628 CheckForDragon(x, y);
9629 else if (element == EL_EXPLOSION)
9630 ; /* drawing of correct explosion animation is handled separately */
9631 else if (element == EL_ELEMENT_SNAPPING ||
9632 element == EL_DIAGONAL_SHRINKING ||
9633 element == EL_DIAGONAL_GROWING)
9636 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9638 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9641 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9642 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9645 if (element == EL_CUSTOM_255 ||
9646 element == EL_CUSTOM_256)
9647 DrawLevelGraphicAnimation(x, y, graphic);
9650 if (IS_BELT_ACTIVE(element))
9651 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9653 if (game.magic_wall_active)
9655 int jx = local_player->jx, jy = local_player->jy;
9657 /* play the element sound at the position nearest to the player */
9658 if ((element == EL_MAGIC_WALL_FULL ||
9659 element == EL_MAGIC_WALL_ACTIVE ||
9660 element == EL_MAGIC_WALL_EMPTYING ||
9661 element == EL_BD_MAGIC_WALL_FULL ||
9662 element == EL_BD_MAGIC_WALL_ACTIVE ||
9663 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9664 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9672 #if USE_NEW_AMOEBA_CODE
9673 /* new experimental amoeba growth stuff */
9674 if (!(FrameCounter % 8))
9676 static unsigned long random = 1684108901;
9678 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9680 x = RND(lev_fieldx);
9681 y = RND(lev_fieldy);
9682 element = Feld[x][y];
9684 if (!IS_PLAYER(x,y) &&
9685 (element == EL_EMPTY ||
9686 CAN_GROW_INTO(element) ||
9687 element == EL_QUICKSAND_EMPTY ||
9688 element == EL_ACID_SPLASH_LEFT ||
9689 element == EL_ACID_SPLASH_RIGHT))
9691 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9692 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9693 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9694 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9695 Feld[x][y] = EL_AMOEBA_DROP;
9698 random = random * 129 + 1;
9704 if (game.explosions_delayed)
9707 game.explosions_delayed = FALSE;
9710 SCAN_PLAYFIELD(x, y)
9712 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9715 element = Feld[x][y];
9717 if (ExplodeField[x][y])
9718 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9719 else if (element == EL_EXPLOSION)
9720 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9722 ExplodeField[x][y] = EX_TYPE_NONE;
9725 game.explosions_delayed = TRUE;
9728 if (game.magic_wall_active)
9730 if (!(game.magic_wall_time_left % 4))
9732 int element = Feld[magic_wall_x][magic_wall_y];
9734 if (element == EL_BD_MAGIC_WALL_FULL ||
9735 element == EL_BD_MAGIC_WALL_ACTIVE ||
9736 element == EL_BD_MAGIC_WALL_EMPTYING)
9737 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9739 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9742 if (game.magic_wall_time_left > 0)
9744 game.magic_wall_time_left--;
9745 if (!game.magic_wall_time_left)
9748 SCAN_PLAYFIELD(x, y)
9750 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9753 element = Feld[x][y];
9755 if (element == EL_MAGIC_WALL_ACTIVE ||
9756 element == EL_MAGIC_WALL_FULL)
9758 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9759 DrawLevelField(x, y);
9761 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9762 element == EL_BD_MAGIC_WALL_FULL)
9764 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9765 DrawLevelField(x, y);
9769 game.magic_wall_active = FALSE;
9774 if (game.light_time_left > 0)
9776 game.light_time_left--;
9778 if (game.light_time_left == 0)
9779 RedrawAllLightSwitchesAndInvisibleElements();
9782 if (game.timegate_time_left > 0)
9784 game.timegate_time_left--;
9786 if (game.timegate_time_left == 0)
9787 CloseAllOpenTimegates();
9790 if (game.lenses_time_left > 0)
9792 game.lenses_time_left--;
9794 if (game.lenses_time_left == 0)
9795 RedrawAllInvisibleElementsForLenses();
9798 if (game.magnify_time_left > 0)
9800 game.magnify_time_left--;
9802 if (game.magnify_time_left == 0)
9803 RedrawAllInvisibleElementsForMagnifier();
9806 for (i = 0; i < MAX_PLAYERS; i++)
9808 struct PlayerInfo *player = &stored_player[i];
9810 if (SHIELD_ON(player))
9812 if (player->shield_deadly_time_left)
9813 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9814 else if (player->shield_normal_time_left)
9815 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9819 if (TimeFrames >= FRAMES_PER_SECOND)
9824 for (i = 0; i < MAX_PLAYERS; i++)
9826 struct PlayerInfo *player = &stored_player[i];
9828 if (SHIELD_ON(player))
9830 player->shield_normal_time_left--;
9832 if (player->shield_deadly_time_left > 0)
9833 player->shield_deadly_time_left--;
9837 if (!level.use_step_counter)
9845 if (TimeLeft <= 10 && setup.time_limit)
9846 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9848 DrawGameValue_Time(TimeLeft);
9850 if (!TimeLeft && setup.time_limit)
9851 for (i = 0; i < MAX_PLAYERS; i++)
9852 KillPlayer(&stored_player[i]);
9854 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9855 DrawGameValue_Time(TimePlayed);
9858 if (tape.recording || tape.playing)
9859 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9863 PlayAllPlayersSound();
9865 if (options.debug) /* calculate frames per second */
9867 static unsigned long fps_counter = 0;
9868 static int fps_frames = 0;
9869 unsigned long fps_delay_ms = Counter() - fps_counter;
9873 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9875 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9878 fps_counter = Counter();
9881 redraw_mask |= REDRAW_FPS;
9884 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9886 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9888 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9890 local_player->show_envelope = 0;
9893 /* use random number generator in every frame to make it less predictable */
9894 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9898 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9900 int min_x = x, min_y = y, max_x = x, max_y = y;
9903 for (i = 0; i < MAX_PLAYERS; i++)
9905 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9907 if (!stored_player[i].active || &stored_player[i] == player)
9910 min_x = MIN(min_x, jx);
9911 min_y = MIN(min_y, jy);
9912 max_x = MAX(max_x, jx);
9913 max_y = MAX(max_y, jy);
9916 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9919 static boolean AllPlayersInVisibleScreen()
9923 for (i = 0; i < MAX_PLAYERS; i++)
9925 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9927 if (!stored_player[i].active)
9930 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9937 void ScrollLevel(int dx, int dy)
9939 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9942 BlitBitmap(drawto_field, drawto_field,
9943 FX + TILEX * (dx == -1) - softscroll_offset,
9944 FY + TILEY * (dy == -1) - softscroll_offset,
9945 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9946 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9947 FX + TILEX * (dx == 1) - softscroll_offset,
9948 FY + TILEY * (dy == 1) - softscroll_offset);
9952 x = (dx == 1 ? BX1 : BX2);
9953 for (y = BY1; y <= BY2; y++)
9954 DrawScreenField(x, y);
9959 y = (dy == 1 ? BY1 : BY2);
9960 for (x = BX1; x <= BX2; x++)
9961 DrawScreenField(x, y);
9964 redraw_mask |= REDRAW_FIELD;
9967 static boolean canFallDown(struct PlayerInfo *player)
9969 int jx = player->jx, jy = player->jy;
9971 return (IN_LEV_FIELD(jx, jy + 1) &&
9972 (IS_FREE(jx, jy + 1) ||
9973 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9974 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9975 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9978 static boolean canPassField(int x, int y, int move_dir)
9980 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9981 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9982 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9985 int element = Feld[x][y];
9987 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9988 !CAN_MOVE(element) &&
9989 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9990 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9991 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9994 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9996 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9997 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9998 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10002 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10003 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10004 (IS_DIGGABLE(Feld[newx][newy]) ||
10005 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10006 canPassField(newx, newy, move_dir)));
10009 static void CheckGravityMovement(struct PlayerInfo *player)
10011 if (game.gravity && !player->programmed_action)
10013 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10014 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10015 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10016 int jx = player->jx, jy = player->jy;
10017 boolean player_is_moving_to_valid_field =
10018 (!player_is_snapping &&
10019 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10020 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10021 boolean player_can_fall_down = canFallDown(player);
10023 if (player_can_fall_down &&
10024 !player_is_moving_to_valid_field)
10025 player->programmed_action = MV_DOWN;
10029 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10031 return CheckGravityMovement(player);
10033 if (game.gravity && !player->programmed_action)
10035 int jx = player->jx, jy = player->jy;
10036 boolean field_under_player_is_free =
10037 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10038 boolean player_is_standing_on_valid_field =
10039 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10040 (IS_WALKABLE(Feld[jx][jy]) &&
10041 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10043 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10044 player->programmed_action = MV_DOWN;
10049 MovePlayerOneStep()
10050 -----------------------------------------------------------------------------
10051 dx, dy: direction (non-diagonal) to try to move the player to
10052 real_dx, real_dy: direction as read from input device (can be diagonal)
10055 boolean MovePlayerOneStep(struct PlayerInfo *player,
10056 int dx, int dy, int real_dx, int real_dy)
10058 int jx = player->jx, jy = player->jy;
10059 int new_jx = jx + dx, new_jy = jy + dy;
10060 #if !USE_FIXED_DONT_RUN_INTO
10064 boolean player_can_move = !player->cannot_move;
10066 if (!player->active || (!dx && !dy))
10067 return MP_NO_ACTION;
10069 player->MovDir = (dx < 0 ? MV_LEFT :
10070 dx > 0 ? MV_RIGHT :
10072 dy > 0 ? MV_DOWN : MV_NONE);
10074 if (!IN_LEV_FIELD(new_jx, new_jy))
10075 return MP_NO_ACTION;
10077 if (!player_can_move)
10080 if (player->MovPos == 0)
10082 player->is_moving = FALSE;
10083 player->is_digging = FALSE;
10084 player->is_collecting = FALSE;
10085 player->is_snapping = FALSE;
10086 player->is_pushing = FALSE;
10089 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10090 SnapField(player, 0, 0);
10094 return MP_NO_ACTION;
10099 if (!options.network && game.centered_player_nr == -1 &&
10100 !AllPlayersInSight(player, new_jx, new_jy))
10101 return MP_NO_ACTION;
10103 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10104 return MP_NO_ACTION;
10107 #if !USE_FIXED_DONT_RUN_INTO
10108 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10110 /* (moved to DigField()) */
10111 if (player_can_move && DONT_RUN_INTO(element))
10113 if (element == EL_ACID && dx == 0 && dy == 1)
10115 SplashAcid(new_jx, new_jy);
10116 Feld[jx][jy] = EL_PLAYER_1;
10117 InitMovingField(jx, jy, MV_DOWN);
10118 Store[jx][jy] = EL_ACID;
10119 ContinueMoving(jx, jy);
10120 BuryPlayer(player);
10123 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10129 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10131 #if USE_FIXED_DONT_RUN_INTO
10132 if (can_move == MP_DONT_RUN_INTO)
10136 if (can_move != MP_MOVING)
10139 #if USE_FIXED_DONT_RUN_INTO
10142 /* check if DigField() has caused relocation of the player */
10143 if (player->jx != jx || player->jy != jy)
10144 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10146 StorePlayer[jx][jy] = 0;
10147 player->last_jx = jx;
10148 player->last_jy = jy;
10149 player->jx = new_jx;
10150 player->jy = new_jy;
10151 StorePlayer[new_jx][new_jy] = player->element_nr;
10153 if (player->move_delay_value_next != -1)
10155 player->move_delay_value = player->move_delay_value_next;
10156 player->move_delay_value_next = -1;
10160 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10162 player->step_counter++;
10164 PlayerVisit[jx][jy] = FrameCounter;
10166 ScrollPlayer(player, SCROLL_INIT);
10171 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10173 int jx = player->jx, jy = player->jy;
10174 int old_jx = jx, old_jy = jy;
10175 int moved = MP_NO_ACTION;
10177 if (!player->active)
10182 if (player->MovPos == 0)
10184 player->is_moving = FALSE;
10185 player->is_digging = FALSE;
10186 player->is_collecting = FALSE;
10187 player->is_snapping = FALSE;
10188 player->is_pushing = FALSE;
10194 if (player->move_delay > 0)
10197 player->move_delay = -1; /* set to "uninitialized" value */
10199 /* store if player is automatically moved to next field */
10200 player->is_auto_moving = (player->programmed_action != MV_NONE);
10202 /* remove the last programmed player action */
10203 player->programmed_action = 0;
10205 if (player->MovPos)
10207 /* should only happen if pre-1.2 tape recordings are played */
10208 /* this is only for backward compatibility */
10210 int original_move_delay_value = player->move_delay_value;
10213 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10217 /* scroll remaining steps with finest movement resolution */
10218 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10220 while (player->MovPos)
10222 ScrollPlayer(player, SCROLL_GO_ON);
10223 ScrollScreen(NULL, SCROLL_GO_ON);
10225 AdvanceFrameAndPlayerCounters(player->index_nr);
10231 player->move_delay_value = original_move_delay_value;
10234 if (player->last_move_dir & MV_HORIZONTAL)
10236 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10237 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10241 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10242 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10249 if (moved & MP_MOVING && !ScreenMovPos &&
10250 (player->index_nr == game.centered_player_nr ||
10251 game.centered_player_nr == -1))
10253 if (moved & MP_MOVING && !ScreenMovPos &&
10254 (player == local_player || !options.network))
10257 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10258 int offset = (setup.scroll_delay ? 3 : 0);
10260 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10262 /* actual player has left the screen -- scroll in that direction */
10263 if (jx != old_jx) /* player has moved horizontally */
10264 scroll_x += (jx - old_jx);
10265 else /* player has moved vertically */
10266 scroll_y += (jy - old_jy);
10270 if (jx != old_jx) /* player has moved horizontally */
10272 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10273 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10274 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10276 /* don't scroll over playfield boundaries */
10277 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10278 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10280 /* don't scroll more than one field at a time */
10281 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10283 /* don't scroll against the player's moving direction */
10284 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10285 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10286 scroll_x = old_scroll_x;
10288 else /* player has moved vertically */
10290 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10291 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10292 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10294 /* don't scroll over playfield boundaries */
10295 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10296 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10298 /* don't scroll more than one field at a time */
10299 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10301 /* don't scroll against the player's moving direction */
10302 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10303 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10304 scroll_y = old_scroll_y;
10308 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10311 if (!options.network && game.centered_player_nr == -1 &&
10312 !AllPlayersInVisibleScreen())
10314 scroll_x = old_scroll_x;
10315 scroll_y = old_scroll_y;
10319 if (!options.network && !AllPlayersInVisibleScreen())
10321 scroll_x = old_scroll_x;
10322 scroll_y = old_scroll_y;
10327 ScrollScreen(player, SCROLL_INIT);
10328 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10333 player->StepFrame = 0;
10335 if (moved & MP_MOVING)
10337 if (old_jx != jx && old_jy == jy)
10338 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10339 else if (old_jx == jx && old_jy != jy)
10340 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10342 DrawLevelField(jx, jy); /* for "crumbled sand" */
10344 player->last_move_dir = player->MovDir;
10345 player->is_moving = TRUE;
10346 player->is_snapping = FALSE;
10347 player->is_switching = FALSE;
10348 player->is_dropping = FALSE;
10349 player->is_dropping_pressed = FALSE;
10350 player->drop_pressed_delay = 0;
10354 CheckGravityMovementWhenNotMoving(player);
10356 player->is_moving = FALSE;
10358 /* at this point, the player is allowed to move, but cannot move right now
10359 (e.g. because of something blocking the way) -- ensure that the player
10360 is also allowed to move in the next frame (in old versions before 3.1.1,
10361 the player was forced to wait again for eight frames before next try) */
10363 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10364 player->move_delay = 0; /* allow direct movement in the next frame */
10367 if (player->move_delay == -1) /* not yet initialized by DigField() */
10368 player->move_delay = player->move_delay_value;
10370 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10372 TestIfPlayerTouchesBadThing(jx, jy);
10373 TestIfPlayerTouchesCustomElement(jx, jy);
10376 if (!player->active)
10377 RemovePlayer(player);
10382 void ScrollPlayer(struct PlayerInfo *player, int mode)
10384 int jx = player->jx, jy = player->jy;
10385 int last_jx = player->last_jx, last_jy = player->last_jy;
10386 int move_stepsize = TILEX / player->move_delay_value;
10388 #if USE_NEW_PLAYER_SPEED
10389 if (!player->active)
10392 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10395 if (!player->active || player->MovPos == 0)
10399 if (mode == SCROLL_INIT)
10401 player->actual_frame_counter = FrameCounter;
10402 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10404 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10405 Feld[last_jx][last_jy] == EL_EMPTY)
10407 int last_field_block_delay = 0; /* start with no blocking at all */
10408 int block_delay_adjustment = player->block_delay_adjustment;
10410 /* if player blocks last field, add delay for exactly one move */
10411 if (player->block_last_field)
10413 last_field_block_delay += player->move_delay_value;
10415 /* when blocking enabled, prevent moving up despite gravity */
10416 if (game.gravity && player->MovDir == MV_UP)
10417 block_delay_adjustment = -1;
10420 /* add block delay adjustment (also possible when not blocking) */
10421 last_field_block_delay += block_delay_adjustment;
10423 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10424 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10427 #if USE_NEW_PLAYER_SPEED
10428 if (player->MovPos != 0) /* player has not yet reached destination */
10434 else if (!FrameReached(&player->actual_frame_counter, 1))
10438 printf("::: player->MovPos: %d -> %d\n",
10440 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10443 #if USE_NEW_PLAYER_SPEED
10444 if (player->MovPos != 0)
10446 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10447 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10449 /* before DrawPlayer() to draw correct player graphic for this case */
10450 if (player->MovPos == 0)
10451 CheckGravityMovement(player);
10454 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10455 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10457 /* before DrawPlayer() to draw correct player graphic for this case */
10458 if (player->MovPos == 0)
10459 CheckGravityMovement(player);
10462 if (player->MovPos == 0) /* player reached destination field */
10465 printf("::: player reached destination field\n");
10468 if (player->move_delay_reset_counter > 0)
10470 player->move_delay_reset_counter--;
10472 if (player->move_delay_reset_counter == 0)
10474 /* continue with normal speed after quickly moving through gate */
10475 HALVE_PLAYER_SPEED(player);
10477 /* be able to make the next move without delay */
10478 player->move_delay = 0;
10482 player->last_jx = jx;
10483 player->last_jy = jy;
10485 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10486 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10487 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10489 DrawPlayer(player); /* needed here only to cleanup last field */
10490 RemovePlayer(player);
10492 if (local_player->friends_still_needed == 0 ||
10493 IS_SP_ELEMENT(Feld[jx][jy]))
10494 player->LevelSolved = player->GameOver = TRUE;
10497 /* this breaks one level: "machine", level 000 */
10499 int move_direction = player->MovDir;
10500 int enter_side = MV_DIR_OPPOSITE(move_direction);
10501 int leave_side = move_direction;
10502 int old_jx = last_jx;
10503 int old_jy = last_jy;
10504 int old_element = Feld[old_jx][old_jy];
10505 int new_element = Feld[jx][jy];
10507 if (IS_CUSTOM_ELEMENT(old_element))
10508 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10510 player->index_bit, leave_side);
10512 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10513 CE_PLAYER_LEAVES_X,
10514 player->index_bit, leave_side);
10516 if (IS_CUSTOM_ELEMENT(new_element))
10517 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10518 player->index_bit, enter_side);
10520 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10521 CE_PLAYER_ENTERS_X,
10522 player->index_bit, enter_side);
10524 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10525 CE_MOVE_OF_X, move_direction);
10528 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10530 TestIfPlayerTouchesBadThing(jx, jy);
10531 TestIfPlayerTouchesCustomElement(jx, jy);
10533 /* needed because pushed element has not yet reached its destination,
10534 so it would trigger a change event at its previous field location */
10535 if (!player->is_pushing)
10536 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10538 if (!player->active)
10539 RemovePlayer(player);
10542 if (level.use_step_counter)
10552 if (TimeLeft <= 10 && setup.time_limit)
10553 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10555 DrawGameValue_Time(TimeLeft);
10557 if (!TimeLeft && setup.time_limit)
10558 for (i = 0; i < MAX_PLAYERS; i++)
10559 KillPlayer(&stored_player[i]);
10561 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10562 DrawGameValue_Time(TimePlayed);
10565 if (tape.single_step && tape.recording && !tape.pausing &&
10566 !player->programmed_action)
10567 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10571 void ScrollScreen(struct PlayerInfo *player, int mode)
10573 static unsigned long screen_frame_counter = 0;
10575 if (mode == SCROLL_INIT)
10577 /* set scrolling step size according to actual player's moving speed */
10578 ScrollStepSize = TILEX / player->move_delay_value;
10580 screen_frame_counter = FrameCounter;
10581 ScreenMovDir = player->MovDir;
10582 ScreenMovPos = player->MovPos;
10583 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10586 else if (!FrameReached(&screen_frame_counter, 1))
10591 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10592 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10593 redraw_mask |= REDRAW_FIELD;
10596 ScreenMovDir = MV_NONE;
10599 void TestIfPlayerTouchesCustomElement(int x, int y)
10601 static int xy[4][2] =
10608 static int trigger_sides[4][2] =
10610 /* center side border side */
10611 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10612 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10613 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10614 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10616 static int touch_dir[4] =
10618 MV_LEFT | MV_RIGHT,
10623 int center_element = Feld[x][y]; /* should always be non-moving! */
10626 for (i = 0; i < NUM_DIRECTIONS; i++)
10628 int xx = x + xy[i][0];
10629 int yy = y + xy[i][1];
10630 int center_side = trigger_sides[i][0];
10631 int border_side = trigger_sides[i][1];
10632 int border_element;
10634 if (!IN_LEV_FIELD(xx, yy))
10637 if (IS_PLAYER(x, y))
10639 struct PlayerInfo *player = PLAYERINFO(x, y);
10641 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10642 border_element = Feld[xx][yy]; /* may be moving! */
10643 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10644 border_element = Feld[xx][yy];
10645 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10646 border_element = MovingOrBlocked2Element(xx, yy);
10648 continue; /* center and border element do not touch */
10650 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10651 player->index_bit, border_side);
10652 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10653 CE_PLAYER_TOUCHES_X,
10654 player->index_bit, border_side);
10656 else if (IS_PLAYER(xx, yy))
10658 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10660 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10662 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10663 continue; /* center and border element do not touch */
10666 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10667 player->index_bit, center_side);
10668 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10669 CE_PLAYER_TOUCHES_X,
10670 player->index_bit, center_side);
10676 #if USE_ELEMENT_TOUCHING_BUGFIX
10678 void TestIfElementTouchesCustomElement(int x, int y)
10680 static int xy[4][2] =
10687 static int trigger_sides[4][2] =
10689 /* center side border side */
10690 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10691 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10692 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10693 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10695 static int touch_dir[4] =
10697 MV_LEFT | MV_RIGHT,
10702 boolean change_center_element = FALSE;
10703 int center_element = Feld[x][y]; /* should always be non-moving! */
10704 int border_element_old[NUM_DIRECTIONS];
10707 for (i = 0; i < NUM_DIRECTIONS; i++)
10709 int xx = x + xy[i][0];
10710 int yy = y + xy[i][1];
10711 int border_element;
10713 border_element_old[i] = -1;
10715 if (!IN_LEV_FIELD(xx, yy))
10718 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10719 border_element = Feld[xx][yy]; /* may be moving! */
10720 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10721 border_element = Feld[xx][yy];
10722 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10723 border_element = MovingOrBlocked2Element(xx, yy);
10725 continue; /* center and border element do not touch */
10727 border_element_old[i] = border_element;
10730 for (i = 0; i < NUM_DIRECTIONS; i++)
10732 int xx = x + xy[i][0];
10733 int yy = y + xy[i][1];
10734 int center_side = trigger_sides[i][0];
10735 int border_element = border_element_old[i];
10737 if (border_element == -1)
10740 /* check for change of border element */
10741 CheckElementChangeBySide(xx, yy, border_element, center_element,
10742 CE_TOUCHING_X, center_side);
10745 for (i = 0; i < NUM_DIRECTIONS; i++)
10747 int border_side = trigger_sides[i][1];
10748 int border_element = border_element_old[i];
10750 if (border_element == -1)
10753 /* check for change of center element (but change it only once) */
10754 if (!change_center_element)
10755 change_center_element =
10756 CheckElementChangeBySide(x, y, center_element, border_element,
10757 CE_TOUCHING_X, border_side);
10763 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10765 static int xy[4][2] =
10772 static int trigger_sides[4][2] =
10774 /* center side border side */
10775 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10776 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10777 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10778 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10780 static int touch_dir[4] =
10782 MV_LEFT | MV_RIGHT,
10787 boolean change_center_element = FALSE;
10788 int center_element = Feld[x][y]; /* should always be non-moving! */
10791 for (i = 0; i < NUM_DIRECTIONS; i++)
10793 int xx = x + xy[i][0];
10794 int yy = y + xy[i][1];
10795 int center_side = trigger_sides[i][0];
10796 int border_side = trigger_sides[i][1];
10797 int border_element;
10799 if (!IN_LEV_FIELD(xx, yy))
10802 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10803 border_element = Feld[xx][yy]; /* may be moving! */
10804 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10805 border_element = Feld[xx][yy];
10806 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10807 border_element = MovingOrBlocked2Element(xx, yy);
10809 continue; /* center and border element do not touch */
10811 /* check for change of center element (but change it only once) */
10812 if (!change_center_element)
10813 change_center_element =
10814 CheckElementChangeBySide(x, y, center_element, border_element,
10815 CE_TOUCHING_X, border_side);
10817 /* check for change of border element */
10818 CheckElementChangeBySide(xx, yy, border_element, center_element,
10819 CE_TOUCHING_X, center_side);
10825 void TestIfElementHitsCustomElement(int x, int y, int direction)
10827 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10828 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10829 int hitx = x + dx, hity = y + dy;
10830 int hitting_element = Feld[x][y];
10831 int touched_element;
10833 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10836 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10837 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10839 if (IN_LEV_FIELD(hitx, hity))
10841 int opposite_direction = MV_DIR_OPPOSITE(direction);
10842 int hitting_side = direction;
10843 int touched_side = opposite_direction;
10844 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10845 MovDir[hitx][hity] != direction ||
10846 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10852 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10853 CE_HITTING_X, touched_side);
10855 CheckElementChangeBySide(hitx, hity, touched_element,
10856 hitting_element, CE_HIT_BY_X, hitting_side);
10858 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10859 CE_HIT_BY_SOMETHING, opposite_direction);
10863 /* "hitting something" is also true when hitting the playfield border */
10864 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10865 CE_HITTING_SOMETHING, direction);
10869 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10871 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10872 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10873 int hitx = x + dx, hity = y + dy;
10874 int hitting_element = Feld[x][y];
10875 int touched_element;
10877 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10878 !IS_FREE(hitx, hity) &&
10879 (!IS_MOVING(hitx, hity) ||
10880 MovDir[hitx][hity] != direction ||
10881 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10884 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10888 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10892 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10893 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10895 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10896 EP_CAN_SMASH_EVERYTHING, direction);
10898 if (IN_LEV_FIELD(hitx, hity))
10900 int opposite_direction = MV_DIR_OPPOSITE(direction);
10901 int hitting_side = direction;
10902 int touched_side = opposite_direction;
10904 int touched_element = MovingOrBlocked2Element(hitx, hity);
10907 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10908 MovDir[hitx][hity] != direction ||
10909 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10918 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10919 CE_SMASHED_BY_SOMETHING, opposite_direction);
10921 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10922 CE_OTHER_IS_SMASHING, touched_side);
10924 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10925 CE_OTHER_GETS_SMASHED, hitting_side);
10931 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10933 int i, kill_x = -1, kill_y = -1;
10935 int bad_element = -1;
10936 static int test_xy[4][2] =
10943 static int test_dir[4] =
10951 for (i = 0; i < NUM_DIRECTIONS; i++)
10953 int test_x, test_y, test_move_dir, test_element;
10955 test_x = good_x + test_xy[i][0];
10956 test_y = good_y + test_xy[i][1];
10958 if (!IN_LEV_FIELD(test_x, test_y))
10962 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10964 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10966 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10967 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10969 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10970 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10974 bad_element = test_element;
10980 if (kill_x != -1 || kill_y != -1)
10982 if (IS_PLAYER(good_x, good_y))
10984 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10986 if (player->shield_deadly_time_left > 0 &&
10987 !IS_INDESTRUCTIBLE(bad_element))
10988 Bang(kill_x, kill_y);
10989 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10990 KillPlayer(player);
10993 Bang(good_x, good_y);
10997 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10999 int i, kill_x = -1, kill_y = -1;
11000 int bad_element = Feld[bad_x][bad_y];
11001 static int test_xy[4][2] =
11008 static int touch_dir[4] =
11010 MV_LEFT | MV_RIGHT,
11015 static int test_dir[4] =
11023 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11026 for (i = 0; i < NUM_DIRECTIONS; i++)
11028 int test_x, test_y, test_move_dir, test_element;
11030 test_x = bad_x + test_xy[i][0];
11031 test_y = bad_y + test_xy[i][1];
11032 if (!IN_LEV_FIELD(test_x, test_y))
11036 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11038 test_element = Feld[test_x][test_y];
11040 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11041 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11043 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11044 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11046 /* good thing is player or penguin that does not move away */
11047 if (IS_PLAYER(test_x, test_y))
11049 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11051 if (bad_element == EL_ROBOT && player->is_moving)
11052 continue; /* robot does not kill player if he is moving */
11054 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11056 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11057 continue; /* center and border element do not touch */
11064 else if (test_element == EL_PENGUIN)
11073 if (kill_x != -1 || kill_y != -1)
11075 if (IS_PLAYER(kill_x, kill_y))
11077 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11079 if (player->shield_deadly_time_left > 0 &&
11080 !IS_INDESTRUCTIBLE(bad_element))
11081 Bang(bad_x, bad_y);
11082 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11083 KillPlayer(player);
11086 Bang(kill_x, kill_y);
11090 void TestIfPlayerTouchesBadThing(int x, int y)
11092 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11095 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11097 TestIfGoodThingHitsBadThing(x, y, move_dir);
11100 void TestIfBadThingTouchesPlayer(int x, int y)
11102 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11105 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11107 TestIfBadThingHitsGoodThing(x, y, move_dir);
11110 void TestIfFriendTouchesBadThing(int x, int y)
11112 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11115 void TestIfBadThingTouchesFriend(int x, int y)
11117 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11120 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11122 int i, kill_x = bad_x, kill_y = bad_y;
11123 static int xy[4][2] =
11131 for (i = 0; i < NUM_DIRECTIONS; i++)
11135 x = bad_x + xy[i][0];
11136 y = bad_y + xy[i][1];
11137 if (!IN_LEV_FIELD(x, y))
11140 element = Feld[x][y];
11141 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11142 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11150 if (kill_x != bad_x || kill_y != bad_y)
11151 Bang(bad_x, bad_y);
11154 void KillPlayer(struct PlayerInfo *player)
11156 int jx = player->jx, jy = player->jy;
11158 if (!player->active)
11161 /* remove accessible field at the player's position */
11162 Feld[jx][jy] = EL_EMPTY;
11164 /* deactivate shield (else Bang()/Explode() would not work right) */
11165 player->shield_normal_time_left = 0;
11166 player->shield_deadly_time_left = 0;
11169 BuryPlayer(player);
11172 static void KillPlayerUnlessEnemyProtected(int x, int y)
11174 if (!PLAYER_ENEMY_PROTECTED(x, y))
11175 KillPlayer(PLAYERINFO(x, y));
11178 static void KillPlayerUnlessExplosionProtected(int x, int y)
11180 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11181 KillPlayer(PLAYERINFO(x, y));
11184 void BuryPlayer(struct PlayerInfo *player)
11186 int jx = player->jx, jy = player->jy;
11188 if (!player->active)
11191 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11192 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11194 player->GameOver = TRUE;
11195 RemovePlayer(player);
11198 void RemovePlayer(struct PlayerInfo *player)
11200 int jx = player->jx, jy = player->jy;
11201 int i, found = FALSE;
11203 player->present = FALSE;
11204 player->active = FALSE;
11206 if (!ExplodeField[jx][jy])
11207 StorePlayer[jx][jy] = 0;
11209 if (player->is_moving)
11210 DrawLevelField(player->last_jx, player->last_jy);
11212 for (i = 0; i < MAX_PLAYERS; i++)
11213 if (stored_player[i].active)
11217 AllPlayersGone = TRUE;
11223 #if USE_NEW_SNAP_DELAY
11224 static void setFieldForSnapping(int x, int y, int element, int direction)
11226 struct ElementInfo *ei = &element_info[element];
11227 int direction_bit = MV_DIR_TO_BIT(direction);
11228 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11229 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11230 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11232 Feld[x][y] = EL_ELEMENT_SNAPPING;
11233 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11235 ResetGfxAnimation(x, y);
11237 GfxElement[x][y] = element;
11238 GfxAction[x][y] = action;
11239 GfxDir[x][y] = direction;
11240 GfxFrame[x][y] = -1;
11245 =============================================================================
11246 checkDiagonalPushing()
11247 -----------------------------------------------------------------------------
11248 check if diagonal input device direction results in pushing of object
11249 (by checking if the alternative direction is walkable, diggable, ...)
11250 =============================================================================
11253 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11254 int x, int y, int real_dx, int real_dy)
11256 int jx, jy, dx, dy, xx, yy;
11258 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11261 /* diagonal direction: check alternative direction */
11266 xx = jx + (dx == 0 ? real_dx : 0);
11267 yy = jy + (dy == 0 ? real_dy : 0);
11269 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11273 =============================================================================
11275 -----------------------------------------------------------------------------
11276 x, y: field next to player (non-diagonal) to try to dig to
11277 real_dx, real_dy: direction as read from input device (can be diagonal)
11278 =============================================================================
11281 int DigField(struct PlayerInfo *player,
11282 int oldx, int oldy, int x, int y,
11283 int real_dx, int real_dy, int mode)
11285 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11286 boolean player_was_pushing = player->is_pushing;
11287 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11288 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11289 int jx = oldx, jy = oldy;
11290 int dx = x - jx, dy = y - jy;
11291 int nextx = x + dx, nexty = y + dy;
11292 int move_direction = (dx == -1 ? MV_LEFT :
11293 dx == +1 ? MV_RIGHT :
11295 dy == +1 ? MV_DOWN : MV_NONE);
11296 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11297 int dig_side = MV_DIR_OPPOSITE(move_direction);
11298 int old_element = Feld[jx][jy];
11299 #if USE_FIXED_DONT_RUN_INTO
11300 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11306 if (is_player) /* function can also be called by EL_PENGUIN */
11308 if (player->MovPos == 0)
11310 player->is_digging = FALSE;
11311 player->is_collecting = FALSE;
11314 if (player->MovPos == 0) /* last pushing move finished */
11315 player->is_pushing = FALSE;
11317 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11319 player->is_switching = FALSE;
11320 player->push_delay = -1;
11322 return MP_NO_ACTION;
11326 #if !USE_FIXED_DONT_RUN_INTO
11327 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11328 return MP_NO_ACTION;
11331 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11332 old_element = Back[jx][jy];
11334 /* in case of element dropped at player position, check background */
11335 else if (Back[jx][jy] != EL_EMPTY &&
11336 game.engine_version >= VERSION_IDENT(2,2,0,0))
11337 old_element = Back[jx][jy];
11340 #if USE_FIXED_DONT_RUN_INTO
11341 if (player_can_move && DONT_RUN_INTO(element))
11343 if (element == EL_ACID && dx == 0 && dy == 1)
11346 Feld[jx][jy] = EL_PLAYER_1;
11347 InitMovingField(jx, jy, MV_DOWN);
11348 Store[jx][jy] = EL_ACID;
11349 ContinueMoving(jx, jy);
11350 BuryPlayer(player);
11353 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11355 return MP_DONT_RUN_INTO;
11361 #if USE_FIXED_DONT_RUN_INTO
11362 if (player_can_move && DONT_RUN_INTO(element))
11364 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11366 return MP_DONT_RUN_INTO;
11371 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11372 return MP_NO_ACTION; /* field has no opening in this direction */
11374 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11375 return MP_NO_ACTION; /* field has no opening in this direction */
11378 #if USE_FIXED_DONT_RUN_INTO
11379 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11382 Feld[jx][jy] = EL_PLAYER_1;
11383 InitMovingField(jx, jy, MV_DOWN);
11384 Store[jx][jy] = EL_ACID;
11385 ContinueMoving(jx, jy);
11386 BuryPlayer(player);
11388 return MP_DONT_RUN_INTO;
11394 #if USE_FIXED_DONT_RUN_INTO
11395 if (player_can_move && DONT_RUN_INTO(element))
11397 if (element == EL_ACID && dx == 0 && dy == 1)
11400 Feld[jx][jy] = EL_PLAYER_1;
11401 InitMovingField(jx, jy, MV_DOWN);
11402 Store[jx][jy] = EL_ACID;
11403 ContinueMoving(jx, jy);
11404 BuryPlayer(player);
11407 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11409 return MP_DONT_RUN_INTO;
11414 #if USE_FIXED_DONT_RUN_INTO
11415 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11416 return MP_NO_ACTION;
11419 #if !USE_FIXED_DONT_RUN_INTO
11420 element = Feld[x][y];
11423 collect_count = element_info[element].collect_count_initial;
11425 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11426 return MP_NO_ACTION;
11428 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11429 player_can_move = player_can_move_or_snap;
11431 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11432 game.engine_version >= VERSION_IDENT(2,2,0,0))
11434 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11435 player->index_bit, dig_side);
11436 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11437 player->index_bit, dig_side);
11439 if (Feld[x][y] != element) /* field changed by snapping */
11442 return MP_NO_ACTION;
11445 if (game.gravity && is_player && !player->is_auto_moving &&
11446 canFallDown(player) && move_direction != MV_DOWN &&
11447 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11448 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11450 if (player_can_move &&
11451 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11453 int sound_element = SND_ELEMENT(element);
11454 int sound_action = ACTION_WALKING;
11456 if (IS_RND_GATE(element))
11458 if (!player->key[RND_GATE_NR(element)])
11459 return MP_NO_ACTION;
11461 else if (IS_RND_GATE_GRAY(element))
11463 if (!player->key[RND_GATE_GRAY_NR(element)])
11464 return MP_NO_ACTION;
11466 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11468 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11469 return MP_NO_ACTION;
11471 else if (element == EL_EXIT_OPEN ||
11472 element == EL_SP_EXIT_OPEN ||
11473 element == EL_SP_EXIT_OPENING)
11475 sound_action = ACTION_PASSING; /* player is passing exit */
11477 else if (element == EL_EMPTY)
11479 sound_action = ACTION_MOVING; /* nothing to walk on */
11482 /* play sound from background or player, whatever is available */
11483 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11484 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11486 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11488 else if (player_can_move &&
11489 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11491 if (!ACCESS_FROM(element, opposite_direction))
11492 return MP_NO_ACTION; /* field not accessible from this direction */
11494 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11495 return MP_NO_ACTION;
11497 if (IS_EM_GATE(element))
11499 if (!player->key[EM_GATE_NR(element)])
11500 return MP_NO_ACTION;
11502 else if (IS_EM_GATE_GRAY(element))
11504 if (!player->key[EM_GATE_GRAY_NR(element)])
11505 return MP_NO_ACTION;
11507 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11509 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11510 return MP_NO_ACTION;
11512 else if (IS_SP_PORT(element))
11514 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11515 element == EL_SP_GRAVITY_PORT_RIGHT ||
11516 element == EL_SP_GRAVITY_PORT_UP ||
11517 element == EL_SP_GRAVITY_PORT_DOWN)
11518 game.gravity = !game.gravity;
11519 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11520 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11521 element == EL_SP_GRAVITY_ON_PORT_UP ||
11522 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11523 game.gravity = TRUE;
11524 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11525 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11526 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11527 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11528 game.gravity = FALSE;
11531 /* automatically move to the next field with double speed */
11532 player->programmed_action = move_direction;
11534 if (player->move_delay_reset_counter == 0)
11536 player->move_delay_reset_counter = 2; /* two double speed steps */
11538 DOUBLE_PLAYER_SPEED(player);
11541 PlayLevelSoundAction(x, y, ACTION_PASSING);
11543 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11547 if (mode != DF_SNAP)
11549 GfxElement[x][y] = GFX_ELEMENT(element);
11550 player->is_digging = TRUE;
11553 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11555 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11556 player->index_bit, dig_side);
11558 if (mode == DF_SNAP)
11560 #if USE_NEW_SNAP_DELAY
11561 if (level.block_snap_field)
11562 setFieldForSnapping(x, y, element, move_direction);
11564 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11566 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11569 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11570 player->index_bit, dig_side);
11573 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11577 if (is_player && mode != DF_SNAP)
11579 GfxElement[x][y] = element;
11580 player->is_collecting = TRUE;
11583 if (element == EL_SPEED_PILL)
11585 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11587 else if (element == EL_EXTRA_TIME && level.time > 0)
11589 TimeLeft += level.extra_time;
11590 DrawGameValue_Time(TimeLeft);
11592 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11594 player->shield_normal_time_left += level.shield_normal_time;
11595 if (element == EL_SHIELD_DEADLY)
11596 player->shield_deadly_time_left += level.shield_deadly_time;
11598 else if (element == EL_DYNAMITE ||
11599 element == EL_EM_DYNAMITE ||
11600 element == EL_SP_DISK_RED)
11602 if (player->inventory_size < MAX_INVENTORY_SIZE)
11603 player->inventory_element[player->inventory_size++] = element;
11605 DrawGameValue_Dynamite(local_player->inventory_size);
11607 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11609 player->dynabomb_count++;
11610 player->dynabombs_left++;
11612 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11614 player->dynabomb_size++;
11616 else if (element == EL_DYNABOMB_INCREASE_POWER)
11618 player->dynabomb_xl = TRUE;
11620 else if (IS_KEY(element))
11622 player->key[KEY_NR(element)] = TRUE;
11624 DrawGameValue_Keys(player->key);
11626 redraw_mask |= REDRAW_DOOR_1;
11628 else if (IS_ENVELOPE(element))
11630 player->show_envelope = element;
11632 else if (element == EL_EMC_LENSES)
11634 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11636 RedrawAllInvisibleElementsForLenses();
11638 else if (element == EL_EMC_MAGNIFIER)
11640 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11642 RedrawAllInvisibleElementsForMagnifier();
11644 else if (IS_DROPPABLE(element) ||
11645 IS_THROWABLE(element)) /* can be collected and dropped */
11649 if (collect_count == 0)
11650 player->inventory_infinite_element = element;
11652 for (i = 0; i < collect_count; i++)
11653 if (player->inventory_size < MAX_INVENTORY_SIZE)
11654 player->inventory_element[player->inventory_size++] = element;
11656 DrawGameValue_Dynamite(local_player->inventory_size);
11658 else if (collect_count > 0)
11660 local_player->gems_still_needed -= collect_count;
11661 if (local_player->gems_still_needed < 0)
11662 local_player->gems_still_needed = 0;
11664 DrawGameValue_Emeralds(local_player->gems_still_needed);
11667 RaiseScoreElement(element);
11668 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11671 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11672 player->index_bit, dig_side);
11674 if (mode == DF_SNAP)
11676 #if USE_NEW_SNAP_DELAY
11677 if (level.block_snap_field)
11678 setFieldForSnapping(x, y, element, move_direction);
11680 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11682 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11685 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11686 player->index_bit, dig_side);
11689 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11691 if (mode == DF_SNAP && element != EL_BD_ROCK)
11692 return MP_NO_ACTION;
11694 if (CAN_FALL(element) && dy)
11695 return MP_NO_ACTION;
11697 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11698 !(element == EL_SPRING && level.use_spring_bug))
11699 return MP_NO_ACTION;
11701 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11702 ((move_direction & MV_VERTICAL &&
11703 ((element_info[element].move_pattern & MV_LEFT &&
11704 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11705 (element_info[element].move_pattern & MV_RIGHT &&
11706 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11707 (move_direction & MV_HORIZONTAL &&
11708 ((element_info[element].move_pattern & MV_UP &&
11709 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11710 (element_info[element].move_pattern & MV_DOWN &&
11711 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11712 return MP_NO_ACTION;
11714 /* do not push elements already moving away faster than player */
11715 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11716 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11717 return MP_NO_ACTION;
11719 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11721 if (player->push_delay_value == -1 || !player_was_pushing)
11722 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11724 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11726 if (player->push_delay_value == -1)
11727 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11729 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11731 if (!player->is_pushing)
11732 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11735 player->is_pushing = TRUE;
11737 if (!(IN_LEV_FIELD(nextx, nexty) &&
11738 (IS_FREE(nextx, nexty) ||
11739 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11740 IS_SB_ELEMENT(element)))))
11741 return MP_NO_ACTION;
11743 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11744 return MP_NO_ACTION;
11746 if (player->push_delay == -1) /* new pushing; restart delay */
11747 player->push_delay = 0;
11749 if (player->push_delay < player->push_delay_value &&
11750 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11751 element != EL_SPRING && element != EL_BALLOON)
11753 /* make sure that there is no move delay before next try to push */
11754 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11755 player->move_delay = 0;
11757 return MP_NO_ACTION;
11760 if (IS_SB_ELEMENT(element))
11762 if (element == EL_SOKOBAN_FIELD_FULL)
11764 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11765 local_player->sokobanfields_still_needed++;
11768 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11770 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11771 local_player->sokobanfields_still_needed--;
11774 Feld[x][y] = EL_SOKOBAN_OBJECT;
11776 if (Back[x][y] == Back[nextx][nexty])
11777 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11778 else if (Back[x][y] != 0)
11779 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11782 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11785 if (local_player->sokobanfields_still_needed == 0 &&
11786 game.emulation == EMU_SOKOBAN)
11788 player->LevelSolved = player->GameOver = TRUE;
11789 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11793 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11795 InitMovingField(x, y, move_direction);
11796 GfxAction[x][y] = ACTION_PUSHING;
11798 if (mode == DF_SNAP)
11799 ContinueMoving(x, y);
11801 MovPos[x][y] = (dx != 0 ? dx : dy);
11803 Pushed[x][y] = TRUE;
11804 Pushed[nextx][nexty] = TRUE;
11806 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11807 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11809 player->push_delay_value = -1; /* get new value later */
11811 /* check for element change _after_ element has been pushed */
11812 if (game.use_change_when_pushing_bug)
11814 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11815 player->index_bit, dig_side);
11816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11817 player->index_bit, dig_side);
11820 else if (IS_SWITCHABLE(element))
11822 if (PLAYER_SWITCHING(player, x, y))
11824 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11825 player->index_bit, dig_side);
11830 player->is_switching = TRUE;
11831 player->switch_x = x;
11832 player->switch_y = y;
11834 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11836 if (element == EL_ROBOT_WHEEL)
11838 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11842 DrawLevelField(x, y);
11844 else if (element == EL_SP_TERMINAL)
11849 SCAN_PLAYFIELD(xx, yy)
11851 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11854 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11856 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11857 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11860 else if (IS_BELT_SWITCH(element))
11862 ToggleBeltSwitch(x, y);
11864 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11865 element == EL_SWITCHGATE_SWITCH_DOWN)
11867 ToggleSwitchgateSwitch(x, y);
11869 else if (element == EL_LIGHT_SWITCH ||
11870 element == EL_LIGHT_SWITCH_ACTIVE)
11872 ToggleLightSwitch(x, y);
11874 else if (element == EL_TIMEGATE_SWITCH)
11876 ActivateTimegateSwitch(x, y);
11878 else if (element == EL_BALLOON_SWITCH_LEFT ||
11879 element == EL_BALLOON_SWITCH_RIGHT ||
11880 element == EL_BALLOON_SWITCH_UP ||
11881 element == EL_BALLOON_SWITCH_DOWN ||
11882 element == EL_BALLOON_SWITCH_NONE ||
11883 element == EL_BALLOON_SWITCH_ANY)
11885 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11886 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11887 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11888 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11889 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11892 else if (element == EL_LAMP)
11894 Feld[x][y] = EL_LAMP_ACTIVE;
11895 local_player->lights_still_needed--;
11897 ResetGfxAnimation(x, y);
11898 DrawLevelField(x, y);
11900 else if (element == EL_TIME_ORB_FULL)
11902 Feld[x][y] = EL_TIME_ORB_EMPTY;
11904 if (level.time > 0 || level.use_time_orb_bug)
11906 TimeLeft += level.time_orb_time;
11907 DrawGameValue_Time(TimeLeft);
11910 ResetGfxAnimation(x, y);
11911 DrawLevelField(x, y);
11913 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11914 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11918 game.ball_state = !game.ball_state;
11921 SCAN_PLAYFIELD(xx, yy)
11923 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11926 int e = Feld[xx][yy];
11928 if (game.ball_state)
11930 if (e == EL_EMC_MAGIC_BALL)
11931 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11932 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11933 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11937 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11938 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11939 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11940 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11945 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11946 player->index_bit, dig_side);
11948 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11949 player->index_bit, dig_side);
11951 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11952 player->index_bit, dig_side);
11958 if (!PLAYER_SWITCHING(player, x, y))
11960 player->is_switching = TRUE;
11961 player->switch_x = x;
11962 player->switch_y = y;
11964 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11965 player->index_bit, dig_side);
11966 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11967 player->index_bit, dig_side);
11969 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11970 player->index_bit, dig_side);
11971 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11972 player->index_bit, dig_side);
11975 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11976 player->index_bit, dig_side);
11977 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11978 player->index_bit, dig_side);
11980 return MP_NO_ACTION;
11983 player->push_delay = -1;
11985 if (is_player) /* function can also be called by EL_PENGUIN */
11987 if (Feld[x][y] != element) /* really digged/collected something */
11988 player->is_collecting = !player->is_digging;
11994 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11996 int jx = player->jx, jy = player->jy;
11997 int x = jx + dx, y = jy + dy;
11998 int snap_direction = (dx == -1 ? MV_LEFT :
11999 dx == +1 ? MV_RIGHT :
12001 dy == +1 ? MV_DOWN : MV_NONE);
12002 boolean can_continue_snapping = (level.continuous_snapping &&
12003 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12005 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12008 if (!player->active || !IN_LEV_FIELD(x, y))
12016 if (player->MovPos == 0)
12017 player->is_pushing = FALSE;
12019 player->is_snapping = FALSE;
12021 if (player->MovPos == 0)
12023 player->is_moving = FALSE;
12024 player->is_digging = FALSE;
12025 player->is_collecting = FALSE;
12031 #if USE_NEW_CONTINUOUS_SNAPPING
12032 /* prevent snapping with already pressed snap key when not allowed */
12033 if (player->is_snapping && !can_continue_snapping)
12036 if (player->is_snapping)
12040 player->MovDir = snap_direction;
12042 if (player->MovPos == 0)
12044 player->is_moving = FALSE;
12045 player->is_digging = FALSE;
12046 player->is_collecting = FALSE;
12049 player->is_dropping = FALSE;
12050 player->is_dropping_pressed = FALSE;
12051 player->drop_pressed_delay = 0;
12053 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12056 player->is_snapping = TRUE;
12058 if (player->MovPos == 0)
12060 player->is_moving = FALSE;
12061 player->is_digging = FALSE;
12062 player->is_collecting = FALSE;
12065 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12066 DrawLevelField(player->last_jx, player->last_jy);
12068 DrawLevelField(x, y);
12073 boolean DropElement(struct PlayerInfo *player)
12075 int old_element, new_element;
12076 int dropx = player->jx, dropy = player->jy;
12077 int drop_direction = player->MovDir;
12078 int drop_side = drop_direction;
12079 int drop_element = (player->inventory_size > 0 ?
12080 player->inventory_element[player->inventory_size - 1] :
12081 player->inventory_infinite_element != EL_UNDEFINED ?
12082 player->inventory_infinite_element :
12083 player->dynabombs_left > 0 ?
12084 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12087 player->is_dropping_pressed = TRUE;
12089 /* do not drop an element on top of another element; when holding drop key
12090 pressed without moving, dropped element must move away before the next
12091 element can be dropped (this is especially important if the next element
12092 is dynamite, which can be placed on background for historical reasons) */
12093 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12096 if (IS_THROWABLE(drop_element))
12098 dropx += GET_DX_FROM_DIR(drop_direction);
12099 dropy += GET_DY_FROM_DIR(drop_direction);
12101 if (!IN_LEV_FIELD(dropx, dropy))
12105 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12106 new_element = drop_element; /* default: no change when dropping */
12108 /* check if player is active, not moving and ready to drop */
12109 if (!player->active || player->MovPos || player->drop_delay > 0)
12112 /* check if player has anything that can be dropped */
12113 if (new_element == EL_UNDEFINED)
12116 /* check if drop key was pressed long enough for EM style dynamite */
12117 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12120 /* check if anything can be dropped at the current position */
12121 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12124 /* collected custom elements can only be dropped on empty fields */
12125 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12128 if (old_element != EL_EMPTY)
12129 Back[dropx][dropy] = old_element; /* store old element on this field */
12131 ResetGfxAnimation(dropx, dropy);
12132 ResetRandomAnimationValue(dropx, dropy);
12134 if (player->inventory_size > 0 ||
12135 player->inventory_infinite_element != EL_UNDEFINED)
12137 if (player->inventory_size > 0)
12139 player->inventory_size--;
12141 DrawGameValue_Dynamite(local_player->inventory_size);
12143 if (new_element == EL_DYNAMITE)
12144 new_element = EL_DYNAMITE_ACTIVE;
12145 else if (new_element == EL_EM_DYNAMITE)
12146 new_element = EL_EM_DYNAMITE_ACTIVE;
12147 else if (new_element == EL_SP_DISK_RED)
12148 new_element = EL_SP_DISK_RED_ACTIVE;
12151 Feld[dropx][dropy] = new_element;
12153 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12154 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12155 el2img(Feld[dropx][dropy]), 0);
12157 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12159 /* needed if previous element just changed to "empty" in the last frame */
12160 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12162 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12163 player->index_bit, drop_side);
12164 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12166 player->index_bit, drop_side);
12168 TestIfElementTouchesCustomElement(dropx, dropy);
12170 else /* player is dropping a dyna bomb */
12172 player->dynabombs_left--;
12174 Feld[dropx][dropy] = new_element;
12176 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12177 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12178 el2img(Feld[dropx][dropy]), 0);
12180 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12183 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12184 InitField_WithBug1(dropx, dropy, FALSE);
12186 new_element = Feld[dropx][dropy]; /* element might have changed */
12188 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12189 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12191 int move_direction, nextx, nexty;
12193 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12194 MovDir[dropx][dropy] = drop_direction;
12196 move_direction = MovDir[dropx][dropy];
12197 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12198 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12200 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12201 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12204 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12205 player->is_dropping = TRUE;
12207 player->drop_pressed_delay = 0;
12208 player->is_dropping_pressed = FALSE;
12210 player->drop_x = dropx;
12211 player->drop_y = dropy;
12216 /* ------------------------------------------------------------------------- */
12217 /* game sound playing functions */
12218 /* ------------------------------------------------------------------------- */
12220 static int *loop_sound_frame = NULL;
12221 static int *loop_sound_volume = NULL;
12223 void InitPlayLevelSound()
12225 int num_sounds = getSoundListSize();
12227 checked_free(loop_sound_frame);
12228 checked_free(loop_sound_volume);
12230 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12231 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12234 static void PlayLevelSound(int x, int y, int nr)
12236 int sx = SCREENX(x), sy = SCREENY(y);
12237 int volume, stereo_position;
12238 int max_distance = 8;
12239 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12241 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12242 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12245 if (!IN_LEV_FIELD(x, y) ||
12246 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12247 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12250 volume = SOUND_MAX_VOLUME;
12252 if (!IN_SCR_FIELD(sx, sy))
12254 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12255 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12257 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12260 stereo_position = (SOUND_MAX_LEFT +
12261 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12262 (SCR_FIELDX + 2 * max_distance));
12264 if (IS_LOOP_SOUND(nr))
12266 /* This assures that quieter loop sounds do not overwrite louder ones,
12267 while restarting sound volume comparison with each new game frame. */
12269 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12272 loop_sound_volume[nr] = volume;
12273 loop_sound_frame[nr] = FrameCounter;
12276 PlaySoundExt(nr, volume, stereo_position, type);
12279 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12281 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12282 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12283 y < LEVELY(BY1) ? LEVELY(BY1) :
12284 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12288 static void PlayLevelSoundAction(int x, int y, int action)
12290 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12293 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12295 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12297 if (sound_effect != SND_UNDEFINED)
12298 PlayLevelSound(x, y, sound_effect);
12301 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12304 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12306 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12307 PlayLevelSound(x, y, sound_effect);
12310 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12312 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12314 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12315 PlayLevelSound(x, y, sound_effect);
12318 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12320 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12322 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12323 StopSound(sound_effect);
12326 static void PlayLevelMusic()
12328 if (levelset.music[level_nr] != MUS_UNDEFINED)
12329 PlayMusic(levelset.music[level_nr]); /* from config file */
12331 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12334 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12336 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12341 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12345 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12349 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12353 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12357 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12361 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12365 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12368 case SAMPLE_android_clone:
12369 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12372 case SAMPLE_android_move:
12373 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12376 case SAMPLE_spring:
12377 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12381 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12385 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12388 case SAMPLE_eater_eat:
12389 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12393 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12396 case SAMPLE_collect:
12397 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12400 case SAMPLE_diamond:
12401 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12404 case SAMPLE_squash:
12405 /* !!! CHECK THIS !!! */
12407 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12409 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12413 case SAMPLE_wonderfall:
12414 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12418 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12422 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12426 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12430 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12434 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12438 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12441 case SAMPLE_wonder:
12442 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12446 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12449 case SAMPLE_exit_open:
12450 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12453 case SAMPLE_exit_leave:
12454 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12457 case SAMPLE_dynamite:
12458 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12462 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12466 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12470 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12474 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12478 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12482 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12486 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12491 void RaiseScore(int value)
12493 local_player->score += value;
12495 DrawGameValue_Score(local_player->score);
12498 void RaiseScoreElement(int element)
12503 case EL_BD_DIAMOND:
12504 case EL_EMERALD_YELLOW:
12505 case EL_EMERALD_RED:
12506 case EL_EMERALD_PURPLE:
12507 case EL_SP_INFOTRON:
12508 RaiseScore(level.score[SC_EMERALD]);
12511 RaiseScore(level.score[SC_DIAMOND]);
12514 RaiseScore(level.score[SC_CRYSTAL]);
12517 RaiseScore(level.score[SC_PEARL]);
12520 case EL_BD_BUTTERFLY:
12521 case EL_SP_ELECTRON:
12522 RaiseScore(level.score[SC_BUG]);
12525 case EL_BD_FIREFLY:
12526 case EL_SP_SNIKSNAK:
12527 RaiseScore(level.score[SC_SPACESHIP]);
12530 case EL_DARK_YAMYAM:
12531 RaiseScore(level.score[SC_YAMYAM]);
12534 RaiseScore(level.score[SC_ROBOT]);
12537 RaiseScore(level.score[SC_PACMAN]);
12540 RaiseScore(level.score[SC_NUT]);
12543 case EL_EM_DYNAMITE:
12544 case EL_SP_DISK_RED:
12545 case EL_DYNABOMB_INCREASE_NUMBER:
12546 case EL_DYNABOMB_INCREASE_SIZE:
12547 case EL_DYNABOMB_INCREASE_POWER:
12548 RaiseScore(level.score[SC_DYNAMITE]);
12550 case EL_SHIELD_NORMAL:
12551 case EL_SHIELD_DEADLY:
12552 RaiseScore(level.score[SC_SHIELD]);
12554 case EL_EXTRA_TIME:
12555 RaiseScore(level.extra_time_score);
12569 RaiseScore(level.score[SC_KEY]);
12572 RaiseScore(element_info[element].collect_score);
12577 void RequestQuitGame(boolean ask_if_really_quit)
12579 if (AllPlayersGone ||
12580 !ask_if_really_quit ||
12581 level_editor_test_game ||
12582 Request("Do you really want to quit the game ?",
12583 REQ_ASK | REQ_STAY_CLOSED))
12585 #if defined(NETWORK_AVALIABLE)
12586 if (options.network)
12587 SendToServer_StopPlaying();
12591 game_status = GAME_MODE_MAIN;
12597 if (tape.playing && tape.deactivate_display)
12598 TapeDeactivateDisplayOff(TRUE);
12600 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12602 if (tape.playing && tape.deactivate_display)
12603 TapeDeactivateDisplayOn();
12608 /* ---------- new game button stuff ---------------------------------------- */
12610 /* graphic position values for game buttons */
12611 #define GAME_BUTTON_XSIZE 30
12612 #define GAME_BUTTON_YSIZE 30
12613 #define GAME_BUTTON_XPOS 5
12614 #define GAME_BUTTON_YPOS 215
12615 #define SOUND_BUTTON_XPOS 5
12616 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12618 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12619 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12620 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12621 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12622 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12623 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12630 } gamebutton_info[NUM_GAME_BUTTONS] =
12633 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12638 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12639 GAME_CTRL_ID_PAUSE,
12643 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12648 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12649 SOUND_CTRL_ID_MUSIC,
12650 "background music on/off"
12653 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12654 SOUND_CTRL_ID_LOOPS,
12655 "sound loops on/off"
12658 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12659 SOUND_CTRL_ID_SIMPLE,
12660 "normal sounds on/off"
12664 void CreateGameButtons()
12668 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12670 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12671 struct GadgetInfo *gi;
12674 unsigned long event_mask;
12675 int gd_xoffset, gd_yoffset;
12676 int gd_x1, gd_x2, gd_y1, gd_y2;
12679 gd_xoffset = gamebutton_info[i].x;
12680 gd_yoffset = gamebutton_info[i].y;
12681 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12682 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12684 if (id == GAME_CTRL_ID_STOP ||
12685 id == GAME_CTRL_ID_PAUSE ||
12686 id == GAME_CTRL_ID_PLAY)
12688 button_type = GD_TYPE_NORMAL_BUTTON;
12690 event_mask = GD_EVENT_RELEASED;
12691 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12692 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12696 button_type = GD_TYPE_CHECK_BUTTON;
12698 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12699 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12700 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12701 event_mask = GD_EVENT_PRESSED;
12702 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12703 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12706 gi = CreateGadget(GDI_CUSTOM_ID, id,
12707 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12708 GDI_X, DX + gd_xoffset,
12709 GDI_Y, DY + gd_yoffset,
12710 GDI_WIDTH, GAME_BUTTON_XSIZE,
12711 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12712 GDI_TYPE, button_type,
12713 GDI_STATE, GD_BUTTON_UNPRESSED,
12714 GDI_CHECKED, checked,
12715 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12716 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12717 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12718 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12719 GDI_EVENT_MASK, event_mask,
12720 GDI_CALLBACK_ACTION, HandleGameButtons,
12724 Error(ERR_EXIT, "cannot create gadget");
12726 game_gadget[id] = gi;
12730 void FreeGameButtons()
12734 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12735 FreeGadget(game_gadget[i]);
12738 static void MapGameButtons()
12742 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12743 MapGadget(game_gadget[i]);
12746 void UnmapGameButtons()
12750 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12751 UnmapGadget(game_gadget[i]);
12754 static void HandleGameButtons(struct GadgetInfo *gi)
12756 int id = gi->custom_id;
12758 if (game_status != GAME_MODE_PLAYING)
12763 case GAME_CTRL_ID_STOP:
12764 RequestQuitGame(TRUE);
12767 case GAME_CTRL_ID_PAUSE:
12768 if (options.network)
12770 #if defined(NETWORK_AVALIABLE)
12772 SendToServer_ContinuePlaying();
12774 SendToServer_PausePlaying();
12778 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12781 case GAME_CTRL_ID_PLAY:
12784 #if defined(NETWORK_AVALIABLE)
12785 if (options.network)
12786 SendToServer_ContinuePlaying();
12790 tape.pausing = FALSE;
12791 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12796 case SOUND_CTRL_ID_MUSIC:
12797 if (setup.sound_music)
12799 setup.sound_music = FALSE;
12802 else if (audio.music_available)
12804 setup.sound = setup.sound_music = TRUE;
12806 SetAudioMode(setup.sound);
12812 case SOUND_CTRL_ID_LOOPS:
12813 if (setup.sound_loops)
12814 setup.sound_loops = FALSE;
12815 else if (audio.loops_available)
12817 setup.sound = setup.sound_loops = TRUE;
12818 SetAudioMode(setup.sound);
12822 case SOUND_CTRL_ID_SIMPLE:
12823 if (setup.sound_simple)
12824 setup.sound_simple = FALSE;
12825 else if (audio.sound_available)
12827 setup.sound = setup.sound_simple = TRUE;
12828 SetAudioMode(setup.sound);