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)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
46 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
53 /* for MovePlayer() */
54 #define MP_NO_ACTION 0
57 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
59 /* for ScrollPlayer() */
61 #define SCROLL_GO_ON 1
63 /* for Bang()/Explode() */
64 #define EX_PHASE_START 0
65 #define EX_TYPE_NONE 0
66 #define EX_TYPE_NORMAL (1 << 0)
67 #define EX_TYPE_CENTER (1 << 1)
68 #define EX_TYPE_BORDER (1 << 2)
69 #define EX_TYPE_CROSS (1 << 3)
70 #define EX_TYPE_DYNA (1 << 4)
71 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
73 /* special positions in the game control window (relative to control window) */
76 #define XX_EMERALDS 29
77 #define YY_EMERALDS 54
78 #define XX_DYNAMITE 29
79 #define YY_DYNAMITE 89
88 /* special positions in the game control window (relative to main window) */
89 #define DX_LEVEL (DX + XX_LEVEL)
90 #define DY_LEVEL (DY + YY_LEVEL)
91 #define DX_EMERALDS (DX + XX_EMERALDS)
92 #define DY_EMERALDS (DY + YY_EMERALDS)
93 #define DX_DYNAMITE (DX + XX_DYNAMITE)
94 #define DY_DYNAMITE (DY + YY_DYNAMITE)
95 #define DX_KEYS (DX + XX_KEYS)
96 #define DY_KEYS (DY + YY_KEYS)
97 #define DX_SCORE (DX + XX_SCORE)
98 #define DY_SCORE (DY + YY_SCORE)
99 #define DX_TIME1 (DX + XX_TIME1)
100 #define DX_TIME2 (DX + XX_TIME2)
101 #define DY_TIME (DY + YY_TIME)
103 /* values for delayed check of falling and moving elements and for collision */
104 #define CHECK_DELAY_MOVING 3
105 #define CHECK_DELAY_FALLING 3
106 #define CHECK_DELAY_COLLISION 2
108 /* values for initial player move delay (initial delay counter value) */
109 #define INITIAL_MOVE_DELAY_OFF -1
110 #define INITIAL_MOVE_DELAY_ON 0
112 /* values for player movement speed (which is in fact a delay value) */
113 #define MOVE_DELAY_MIN_SPEED 32
114 #define MOVE_DELAY_NORMAL_SPEED 8
115 #define MOVE_DELAY_HIGH_SPEED 4
116 #define MOVE_DELAY_MAX_SPEED 1
119 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
120 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
122 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
123 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
125 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
126 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
128 /* values for other actions */
129 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
130 #define MOVE_STEPSIZE_MIN (1)
131 #define MOVE_STEPSIZE_MAX (TILEX)
133 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
134 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
136 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
138 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
139 RND(element_info[e].push_delay_random))
140 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
141 RND(element_info[e].drop_delay_random))
142 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
143 RND(element_info[e].move_delay_random))
144 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
145 (element_info[e].move_delay_random))
146 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
147 RND(element_info[e].ce_value_random_initial))
148 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
149 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
150 RND((c)->delay_random * (c)->delay_frames))
151 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
152 RND((c)->delay_random))
156 #define GET_VALID_RUNTIME_ELEMENT(e) \
157 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
159 #define GET_VALID_FILE_ELEMENT(e) \
160 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
163 #define GET_TARGET_ELEMENT(e, ch, cv, cs) \
164 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
165 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
166 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
167 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
168 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
169 (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
171 #define CAN_GROW_INTO(e) \
172 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
174 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
175 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
184 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
185 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
186 (CAN_MOVE_INTO_ACID(e) && \
187 Feld[x][y] == EL_ACID) || \
190 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
191 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 (CAN_MOVE_INTO_ACID(e) && \
194 Feld[x][y] == EL_ACID) || \
195 (DONT_COLLIDE_WITH(e) && \
197 !PLAYER_ENEMY_PROTECTED(x, y))))
199 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
202 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
205 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
206 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
208 #define ANDROID_CAN_CLONE_FIELD(x, y) \
209 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
210 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
212 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
218 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
221 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
224 #define PIG_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
227 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
229 IS_FOOD_PENGUIN(Feld[x][y])))
230 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
236 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
239 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
240 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
241 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
244 #define GROUP_NR(e) ((e) - EL_GROUP_START)
245 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
246 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
248 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
249 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
252 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
254 #define CE_ENTER_FIELD_COND(e, x, y) \
255 (!IS_PLAYER(x, y) && \
256 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
258 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
261 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
262 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
264 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
265 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
266 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
267 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
269 /* game button identifiers */
270 #define GAME_CTRL_ID_STOP 0
271 #define GAME_CTRL_ID_PAUSE 1
272 #define GAME_CTRL_ID_PLAY 2
273 #define SOUND_CTRL_ID_MUSIC 3
274 #define SOUND_CTRL_ID_LOOPS 4
275 #define SOUND_CTRL_ID_SIMPLE 5
277 #define NUM_GAME_BUTTONS 6
280 /* forward declaration for internal use */
282 static void CreateField(int, int, int);
284 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
285 static void AdvanceFrameAndPlayerCounters(int);
287 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
288 static boolean MovePlayer(struct PlayerInfo *, int, int);
289 static void ScrollPlayer(struct PlayerInfo *, int);
290 static void ScrollScreen(struct PlayerInfo *, int);
292 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
294 static void InitBeltMovement(void);
295 static void CloseAllOpenTimegates(void);
296 static void CheckGravityMovement(struct PlayerInfo *);
297 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
298 static void KillPlayerUnlessEnemyProtected(int, int);
299 static void KillPlayerUnlessExplosionProtected(int, int);
301 static void TestIfPlayerTouchesCustomElement(int, int);
302 static void TestIfElementTouchesCustomElement(int, int);
303 static void TestIfElementHitsCustomElement(int, int, int);
305 static void TestIfElementSmashesCustomElement(int, int, int);
308 static void HandleElementChange(int, int, int);
309 static void ExecuteCustomElementAction(int, int, int, int);
310 static boolean ChangeElement(int, int, int, int);
312 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
313 #define CheckTriggeredElementChange(x, y, e, ev) \
314 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
315 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
316 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
317 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
318 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
319 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
320 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
322 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
323 #define CheckElementChange(x, y, e, te, ev) \
324 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
325 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
326 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
327 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
328 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
330 static void PlayLevelSound(int, int, int);
331 static void PlayLevelSoundNearest(int, int, int);
332 static void PlayLevelSoundAction(int, int, int);
333 static void PlayLevelSoundElementAction(int, int, int, int);
334 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
335 static void PlayLevelSoundActionIfLoop(int, int, int);
336 static void StopLevelSoundActionIfLoop(int, int, int);
337 static void PlayLevelMusic();
339 static void MapGameButtons();
340 static void HandleGameButtons(struct GadgetInfo *);
342 int AmoebeNachbarNr(int, int);
343 void AmoebeUmwandeln(int, int);
344 void ContinueMoving(int, int);
346 void InitMovDir(int, int);
347 void InitAmoebaNr(int, int);
348 int NewHiScore(void);
350 void TestIfGoodThingHitsBadThing(int, int, int);
351 void TestIfBadThingHitsGoodThing(int, int, int);
352 void TestIfPlayerTouchesBadThing(int, int);
353 void TestIfPlayerRunsIntoBadThing(int, int, int);
354 void TestIfBadThingTouchesPlayer(int, int);
355 void TestIfBadThingRunsIntoPlayer(int, int, int);
356 void TestIfFriendTouchesBadThing(int, int);
357 void TestIfBadThingTouchesFriend(int, int);
358 void TestIfBadThingTouchesOtherBadThing(int, int);
360 void KillPlayer(struct PlayerInfo *);
361 void BuryPlayer(struct PlayerInfo *);
362 void RemovePlayer(struct PlayerInfo *);
364 boolean SnapField(struct PlayerInfo *, int, int);
365 boolean DropElement(struct PlayerInfo *);
367 static int getInvisibleActiveFromInvisibleElement(int);
368 static int getInvisibleFromInvisibleActiveElement(int);
370 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
373 /* ------------------------------------------------------------------------- */
374 /* definition of elements that automatically change to other elements after */
375 /* a specified time, eventually calling a function when changing */
376 /* ------------------------------------------------------------------------- */
378 /* forward declaration for changer functions */
379 static void InitBuggyBase(int, int);
380 static void WarnBuggyBase(int, int);
382 static void InitTrap(int, int);
383 static void ActivateTrap(int, int);
384 static void ChangeActiveTrap(int, int);
386 static void InitRobotWheel(int, int);
387 static void RunRobotWheel(int, int);
388 static void StopRobotWheel(int, int);
390 static void InitTimegateWheel(int, int);
391 static void RunTimegateWheel(int, int);
393 static void InitMagicBallDelay(int, int);
394 static void ActivateMagicBall(int, int);
396 static void InitDiagonalMovingElement(int, int);
398 struct ChangingElementInfo
403 void (*pre_change_function)(int x, int y);
404 void (*change_function)(int x, int y);
405 void (*post_change_function)(int x, int y);
408 static struct ChangingElementInfo change_delay_list[] =
459 EL_SWITCHGATE_OPENING,
467 EL_SWITCHGATE_CLOSING,
468 EL_SWITCHGATE_CLOSED,
500 EL_ACID_SPLASH_RIGHT,
509 EL_SP_BUGGY_BASE_ACTIVATING,
516 EL_SP_BUGGY_BASE_ACTIVATING,
517 EL_SP_BUGGY_BASE_ACTIVE,
524 EL_SP_BUGGY_BASE_ACTIVE,
548 EL_ROBOT_WHEEL_ACTIVE,
556 EL_TIMEGATE_SWITCH_ACTIVE,
564 EL_EMC_MAGIC_BALL_ACTIVE,
565 EL_EMC_MAGIC_BALL_ACTIVE,
572 EL_EMC_SPRING_BUMPER_ACTIVE,
573 EL_EMC_SPRING_BUMPER,
580 EL_DIAGONAL_SHRINKING,
593 InitDiagonalMovingElement
609 int push_delay_fixed, push_delay_random;
614 { EL_BALLOON, 0, 0 },
616 { EL_SOKOBAN_OBJECT, 2, 0 },
617 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
618 { EL_SATELLITE, 2, 0 },
619 { EL_SP_DISK_YELLOW, 2, 0 },
621 { EL_UNDEFINED, 0, 0 },
629 move_stepsize_list[] =
631 { EL_AMOEBA_DROP, 2 },
632 { EL_AMOEBA_DROPPING, 2 },
633 { EL_QUICKSAND_FILLING, 1 },
634 { EL_QUICKSAND_EMPTYING, 1 },
635 { EL_MAGIC_WALL_FILLING, 2 },
636 { EL_BD_MAGIC_WALL_FILLING, 2 },
637 { EL_MAGIC_WALL_EMPTYING, 2 },
638 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
648 collect_count_list[] =
651 { EL_BD_DIAMOND, 1 },
652 { EL_EMERALD_YELLOW, 1 },
653 { EL_EMERALD_RED, 1 },
654 { EL_EMERALD_PURPLE, 1 },
656 { EL_SP_INFOTRON, 1 },
668 access_direction_list[] =
670 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
671 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
672 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
673 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
674 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
675 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
676 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
677 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
678 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
679 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
680 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
682 { EL_SP_PORT_LEFT, MV_RIGHT },
683 { EL_SP_PORT_RIGHT, MV_LEFT },
684 { EL_SP_PORT_UP, MV_DOWN },
685 { EL_SP_PORT_DOWN, MV_UP },
686 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
687 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
688 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
689 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
690 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
691 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
692 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
693 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
694 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
695 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
696 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
697 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
698 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
699 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
700 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
702 { EL_UNDEFINED, MV_NONE }
705 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
707 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
708 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
709 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
710 IS_JUST_CHANGING(x, y))
712 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
714 /* static variables for playfield scan mode (scanning forward or backward) */
715 static int playfield_scan_start_x = 0;
716 static int playfield_scan_start_y = 0;
717 static int playfield_scan_delta_x = 1;
718 static int playfield_scan_delta_y = 1;
720 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
721 (y) >= 0 && (y) <= lev_fieldy - 1; \
722 (y) += playfield_scan_delta_y) \
723 for ((x) = playfield_scan_start_x; \
724 (x) >= 0 && (x) <= lev_fieldx - 1; \
725 (x) += playfield_scan_delta_x) \
728 void DEBUG_SetMaximumDynamite()
732 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
733 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
734 local_player->inventory_element[local_player->inventory_size++] =
739 static void InitPlayfieldScanModeVars()
741 if (game.use_reverse_scan_direction)
743 playfield_scan_start_x = lev_fieldx - 1;
744 playfield_scan_start_y = lev_fieldy - 1;
746 playfield_scan_delta_x = -1;
747 playfield_scan_delta_y = -1;
751 playfield_scan_start_x = 0;
752 playfield_scan_start_y = 0;
754 playfield_scan_delta_x = 1;
755 playfield_scan_delta_y = 1;
759 static void InitPlayfieldScanMode(int mode)
761 game.use_reverse_scan_direction =
762 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
764 InitPlayfieldScanModeVars();
767 static int get_move_delay_from_stepsize(int move_stepsize)
770 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
772 /* make sure that stepsize value is always a power of 2 */
773 move_stepsize = (1 << log_2(move_stepsize));
775 return TILEX / move_stepsize;
778 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
781 int move_delay = get_move_delay_from_stepsize(move_stepsize);
782 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
784 /* do no immediately change move delay -- the player might just be moving */
785 player->move_delay_value_next = move_delay;
787 /* information if player can move must be set separately */
788 player->cannot_move = cannot_move;
792 player->move_delay = game.initial_move_delay;
793 player->move_delay_value = game.initial_move_delay_value;
795 player->move_delay_value_next = -1;
797 player->move_delay_reset_counter = 0;
801 void GetPlayerConfig()
803 if (!audio.sound_available)
804 setup.sound_simple = FALSE;
806 if (!audio.loops_available)
807 setup.sound_loops = FALSE;
809 if (!audio.music_available)
810 setup.sound_music = FALSE;
812 if (!video.fullscreen_available)
813 setup.fullscreen = FALSE;
815 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
817 SetAudioMode(setup.sound);
821 static int getBeltNrFromBeltElement(int element)
823 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
824 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
825 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
828 static int getBeltNrFromBeltActiveElement(int element)
830 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
831 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
832 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
835 static int getBeltNrFromBeltSwitchElement(int element)
837 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
838 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
839 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
842 static int getBeltDirNrFromBeltSwitchElement(int element)
844 static int belt_base_element[4] =
846 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
847 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
848 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
849 EL_CONVEYOR_BELT_4_SWITCH_LEFT
852 int belt_nr = getBeltNrFromBeltSwitchElement(element);
853 int belt_dir_nr = element - belt_base_element[belt_nr];
855 return (belt_dir_nr % 3);
858 static int getBeltDirFromBeltSwitchElement(int element)
860 static int belt_move_dir[3] =
867 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
869 return belt_move_dir[belt_dir_nr];
872 static int get_element_from_group_element(int element)
874 if (IS_GROUP_ELEMENT(element))
876 struct ElementGroupInfo *group = element_info[element].group;
877 int last_anim_random_frame = gfx.anim_random_frame;
880 if (group->choice_mode == ANIM_RANDOM)
881 gfx.anim_random_frame = RND(group->num_elements_resolved);
883 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
884 group->choice_mode, 0,
887 if (group->choice_mode == ANIM_RANDOM)
888 gfx.anim_random_frame = last_anim_random_frame;
892 element = group->element_resolved[element_pos];
898 static void InitPlayerField(int x, int y, int element, boolean init_game)
900 if (element == EL_SP_MURPHY)
904 if (stored_player[0].present)
906 Feld[x][y] = EL_SP_MURPHY_CLONE;
912 stored_player[0].use_murphy = TRUE;
914 if (!level.use_artwork_element[0])
915 stored_player[0].artwork_element = EL_SP_MURPHY;
918 Feld[x][y] = EL_PLAYER_1;
924 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
925 int jx = player->jx, jy = player->jy;
927 player->present = TRUE;
929 player->block_last_field = (element == EL_SP_MURPHY ?
930 level.sp_block_last_field :
931 level.block_last_field);
933 /* ---------- initialize player's last field block delay --------------- */
935 /* always start with reliable default value (no adjustment needed) */
936 player->block_delay_adjustment = 0;
938 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
939 if (player->block_last_field && element == EL_SP_MURPHY)
940 player->block_delay_adjustment = 1;
942 /* special case 2: in game engines before 3.1.1, blocking was different */
943 if (game.use_block_last_field_bug)
944 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
946 if (!options.network || player->connected)
948 player->active = TRUE;
950 /* remove potentially duplicate players */
951 if (StorePlayer[jx][jy] == Feld[x][y])
952 StorePlayer[jx][jy] = 0;
954 StorePlayer[x][y] = Feld[x][y];
958 printf("Player %d activated.\n", player->element_nr);
959 printf("[Local player is %d and currently %s.]\n",
960 local_player->element_nr,
961 local_player->active ? "active" : "not active");
965 Feld[x][y] = EL_EMPTY;
967 player->jx = player->last_jx = x;
968 player->jy = player->last_jy = y;
972 static void InitField(int x, int y, boolean init_game)
974 int element = Feld[x][y];
983 InitPlayerField(x, y, element, init_game);
986 case EL_SOKOBAN_FIELD_PLAYER:
987 element = Feld[x][y] = EL_PLAYER_1;
988 InitField(x, y, init_game);
990 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
991 InitField(x, y, init_game);
994 case EL_SOKOBAN_FIELD_EMPTY:
995 local_player->sokobanfields_still_needed++;
999 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1000 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1001 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1002 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1003 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1004 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1005 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1006 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1007 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1008 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1017 case EL_SPACESHIP_RIGHT:
1018 case EL_SPACESHIP_UP:
1019 case EL_SPACESHIP_LEFT:
1020 case EL_SPACESHIP_DOWN:
1021 case EL_BD_BUTTERFLY:
1022 case EL_BD_BUTTERFLY_RIGHT:
1023 case EL_BD_BUTTERFLY_UP:
1024 case EL_BD_BUTTERFLY_LEFT:
1025 case EL_BD_BUTTERFLY_DOWN:
1027 case EL_BD_FIREFLY_RIGHT:
1028 case EL_BD_FIREFLY_UP:
1029 case EL_BD_FIREFLY_LEFT:
1030 case EL_BD_FIREFLY_DOWN:
1031 case EL_PACMAN_RIGHT:
1033 case EL_PACMAN_LEFT:
1034 case EL_PACMAN_DOWN:
1036 case EL_YAMYAM_LEFT:
1037 case EL_YAMYAM_RIGHT:
1039 case EL_YAMYAM_DOWN:
1040 case EL_DARK_YAMYAM:
1043 case EL_SP_SNIKSNAK:
1044 case EL_SP_ELECTRON:
1053 case EL_AMOEBA_FULL:
1058 case EL_AMOEBA_DROP:
1059 if (y == lev_fieldy - 1)
1061 Feld[x][y] = EL_AMOEBA_GROWING;
1062 Store[x][y] = EL_AMOEBA_WET;
1066 case EL_DYNAMITE_ACTIVE:
1067 case EL_SP_DISK_RED_ACTIVE:
1068 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1069 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1070 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1071 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1072 MovDelay[x][y] = 96;
1075 case EL_EM_DYNAMITE_ACTIVE:
1076 MovDelay[x][y] = 32;
1080 local_player->lights_still_needed++;
1084 local_player->friends_still_needed++;
1089 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1092 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1093 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1094 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1095 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1096 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1097 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1098 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1099 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1100 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1101 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1102 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1103 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1106 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1107 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1108 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1110 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1112 game.belt_dir[belt_nr] = belt_dir;
1113 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1115 else /* more than one switch -- set it like the first switch */
1117 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1122 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1124 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1127 case EL_LIGHT_SWITCH_ACTIVE:
1129 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1132 case EL_INVISIBLE_STEELWALL:
1133 case EL_INVISIBLE_WALL:
1134 case EL_INVISIBLE_SAND:
1135 if (game.light_time_left > 0 ||
1136 game.lenses_time_left > 0)
1137 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1140 case EL_EMC_MAGIC_BALL:
1141 if (game.ball_state)
1142 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1145 case EL_EMC_MAGIC_BALL_SWITCH:
1146 if (game.ball_state)
1147 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1152 if (IS_CUSTOM_ELEMENT(element))
1154 if (CAN_MOVE(element))
1157 #if USE_NEW_CUSTOM_VALUE
1158 if (!element_info[element].use_last_ce_value || init_game)
1159 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1163 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1166 else if (IS_GROUP_ELEMENT(element))
1169 Feld[x][y] = get_element_from_group_element(element);
1171 InitField(x, y, init_game);
1173 struct ElementGroupInfo *group = element_info[element].group;
1174 int last_anim_random_frame = gfx.anim_random_frame;
1177 if (group->choice_mode == ANIM_RANDOM)
1178 gfx.anim_random_frame = RND(group->num_elements_resolved);
1180 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1181 group->choice_mode, 0,
1184 if (group->choice_mode == ANIM_RANDOM)
1185 gfx.anim_random_frame = last_anim_random_frame;
1187 group->choice_pos++;
1189 Feld[x][y] = group->element_resolved[element_pos];
1191 InitField(x, y, init_game);
1199 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1204 #if USE_NEW_CUSTOM_VALUE
1207 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1209 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1217 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1219 InitField(x, y, init_game);
1221 /* not needed to call InitMovDir() -- already done by InitField()! */
1222 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1223 CAN_MOVE(Feld[x][y]))
1227 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1229 int old_element = Feld[x][y];
1231 InitField(x, y, init_game);
1233 /* not needed to call InitMovDir() -- already done by InitField()! */
1234 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1235 CAN_MOVE(old_element) &&
1236 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1239 /* this case is in fact a combination of not less than three bugs:
1240 first, it calls InitMovDir() for elements that can move, although this is
1241 already done by InitField(); then, it checks the element that was at this
1242 field _before_ the call to InitField() (which can change it); lastly, it
1243 was not called for "mole with direction" elements, which were treated as
1244 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1248 inline void DrawGameValue_Emeralds(int value)
1250 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1252 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1255 inline void DrawGameValue_Dynamite(int value)
1257 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1259 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1262 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1264 int base_key_graphic = EL_KEY_1;
1267 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1268 base_key_graphic = EL_EM_KEY_1;
1270 /* currently only 4 of 8 possible keys are displayed */
1271 for (i = 0; i < STD_NUM_KEYS; i++)
1274 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1275 el2edimg(base_key_graphic + i));
1277 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1278 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1279 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1283 inline void DrawGameValue_Score(int value)
1285 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1287 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1290 inline void DrawGameValue_Time(int value)
1292 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1293 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1295 /* clear background if value just changed its size */
1296 if (value == 999 || value == 1000)
1297 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1300 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1302 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1305 inline void DrawGameValue_Level(int value)
1308 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1311 /* misuse area for displaying emeralds to draw bigger level number */
1312 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1313 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1315 /* now copy it to the area for displaying level number */
1316 BlitBitmap(drawto, drawto,
1317 DX_EMERALDS, DY_EMERALDS + 1,
1318 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1319 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1320 DX_LEVEL - 1, DY_LEVEL + 1);
1322 /* restore the area for displaying emeralds */
1323 DrawGameValue_Emeralds(local_player->gems_still_needed);
1325 /* yes, this is all really ugly :-) */
1329 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1332 int key[MAX_NUM_KEYS];
1335 for (i = 0; i < MAX_NUM_KEYS; i++)
1336 key[i] = key_bits & (1 << i);
1338 DrawGameValue_Level(level_nr);
1340 DrawGameValue_Emeralds(emeralds);
1341 DrawGameValue_Dynamite(dynamite);
1342 DrawGameValue_Score(score);
1343 DrawGameValue_Time(time);
1345 DrawGameValue_Keys(key);
1348 void DrawGameDoorValues()
1350 int dynamite_state = 0;
1354 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1356 DrawGameDoorValues_EM();
1362 DrawGameValue_Level(level_nr);
1364 DrawGameValue_Emeralds(local_player->gems_still_needed);
1365 DrawGameValue_Dynamite(local_player->inventory_size);
1366 DrawGameValue_Score(local_player->score);
1367 DrawGameValue_Time(TimeLeft);
1371 if (game.centered_player_nr == -1)
1373 for (i = 0; i < MAX_PLAYERS; i++)
1375 for (j = 0; j < MAX_NUM_KEYS; j++)
1376 if (stored_player[i].key[j])
1377 key_bits |= (1 << j);
1379 dynamite_state += stored_player[i].inventory_size;
1383 DrawGameValue_Keys(stored_player[i].key);
1388 int player_nr = game.centered_player_nr;
1390 for (i = 0; i < MAX_NUM_KEYS; i++)
1391 if (stored_player[player_nr].key[i])
1392 key_bits |= (1 << i);
1394 dynamite_state = stored_player[player_nr].inventory_size;
1397 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1398 local_player->score, TimeLeft, key_bits);
1403 static void resolve_group_element(int group_element, int recursion_depth)
1405 static int group_nr;
1406 static struct ElementGroupInfo *group;
1407 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1410 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1412 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1413 group_element - EL_GROUP_START + 1);
1415 /* replace element which caused too deep recursion by question mark */
1416 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1421 if (recursion_depth == 0) /* initialization */
1423 group = element_info[group_element].group;
1424 group_nr = group_element - EL_GROUP_START;
1426 group->num_elements_resolved = 0;
1427 group->choice_pos = 0;
1430 for (i = 0; i < actual_group->num_elements; i++)
1432 int element = actual_group->element[i];
1434 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1437 if (IS_GROUP_ELEMENT(element))
1438 resolve_group_element(element, recursion_depth + 1);
1441 group->element_resolved[group->num_elements_resolved++] = element;
1442 element_info[element].in_group[group_nr] = TRUE;
1449 =============================================================================
1451 -----------------------------------------------------------------------------
1452 initialize game engine due to level / tape version number
1453 =============================================================================
1456 static void InitGameEngine()
1458 int i, j, k, l, x, y;
1460 /* set game engine from tape file when re-playing, else from level file */
1461 game.engine_version = (tape.playing ? tape.engine_version :
1462 level.game_version);
1464 /* ---------------------------------------------------------------------- */
1465 /* set flags for bugs and changes according to active game engine version */
1466 /* ---------------------------------------------------------------------- */
1469 Summary of bugfix/change:
1470 Fixed handling for custom elements that change when pushed by the player.
1472 Fixed/changed in version:
1476 Before 3.1.0, custom elements that "change when pushing" changed directly
1477 after the player started pushing them (until then handled in "DigField()").
1478 Since 3.1.0, these custom elements are not changed until the "pushing"
1479 move of the element is finished (now handled in "ContinueMoving()").
1481 Affected levels/tapes:
1482 The first condition is generally needed for all levels/tapes before version
1483 3.1.0, which might use the old behaviour before it was changed; known tapes
1484 that are affected are some tapes from the level set "Walpurgis Gardens" by
1486 The second condition is an exception from the above case and is needed for
1487 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1488 above (including some development versions of 3.1.0), but before it was
1489 known that this change would break tapes like the above and was fixed in
1490 3.1.1, so that the changed behaviour was active although the engine version
1491 while recording maybe was before 3.1.0. There is at least one tape that is
1492 affected by this exception, which is the tape for the one-level set "Bug
1493 Machine" by Juergen Bonhagen.
1496 game.use_change_when_pushing_bug =
1497 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1499 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1500 tape.game_version < VERSION_IDENT(3,1,1,0)));
1503 Summary of bugfix/change:
1504 Fixed handling for blocking the field the player leaves when moving.
1506 Fixed/changed in version:
1510 Before 3.1.1, when "block last field when moving" was enabled, the field
1511 the player is leaving when moving was blocked for the time of the move,
1512 and was directly unblocked afterwards. This resulted in the last field
1513 being blocked for exactly one less than the number of frames of one player
1514 move. Additionally, even when blocking was disabled, the last field was
1515 blocked for exactly one frame.
1516 Since 3.1.1, due to changes in player movement handling, the last field
1517 is not blocked at all when blocking is disabled. When blocking is enabled,
1518 the last field is blocked for exactly the number of frames of one player
1519 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1520 last field is blocked for exactly one more than the number of frames of
1523 Affected levels/tapes:
1524 (!!! yet to be determined -- probably many !!!)
1527 game.use_block_last_field_bug =
1528 (game.engine_version < VERSION_IDENT(3,1,1,0));
1531 Summary of bugfix/change:
1532 Changed behaviour of CE changes with multiple changes per single frame.
1534 Fixed/changed in version:
1538 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1539 This resulted in race conditions where CEs seem to behave strange in some
1540 situations (where triggered CE changes were just skipped because there was
1541 already a CE change on that tile in the playfield in that engine frame).
1542 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1543 (The number of changes per frame must be limited in any case, because else
1544 it is easily possible to define CE changes that would result in an infinite
1545 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1546 should be set large enough so that it would only be reached in cases where
1547 the corresponding CE change conditions run into a loop. Therefore, it seems
1548 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1549 maximal number of change pages for custom elements.)
1551 Affected levels/tapes:
1555 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1556 game.max_num_changes_per_frame = 1;
1558 game.max_num_changes_per_frame =
1559 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1562 /* ---------------------------------------------------------------------- */
1564 /* default scan direction: scan playfield from top/left to bottom/right */
1565 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1567 /* dynamically adjust element properties according to game engine version */
1568 InitElementPropertiesEngine(game.engine_version);
1571 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1572 printf(" tape version == %06d [%s] [file: %06d]\n",
1573 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1575 printf(" => game.engine_version == %06d\n", game.engine_version);
1579 /* ---------- recursively resolve group elements ------------------------- */
1581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1582 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1583 element_info[i].in_group[j] = FALSE;
1585 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1586 resolve_group_element(EL_GROUP_START + i, 0);
1589 /* ---------- initialize player's initial move delay --------------------- */
1592 /* dynamically adjust player properties according to level information */
1593 game.initial_move_delay_value =
1594 get_move_delay_from_stepsize(level.initial_player_stepsize);
1596 /* dynamically adjust player properties according to level information */
1597 game.initial_move_delay_value =
1598 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1601 /* dynamically adjust player properties according to game engine version */
1602 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1603 game.initial_move_delay_value : 0);
1605 /* ---------- initialize player's initial push delay --------------------- */
1607 /* dynamically adjust player properties according to game engine version */
1608 game.initial_push_delay_value =
1609 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1611 /* ---------- initialize changing elements ------------------------------- */
1613 /* initialize changing elements information */
1614 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1616 struct ElementInfo *ei = &element_info[i];
1618 /* this pointer might have been changed in the level editor */
1619 ei->change = &ei->change_page[0];
1621 if (!IS_CUSTOM_ELEMENT(i))
1623 ei->change->target_element = EL_EMPTY_SPACE;
1624 ei->change->delay_fixed = 0;
1625 ei->change->delay_random = 0;
1626 ei->change->delay_frames = 1;
1629 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1631 ei->has_change_event[j] = FALSE;
1633 ei->event_page_nr[j] = 0;
1634 ei->event_page[j] = &ei->change_page[0];
1638 /* add changing elements from pre-defined list */
1639 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1641 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1642 struct ElementInfo *ei = &element_info[ch_delay->element];
1644 ei->change->target_element = ch_delay->target_element;
1645 ei->change->delay_fixed = ch_delay->change_delay;
1647 ei->change->pre_change_function = ch_delay->pre_change_function;
1648 ei->change->change_function = ch_delay->change_function;
1649 ei->change->post_change_function = ch_delay->post_change_function;
1651 ei->change->can_change = TRUE;
1652 ei->change->can_change_or_has_action = TRUE;
1654 ei->has_change_event[CE_DELAY] = TRUE;
1656 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1657 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1660 /* ---------- initialize internal run-time variables ------------- */
1662 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1664 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1666 for (j = 0; j < ei->num_change_pages; j++)
1668 ei->change_page[j].can_change_or_has_action =
1669 (ei->change_page[j].can_change |
1670 ei->change_page[j].has_action);
1674 /* add change events from custom element configuration */
1675 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1677 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1679 for (j = 0; j < ei->num_change_pages; j++)
1681 if (!ei->change_page[j].can_change_or_has_action)
1684 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1686 /* only add event page for the first page found with this event */
1687 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1689 ei->has_change_event[k] = TRUE;
1691 ei->event_page_nr[k] = j;
1692 ei->event_page[k] = &ei->change_page[j];
1698 /* ---------- initialize run-time trigger player and element ------------- */
1700 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1702 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1704 for (j = 0; j < ei->num_change_pages; j++)
1706 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1707 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1708 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1709 ei->change_page[j].actual_trigger_ce_value = 0;
1710 ei->change_page[j].actual_trigger_ce_score = 0;
1714 /* ---------- initialize trigger events ---------------------------------- */
1716 /* initialize trigger events information */
1717 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1718 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1719 trigger_events[i][j] = FALSE;
1721 /* add trigger events from element change event properties */
1722 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1724 struct ElementInfo *ei = &element_info[i];
1726 for (j = 0; j < ei->num_change_pages; j++)
1728 if (!ei->change_page[j].can_change_or_has_action)
1731 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1733 int trigger_element = ei->change_page[j].trigger_element;
1735 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1737 if (ei->change_page[j].has_event[k])
1739 if (IS_GROUP_ELEMENT(trigger_element))
1741 struct ElementGroupInfo *group =
1742 element_info[trigger_element].group;
1744 for (l = 0; l < group->num_elements_resolved; l++)
1745 trigger_events[group->element_resolved[l]][k] = TRUE;
1748 trigger_events[trigger_element][k] = TRUE;
1755 /* ---------- initialize push delay -------------------------------------- */
1757 /* initialize push delay values to default */
1758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1760 if (!IS_CUSTOM_ELEMENT(i))
1762 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1763 element_info[i].push_delay_random = game.default_push_delay_random;
1767 /* set push delay value for certain elements from pre-defined list */
1768 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1770 int e = push_delay_list[i].element;
1772 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1773 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1776 /* set push delay value for Supaplex elements for newer engine versions */
1777 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1779 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1781 if (IS_SP_ELEMENT(i))
1783 /* set SP push delay to just enough to push under a falling zonk */
1784 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1786 element_info[i].push_delay_fixed = delay;
1787 element_info[i].push_delay_random = 0;
1792 /* ---------- initialize move stepsize ----------------------------------- */
1794 /* initialize move stepsize values to default */
1795 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1796 if (!IS_CUSTOM_ELEMENT(i))
1797 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1799 /* set move stepsize value for certain elements from pre-defined list */
1800 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1802 int e = move_stepsize_list[i].element;
1804 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1807 /* ---------- initialize collect score ----------------------------------- */
1809 /* initialize collect score values for custom elements from initial value */
1810 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1811 if (IS_CUSTOM_ELEMENT(i))
1812 element_info[i].collect_score = element_info[i].collect_score_initial;
1814 /* ---------- initialize collect count ----------------------------------- */
1816 /* initialize collect count values for non-custom elements */
1817 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1818 if (!IS_CUSTOM_ELEMENT(i))
1819 element_info[i].collect_count_initial = 0;
1821 /* add collect count values for all elements from pre-defined list */
1822 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1823 element_info[collect_count_list[i].element].collect_count_initial =
1824 collect_count_list[i].count;
1826 /* ---------- initialize access direction -------------------------------- */
1828 /* initialize access direction values to default (access from every side) */
1829 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1830 if (!IS_CUSTOM_ELEMENT(i))
1831 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1833 /* set access direction value for certain elements from pre-defined list */
1834 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1835 element_info[access_direction_list[i].element].access_direction =
1836 access_direction_list[i].direction;
1838 /* ---------- initialize explosion content ------------------------------- */
1839 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1841 if (IS_CUSTOM_ELEMENT(i))
1844 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1846 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1848 element_info[i].content.e[x][y] =
1849 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1850 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1851 i == EL_PLAYER_3 ? EL_EMERALD :
1852 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1853 i == EL_MOLE ? EL_EMERALD_RED :
1854 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1855 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1856 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1857 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1858 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1859 i == EL_WALL_EMERALD ? EL_EMERALD :
1860 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1861 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1862 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1863 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1864 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1865 i == EL_WALL_PEARL ? EL_PEARL :
1866 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1872 int get_num_special_action(int element, int action_first, int action_last)
1874 int num_special_action = 0;
1877 for (i = action_first; i <= action_last; i++)
1879 boolean found = FALSE;
1881 for (j = 0; j < NUM_DIRECTIONS; j++)
1882 if (el_act_dir2img(element, i, j) !=
1883 el_act_dir2img(element, ACTION_DEFAULT, j))
1887 num_special_action++;
1893 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1896 return num_special_action;
1900 =============================================================================
1902 -----------------------------------------------------------------------------
1903 initialize and start new game
1904 =============================================================================
1909 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1910 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1911 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1916 /* don't play tapes over network */
1917 network_playing = (options.network && !tape.playing);
1919 for (i = 0; i < MAX_PLAYERS; i++)
1921 struct PlayerInfo *player = &stored_player[i];
1923 player->index_nr = i;
1924 player->index_bit = (1 << i);
1925 player->element_nr = EL_PLAYER_1 + i;
1927 player->present = FALSE;
1928 player->active = FALSE;
1931 player->effective_action = 0;
1932 player->programmed_action = 0;
1935 player->gems_still_needed = level.gems_needed;
1936 player->sokobanfields_still_needed = 0;
1937 player->lights_still_needed = 0;
1938 player->friends_still_needed = 0;
1940 for (j = 0; j < MAX_NUM_KEYS; j++)
1941 player->key[j] = FALSE;
1943 player->dynabomb_count = 0;
1944 player->dynabomb_size = 1;
1945 player->dynabombs_left = 0;
1946 player->dynabomb_xl = FALSE;
1948 player->MovDir = MV_NONE;
1951 player->GfxDir = MV_NONE;
1952 player->GfxAction = ACTION_DEFAULT;
1954 player->StepFrame = 0;
1956 player->use_murphy = FALSE;
1957 player->artwork_element =
1958 (level.use_artwork_element[i] ? level.artwork_element[i] :
1959 player->element_nr);
1961 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1962 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1964 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1966 player->actual_frame_counter = 0;
1968 player->step_counter = 0;
1970 player->last_move_dir = MV_NONE;
1972 player->is_waiting = FALSE;
1973 player->is_moving = FALSE;
1974 player->is_auto_moving = FALSE;
1975 player->is_digging = FALSE;
1976 player->is_snapping = FALSE;
1977 player->is_collecting = FALSE;
1978 player->is_pushing = FALSE;
1979 player->is_switching = FALSE;
1980 player->is_dropping = FALSE;
1981 player->is_dropping_pressed = FALSE;
1983 player->is_bored = FALSE;
1984 player->is_sleeping = FALSE;
1986 player->frame_counter_bored = -1;
1987 player->frame_counter_sleeping = -1;
1989 player->anim_delay_counter = 0;
1990 player->post_delay_counter = 0;
1992 player->dir_waiting = MV_NONE;
1993 player->action_waiting = ACTION_DEFAULT;
1994 player->last_action_waiting = ACTION_DEFAULT;
1995 player->special_action_bored = ACTION_DEFAULT;
1996 player->special_action_sleeping = ACTION_DEFAULT;
1999 /* cannot be set here -- could be modified in Init[Player]Field() below */
2001 /* set number of special actions for bored and sleeping animation */
2002 player->num_special_action_bored =
2003 get_num_special_action(player->artwork_element,
2004 ACTION_BORING_1, ACTION_BORING_LAST);
2005 player->num_special_action_sleeping =
2006 get_num_special_action(player->artwork_element,
2007 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2010 player->switch_x = -1;
2011 player->switch_y = -1;
2013 player->drop_x = -1;
2014 player->drop_y = -1;
2016 player->show_envelope = 0;
2019 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
2021 player->move_delay = game.initial_move_delay;
2022 player->move_delay_value = game.initial_move_delay_value;
2024 player->move_delay_value_next = -1;
2026 player->move_delay_reset_counter = 0;
2028 player->cannot_move = FALSE;
2031 player->push_delay = -1; /* initialized when pushing starts */
2032 player->push_delay_value = game.initial_push_delay_value;
2034 player->drop_delay = 0;
2035 player->drop_pressed_delay = 0;
2037 player->last_jx = player->last_jy = 0;
2038 player->jx = player->jy = 0;
2040 player->shield_normal_time_left = 0;
2041 player->shield_deadly_time_left = 0;
2043 player->inventory_infinite_element = EL_UNDEFINED;
2044 player->inventory_size = 0;
2046 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2047 SnapField(player, 0, 0);
2049 player->LevelSolved = FALSE;
2050 player->GameOver = FALSE;
2053 network_player_action_received = FALSE;
2055 #if defined(NETWORK_AVALIABLE)
2056 /* initial null action */
2057 if (network_playing)
2058 SendToServer_MovePlayer(MV_NONE);
2067 TimeLeft = level.time;
2070 ScreenMovDir = MV_NONE;
2074 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2076 AllPlayersGone = FALSE;
2078 game.yamyam_content_nr = 0;
2079 game.magic_wall_active = FALSE;
2080 game.magic_wall_time_left = 0;
2081 game.light_time_left = 0;
2082 game.timegate_time_left = 0;
2083 game.switchgate_pos = 0;
2084 game.wind_direction = level.wind_direction_initial;
2085 game.gravity = level.initial_gravity;
2086 game.explosions_delayed = TRUE;
2088 game.lenses_time_left = 0;
2089 game.magnify_time_left = 0;
2091 game.ball_state = level.ball_state_initial;
2092 game.ball_content_nr = 0;
2094 game.envelope_active = FALSE;
2096 /* set focus to local player for network games, else to all players */
2097 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2098 game.centered_player_nr_next = game.centered_player_nr;
2099 game.set_centered_player = FALSE;
2101 if (network_playing && tape.recording)
2103 /* store client dependent player focus when recording network games */
2104 tape.centered_player_nr_next = game.centered_player_nr_next;
2105 tape.set_centered_player = TRUE;
2109 printf("::: focus set to player %d [%d]\n",
2110 game.centered_player_nr, local_player->index_nr);
2113 for (i = 0; i < NUM_BELTS; i++)
2115 game.belt_dir[i] = MV_NONE;
2116 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2119 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2120 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2123 SCAN_PLAYFIELD(x, y)
2125 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2128 Feld[x][y] = level.field[x][y];
2129 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2130 ChangeDelay[x][y] = 0;
2131 ChangePage[x][y] = -1;
2132 #if USE_NEW_CUSTOM_VALUE
2133 CustomValue[x][y] = 0; /* initialized in InitField() */
2135 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2137 WasJustMoving[x][y] = 0;
2138 WasJustFalling[x][y] = 0;
2139 CheckCollision[x][y] = 0;
2141 Pushed[x][y] = FALSE;
2143 ChangeCount[x][y] = 0;
2144 ChangeEvent[x][y] = -1;
2146 ExplodePhase[x][y] = 0;
2147 ExplodeDelay[x][y] = 0;
2148 ExplodeField[x][y] = EX_TYPE_NONE;
2150 RunnerVisit[x][y] = 0;
2151 PlayerVisit[x][y] = 0;
2154 GfxRandom[x][y] = INIT_GFX_RANDOM();
2155 GfxElement[x][y] = EL_UNDEFINED;
2156 GfxAction[x][y] = ACTION_DEFAULT;
2157 GfxDir[x][y] = MV_NONE;
2161 SCAN_PLAYFIELD(x, y)
2163 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2166 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2168 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2170 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2173 InitField(x, y, TRUE);
2178 for (i = 0; i < MAX_PLAYERS; i++)
2180 struct PlayerInfo *player = &stored_player[i];
2183 /* set number of special actions for bored and sleeping animation */
2184 player->num_special_action_bored =
2185 get_num_special_action(player->artwork_element,
2186 ACTION_BORING_1, ACTION_BORING_LAST);
2187 player->num_special_action_sleeping =
2188 get_num_special_action(player->artwork_element,
2189 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2194 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2195 emulate_sb ? EMU_SOKOBAN :
2196 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2198 #if USE_NEW_ALL_SLIPPERY
2199 /* initialize type of slippery elements */
2200 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2202 if (!IS_CUSTOM_ELEMENT(i))
2204 /* default: elements slip down either to the left or right randomly */
2205 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2207 /* SP style elements prefer to slip down on the left side */
2208 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2209 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2211 /* BD style elements prefer to slip down on the left side */
2212 if (game.emulation == EMU_BOULDERDASH)
2213 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2218 /* initialize explosion and ignition delay */
2219 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2221 if (!IS_CUSTOM_ELEMENT(i))
2224 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2225 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2226 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2227 int last_phase = (num_phase + 1) * delay;
2228 int half_phase = (num_phase / 2) * delay;
2230 element_info[i].explosion_delay = last_phase - 1;
2231 element_info[i].ignition_delay = half_phase;
2233 if (i == EL_BLACK_ORB)
2234 element_info[i].ignition_delay = 1;
2238 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2239 element_info[i].explosion_delay = 1;
2241 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2242 element_info[i].ignition_delay = 1;
2246 /* correct non-moving belts to start moving left */
2247 for (i = 0; i < NUM_BELTS; i++)
2248 if (game.belt_dir[i] == MV_NONE)
2249 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2251 /* check if any connected player was not found in playfield */
2252 for (i = 0; i < MAX_PLAYERS; i++)
2254 struct PlayerInfo *player = &stored_player[i];
2256 if (player->connected && !player->present)
2258 for (j = 0; j < MAX_PLAYERS; j++)
2260 struct PlayerInfo *some_player = &stored_player[j];
2261 int jx = some_player->jx, jy = some_player->jy;
2263 /* assign first free player found that is present in the playfield */
2264 if (some_player->present && !some_player->connected)
2266 player->present = TRUE;
2267 player->active = TRUE;
2269 some_player->present = FALSE;
2270 some_player->active = FALSE;
2273 player->element_nr = some_player->element_nr;
2276 player->artwork_element = some_player->artwork_element;
2278 player->block_last_field = some_player->block_last_field;
2279 player->block_delay_adjustment = some_player->block_delay_adjustment;
2281 StorePlayer[jx][jy] = player->element_nr;
2282 player->jx = player->last_jx = jx;
2283 player->jy = player->last_jy = jy;
2293 /* when playing a tape, eliminate all players who do not participate */
2295 for (i = 0; i < MAX_PLAYERS; i++)
2297 if (stored_player[i].active && !tape.player_participates[i])
2299 struct PlayerInfo *player = &stored_player[i];
2300 int jx = player->jx, jy = player->jy;
2302 player->active = FALSE;
2303 StorePlayer[jx][jy] = 0;
2304 Feld[jx][jy] = EL_EMPTY;
2308 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2310 /* when in single player mode, eliminate all but the first active player */
2312 for (i = 0; i < MAX_PLAYERS; i++)
2314 if (stored_player[i].active)
2316 for (j = i + 1; j < MAX_PLAYERS; j++)
2318 if (stored_player[j].active)
2320 struct PlayerInfo *player = &stored_player[j];
2321 int jx = player->jx, jy = player->jy;
2323 player->active = FALSE;
2324 player->present = FALSE;
2326 StorePlayer[jx][jy] = 0;
2327 Feld[jx][jy] = EL_EMPTY;
2334 /* when recording the game, store which players take part in the game */
2337 for (i = 0; i < MAX_PLAYERS; i++)
2338 if (stored_player[i].active)
2339 tape.player_participates[i] = TRUE;
2344 for (i = 0; i < MAX_PLAYERS; i++)
2346 struct PlayerInfo *player = &stored_player[i];
2348 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2353 if (local_player == player)
2354 printf("Player %d is local player.\n", i+1);
2358 if (BorderElement == EL_EMPTY)
2361 SBX_Right = lev_fieldx - SCR_FIELDX;
2363 SBY_Lower = lev_fieldy - SCR_FIELDY;
2368 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2370 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2373 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2374 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2376 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2377 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2379 /* if local player not found, look for custom element that might create
2380 the player (make some assumptions about the right custom element) */
2381 if (!local_player->present)
2383 int start_x = 0, start_y = 0;
2384 int found_rating = 0;
2385 int found_element = EL_UNDEFINED;
2386 int player_nr = local_player->index_nr;
2389 SCAN_PLAYFIELD(x, y)
2391 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2394 int element = Feld[x][y];
2399 if (level.use_start_element[player_nr] &&
2400 level.start_element[player_nr] == element &&
2407 found_element = element;
2410 if (!IS_CUSTOM_ELEMENT(element))
2413 if (CAN_CHANGE(element))
2415 for (i = 0; i < element_info[element].num_change_pages; i++)
2417 /* check for player created from custom element as single target */
2418 content = element_info[element].change_page[i].target_element;
2419 is_player = ELEM_IS_PLAYER(content);
2421 if (is_player && (found_rating < 3 || element < found_element))
2427 found_element = element;
2432 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2434 /* check for player created from custom element as explosion content */
2435 content = element_info[element].content.e[xx][yy];
2436 is_player = ELEM_IS_PLAYER(content);
2438 if (is_player && (found_rating < 2 || element < found_element))
2440 start_x = x + xx - 1;
2441 start_y = y + yy - 1;
2444 found_element = element;
2447 if (!CAN_CHANGE(element))
2450 for (i = 0; i < element_info[element].num_change_pages; i++)
2452 /* check for player created from custom element as extended target */
2454 element_info[element].change_page[i].target_content.e[xx][yy];
2456 is_player = ELEM_IS_PLAYER(content);
2458 if (is_player && (found_rating < 1 || element < found_element))
2460 start_x = x + xx - 1;
2461 start_y = y + yy - 1;
2464 found_element = element;
2470 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2471 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2474 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2475 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2480 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2481 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2482 local_player->jx - MIDPOSX);
2484 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2485 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2486 local_player->jy - MIDPOSY);
2489 if (!game.restart_level)
2490 CloseDoor(DOOR_CLOSE_1);
2492 /* !!! FIX THIS (START) !!! */
2493 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2495 InitGameEngine_EM();
2502 /* after drawing the level, correct some elements */
2503 if (game.timegate_time_left == 0)
2504 CloseAllOpenTimegates();
2506 if (setup.soft_scrolling)
2507 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2509 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2512 /* !!! FIX THIS (END) !!! */
2514 if (!game.restart_level)
2516 /* copy default game door content to main double buffer */
2517 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2518 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2521 DrawGameDoorValues();
2523 if (!game.restart_level)
2527 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2528 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2529 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2533 /* copy actual game door content to door double buffer for OpenDoor() */
2534 BlitBitmap(drawto, bitmap_db_door,
2535 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2537 OpenDoor(DOOR_OPEN_ALL);
2539 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2541 if (setup.sound_music)
2544 KeyboardAutoRepeatOffUnlessAutoplay();
2548 for (i = 0; i < MAX_PLAYERS; i++)
2549 printf("Player %d %sactive.\n",
2550 i + 1, (stored_player[i].active ? "" : "not "));
2554 game.restart_level = FALSE;
2557 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2559 /* this is used for non-R'n'D game engines to update certain engine values */
2561 /* needed to determine if sounds are played within the visible screen area */
2562 scroll_x = actual_scroll_x;
2563 scroll_y = actual_scroll_y;
2566 void InitMovDir(int x, int y)
2568 int i, element = Feld[x][y];
2569 static int xy[4][2] =
2576 static int direction[3][4] =
2578 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2579 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2580 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2589 Feld[x][y] = EL_BUG;
2590 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2593 case EL_SPACESHIP_RIGHT:
2594 case EL_SPACESHIP_UP:
2595 case EL_SPACESHIP_LEFT:
2596 case EL_SPACESHIP_DOWN:
2597 Feld[x][y] = EL_SPACESHIP;
2598 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2601 case EL_BD_BUTTERFLY_RIGHT:
2602 case EL_BD_BUTTERFLY_UP:
2603 case EL_BD_BUTTERFLY_LEFT:
2604 case EL_BD_BUTTERFLY_DOWN:
2605 Feld[x][y] = EL_BD_BUTTERFLY;
2606 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2609 case EL_BD_FIREFLY_RIGHT:
2610 case EL_BD_FIREFLY_UP:
2611 case EL_BD_FIREFLY_LEFT:
2612 case EL_BD_FIREFLY_DOWN:
2613 Feld[x][y] = EL_BD_FIREFLY;
2614 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2617 case EL_PACMAN_RIGHT:
2619 case EL_PACMAN_LEFT:
2620 case EL_PACMAN_DOWN:
2621 Feld[x][y] = EL_PACMAN;
2622 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2625 case EL_YAMYAM_LEFT:
2626 case EL_YAMYAM_RIGHT:
2628 case EL_YAMYAM_DOWN:
2629 Feld[x][y] = EL_YAMYAM;
2630 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2633 case EL_SP_SNIKSNAK:
2634 MovDir[x][y] = MV_UP;
2637 case EL_SP_ELECTRON:
2638 MovDir[x][y] = MV_LEFT;
2645 Feld[x][y] = EL_MOLE;
2646 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2650 if (IS_CUSTOM_ELEMENT(element))
2652 struct ElementInfo *ei = &element_info[element];
2653 int move_direction_initial = ei->move_direction_initial;
2654 int move_pattern = ei->move_pattern;
2656 if (move_direction_initial == MV_START_PREVIOUS)
2658 if (MovDir[x][y] != MV_NONE)
2661 move_direction_initial = MV_START_AUTOMATIC;
2664 if (move_direction_initial == MV_START_RANDOM)
2665 MovDir[x][y] = 1 << RND(4);
2666 else if (move_direction_initial & MV_ANY_DIRECTION)
2667 MovDir[x][y] = move_direction_initial;
2668 else if (move_pattern == MV_ALL_DIRECTIONS ||
2669 move_pattern == MV_TURNING_LEFT ||
2670 move_pattern == MV_TURNING_RIGHT ||
2671 move_pattern == MV_TURNING_LEFT_RIGHT ||
2672 move_pattern == MV_TURNING_RIGHT_LEFT ||
2673 move_pattern == MV_TURNING_RANDOM)
2674 MovDir[x][y] = 1 << RND(4);
2675 else if (move_pattern == MV_HORIZONTAL)
2676 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2677 else if (move_pattern == MV_VERTICAL)
2678 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2679 else if (move_pattern & MV_ANY_DIRECTION)
2680 MovDir[x][y] = element_info[element].move_pattern;
2681 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2682 move_pattern == MV_ALONG_RIGHT_SIDE)
2684 /* use random direction as default start direction */
2685 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2686 MovDir[x][y] = 1 << RND(4);
2688 for (i = 0; i < NUM_DIRECTIONS; i++)
2690 int x1 = x + xy[i][0];
2691 int y1 = y + xy[i][1];
2693 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2695 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2696 MovDir[x][y] = direction[0][i];
2698 MovDir[x][y] = direction[1][i];
2707 MovDir[x][y] = 1 << RND(4);
2709 if (element != EL_BUG &&
2710 element != EL_SPACESHIP &&
2711 element != EL_BD_BUTTERFLY &&
2712 element != EL_BD_FIREFLY)
2715 for (i = 0; i < NUM_DIRECTIONS; i++)
2717 int x1 = x + xy[i][0];
2718 int y1 = y + xy[i][1];
2720 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2722 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2724 MovDir[x][y] = direction[0][i];
2727 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2728 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2730 MovDir[x][y] = direction[1][i];
2739 GfxDir[x][y] = MovDir[x][y];
2742 void InitAmoebaNr(int x, int y)
2745 int group_nr = AmoebeNachbarNr(x, y);
2749 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2751 if (AmoebaCnt[i] == 0)
2759 AmoebaNr[x][y] = group_nr;
2760 AmoebaCnt[group_nr]++;
2761 AmoebaCnt2[group_nr]++;
2767 boolean raise_level = FALSE;
2769 if (local_player->MovPos)
2772 if (tape.auto_play) /* tape might already be stopped here */
2773 tape.auto_play_level_solved = TRUE;
2775 local_player->LevelSolved = FALSE;
2777 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2781 if (!tape.playing && setup.sound_loops)
2782 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2783 SND_CTRL_PLAY_LOOP);
2785 while (TimeLeft > 0)
2787 if (!tape.playing && !setup.sound_loops)
2788 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2790 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2793 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2798 RaiseScore(level.score[SC_TIME_BONUS]);
2801 DrawGameValue_Time(TimeLeft);
2809 if (!tape.playing && setup.sound_loops)
2810 StopSound(SND_GAME_LEVELTIME_BONUS);
2812 else if (level.time == 0) /* level without time limit */
2814 if (!tape.playing && setup.sound_loops)
2815 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2816 SND_CTRL_PLAY_LOOP);
2818 while (TimePlayed < 999)
2820 if (!tape.playing && !setup.sound_loops)
2821 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2823 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2826 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2831 RaiseScore(level.score[SC_TIME_BONUS]);
2834 DrawGameValue_Time(TimePlayed);
2842 if (!tape.playing && setup.sound_loops)
2843 StopSound(SND_GAME_LEVELTIME_BONUS);
2846 /* close exit door after last player */
2847 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2848 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2849 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2851 int element = Feld[ExitX][ExitY];
2853 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2854 EL_SP_EXIT_CLOSING);
2856 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2859 /* player disappears */
2860 if (ExitX >= 0 && ExitY >= 0)
2861 DrawLevelField(ExitX, ExitY);
2867 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2869 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2875 CloseDoor(DOOR_CLOSE_1);
2880 SaveTape(tape.level_nr); /* Ask to save tape */
2883 if (level_nr == leveldir_current->handicap_level)
2885 leveldir_current->handicap_level++;
2886 SaveLevelSetup_SeriesInfo();
2889 if (level_editor_test_game)
2890 local_player->score = -1; /* no highscore when playing from editor */
2891 else if (level_nr < leveldir_current->last_level)
2892 raise_level = TRUE; /* advance to next level */
2894 if ((hi_pos = NewHiScore()) >= 0)
2896 game_status = GAME_MODE_SCORES;
2897 DrawHallOfFame(hi_pos);
2906 game_status = GAME_MODE_MAIN;
2923 LoadScore(level_nr);
2925 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2926 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2929 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2931 if (local_player->score > highscore[k].Score)
2933 /* player has made it to the hall of fame */
2935 if (k < MAX_SCORE_ENTRIES - 1)
2937 int m = MAX_SCORE_ENTRIES - 1;
2940 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2941 if (strEqual(setup.player_name, highscore[l].Name))
2943 if (m == k) /* player's new highscore overwrites his old one */
2947 for (l = m; l > k; l--)
2949 strcpy(highscore[l].Name, highscore[l - 1].Name);
2950 highscore[l].Score = highscore[l - 1].Score;
2957 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2958 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2959 highscore[k].Score = local_player->score;
2965 else if (!strncmp(setup.player_name, highscore[k].Name,
2966 MAX_PLAYER_NAME_LEN))
2967 break; /* player already there with a higher score */
2973 SaveScore(level_nr);
2978 inline static int getElementMoveStepsize(int x, int y)
2980 int element = Feld[x][y];
2981 int direction = MovDir[x][y];
2982 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2983 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2984 int horiz_move = (dx != 0);
2985 int sign = (horiz_move ? dx : dy);
2986 int step = sign * element_info[element].move_stepsize;
2988 /* special values for move stepsize for spring and things on conveyor belt */
2992 if (element == EL_SPRING)
2993 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2994 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2995 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2996 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2998 if (CAN_FALL(element) &&
2999 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3000 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3001 else if (element == EL_SPRING)
3002 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3009 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3011 if (player->GfxAction != action || player->GfxDir != dir)
3014 printf("Player frame reset! (%d => %d, %d => %d)\n",
3015 player->GfxAction, action, player->GfxDir, dir);
3018 player->GfxAction = action;
3019 player->GfxDir = dir;
3021 player->StepFrame = 0;
3025 #if USE_GFX_RESET_GFX_ANIMATION
3026 static void ResetGfxFrame(int x, int y, boolean redraw)
3028 int element = Feld[x][y];
3029 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3030 int last_gfx_frame = GfxFrame[x][y];
3032 if (graphic_info[graphic].anim_global_sync)
3033 GfxFrame[x][y] = FrameCounter;
3034 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3035 GfxFrame[x][y] = CustomValue[x][y];
3036 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3037 GfxFrame[x][y] = element_info[element].collect_score;
3039 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3040 DrawLevelGraphicAnimation(x, y, graphic);
3044 static void ResetGfxAnimation(int x, int y)
3047 int element, graphic;
3050 GfxAction[x][y] = ACTION_DEFAULT;
3051 GfxDir[x][y] = MovDir[x][y];
3055 element = Feld[x][y];
3056 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3058 if (graphic_info[graphic].anim_global_sync)
3059 GfxFrame[x][y] = FrameCounter;
3060 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3061 GfxFrame[x][y] = CustomValue[x][y];
3062 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3063 GfxFrame[x][y] = element_info[element].collect_score;
3066 #if USE_GFX_RESET_GFX_ANIMATION
3067 ResetGfxFrame(x, y, FALSE);
3071 static void ResetRandomAnimationValue(int x, int y)
3073 GfxRandom[x][y] = INIT_GFX_RANDOM();
3076 void InitMovingField(int x, int y, int direction)
3078 int element = Feld[x][y];
3082 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3083 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3087 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3088 ResetGfxAnimation(x, y);
3090 MovDir[x][y] = direction;
3091 GfxDir[x][y] = direction;
3092 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3093 ACTION_FALLING : ACTION_MOVING);
3096 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3098 if (graphic_info[graphic].anim_global_sync)
3099 GfxFrame[x][y] = FrameCounter;
3100 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3101 GfxFrame[x][y] = CustomValue[x][y];
3102 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3103 GfxFrame[x][y] = element_info[element].collect_score;
3106 /* this is needed for CEs with property "can move" / "not moving" */
3108 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3110 if (Feld[newx][newy] == EL_EMPTY)
3111 Feld[newx][newy] = EL_BLOCKED;
3113 MovDir[newx][newy] = MovDir[x][y];
3115 #if USE_NEW_CUSTOM_VALUE
3116 CustomValue[newx][newy] = CustomValue[x][y];
3119 GfxFrame[newx][newy] = GfxFrame[x][y];
3120 GfxRandom[newx][newy] = GfxRandom[x][y];
3121 GfxAction[newx][newy] = GfxAction[x][y];
3122 GfxDir[newx][newy] = GfxDir[x][y];
3126 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3128 int direction = MovDir[x][y];
3130 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3131 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3133 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3134 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3141 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3143 int oldx = x, oldy = y;
3144 int direction = MovDir[x][y];
3146 if (direction == MV_LEFT)
3148 else if (direction == MV_RIGHT)
3150 else if (direction == MV_UP)
3152 else if (direction == MV_DOWN)
3155 *comes_from_x = oldx;
3156 *comes_from_y = oldy;
3159 int MovingOrBlocked2Element(int x, int y)
3161 int element = Feld[x][y];
3163 if (element == EL_BLOCKED)
3167 Blocked2Moving(x, y, &oldx, &oldy);
3168 return Feld[oldx][oldy];
3174 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3176 /* like MovingOrBlocked2Element(), but if element is moving
3177 and (x,y) is the field the moving element is just leaving,
3178 return EL_BLOCKED instead of the element value */
3179 int element = Feld[x][y];
3181 if (IS_MOVING(x, y))
3183 if (element == EL_BLOCKED)
3187 Blocked2Moving(x, y, &oldx, &oldy);
3188 return Feld[oldx][oldy];
3197 static void RemoveField(int x, int y)
3199 Feld[x][y] = EL_EMPTY;
3205 #if USE_NEW_CUSTOM_VALUE
3206 CustomValue[x][y] = 0;
3210 ChangeDelay[x][y] = 0;
3211 ChangePage[x][y] = -1;
3212 Pushed[x][y] = FALSE;
3215 ExplodeField[x][y] = EX_TYPE_NONE;
3218 GfxElement[x][y] = EL_UNDEFINED;
3219 GfxAction[x][y] = ACTION_DEFAULT;
3220 GfxDir[x][y] = MV_NONE;
3223 void RemoveMovingField(int x, int y)
3225 int oldx = x, oldy = y, newx = x, newy = y;
3226 int element = Feld[x][y];
3227 int next_element = EL_UNDEFINED;
3229 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3232 if (IS_MOVING(x, y))
3234 Moving2Blocked(x, y, &newx, &newy);
3236 if (Feld[newx][newy] != EL_BLOCKED)
3238 /* element is moving, but target field is not free (blocked), but
3239 already occupied by something different (example: acid pool);
3240 in this case, only remove the moving field, but not the target */
3242 RemoveField(oldx, oldy);
3244 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3246 DrawLevelField(oldx, oldy);
3251 else if (element == EL_BLOCKED)
3253 Blocked2Moving(x, y, &oldx, &oldy);
3254 if (!IS_MOVING(oldx, oldy))
3258 if (element == EL_BLOCKED &&
3259 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3260 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3261 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3262 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3263 next_element = get_next_element(Feld[oldx][oldy]);
3265 RemoveField(oldx, oldy);
3266 RemoveField(newx, newy);
3268 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3270 if (next_element != EL_UNDEFINED)
3271 Feld[oldx][oldy] = next_element;
3273 DrawLevelField(oldx, oldy);
3274 DrawLevelField(newx, newy);
3277 void DrawDynamite(int x, int y)
3279 int sx = SCREENX(x), sy = SCREENY(y);
3280 int graphic = el2img(Feld[x][y]);
3283 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3286 if (IS_WALKABLE_INSIDE(Back[x][y]))
3290 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3291 else if (Store[x][y])
3292 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3294 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3296 if (Back[x][y] || Store[x][y])
3297 DrawGraphicThruMask(sx, sy, graphic, frame);
3299 DrawGraphic(sx, sy, graphic, frame);
3302 void CheckDynamite(int x, int y)
3304 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3308 if (MovDelay[x][y] != 0)
3311 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3317 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3324 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3326 boolean num_checked_players = 0;
3329 for (i = 0; i < MAX_PLAYERS; i++)
3331 if (stored_player[i].active)
3333 int sx = stored_player[i].jx;
3334 int sy = stored_player[i].jy;
3336 if (num_checked_players == 0)
3343 *sx1 = MIN(*sx1, sx);
3344 *sy1 = MIN(*sy1, sy);
3345 *sx2 = MAX(*sx2, sx);
3346 *sy2 = MAX(*sy2, sy);
3349 num_checked_players++;
3354 static boolean checkIfAllPlayersFitToScreen_RND()
3356 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3358 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3360 return (sx2 - sx1 < SCR_FIELDX &&
3361 sy2 - sy1 < SCR_FIELDY);
3364 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3366 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3368 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3370 *sx = (sx1 + sx2) / 2;
3371 *sy = (sy1 + sy2) / 2;
3375 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3376 int center_x, int center_y)
3378 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3380 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3382 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3383 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3386 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3390 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3392 return (max_dx <= SCR_FIELDX / 2 &&
3393 max_dy <= SCR_FIELDY / 2);
3401 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3402 boolean quick_relocation)
3404 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3405 boolean no_delay = (tape.warp_forward);
3406 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3407 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3409 if (quick_relocation)
3411 int offset = (setup.scroll_delay ? 3 : 0);
3418 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3420 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3421 x > SBX_Right + MIDPOSX ? SBX_Right :
3424 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3425 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3430 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3431 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3432 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3434 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3435 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3436 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3438 /* don't scroll over playfield boundaries */
3439 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3440 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3442 /* don't scroll over playfield boundaries */
3443 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3444 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3447 RedrawPlayfield(TRUE, 0,0,0,0);
3451 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3452 x > SBX_Right + MIDPOSX ? SBX_Right :
3455 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3456 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3459 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3461 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3464 int fx = FX, fy = FY;
3466 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3467 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3469 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3475 fx += dx * TILEX / 2;
3476 fy += dy * TILEY / 2;
3478 ScrollLevel(dx, dy);
3481 /* scroll in two steps of half tile size to make things smoother */
3482 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3484 Delay(wait_delay_value);
3486 /* scroll second step to align at full tile size */
3488 Delay(wait_delay_value);
3493 Delay(wait_delay_value);
3499 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3501 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3502 boolean no_delay = (tape.warp_forward);
3503 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3504 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3505 int jx = player->jx;
3506 int jy = player->jy;
3508 if (quick_relocation)
3510 int offset = (setup.scroll_delay ? 3 : 0);
3512 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3514 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3515 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3516 player->jx - MIDPOSX);
3518 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3519 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3520 player->jy - MIDPOSY);
3524 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3525 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3526 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3528 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3529 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3530 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3532 /* don't scroll over playfield boundaries */
3533 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3534 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3536 /* don't scroll over playfield boundaries */
3537 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3538 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3541 RedrawPlayfield(TRUE, 0,0,0,0);
3545 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3546 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3547 player->jx - MIDPOSX);
3549 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3550 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3551 player->jy - MIDPOSY);
3553 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3555 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3558 int fx = FX, fy = FY;
3560 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3561 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3563 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3569 fx += dx * TILEX / 2;
3570 fy += dy * TILEY / 2;
3572 ScrollLevel(dx, dy);
3575 /* scroll in two steps of half tile size to make things smoother */
3576 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3578 Delay(wait_delay_value);
3580 /* scroll second step to align at full tile size */
3582 Delay(wait_delay_value);
3587 Delay(wait_delay_value);
3593 void RelocatePlayer(int jx, int jy, int el_player_raw)
3595 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3596 int player_nr = GET_PLAYER_NR(el_player);
3597 struct PlayerInfo *player = &stored_player[player_nr];
3598 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3599 boolean no_delay = (tape.warp_forward);
3600 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3601 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3602 int old_jx = player->jx;
3603 int old_jy = player->jy;
3604 int old_element = Feld[old_jx][old_jy];
3605 int element = Feld[jx][jy];
3606 boolean player_relocated = (old_jx != jx || old_jy != jy);
3608 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3609 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3610 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3611 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3612 int leave_side_horiz = move_dir_horiz;
3613 int leave_side_vert = move_dir_vert;
3614 int enter_side = enter_side_horiz | enter_side_vert;
3615 int leave_side = leave_side_horiz | leave_side_vert;
3617 if (player->GameOver) /* do not reanimate dead player */
3620 if (!player_relocated) /* no need to relocate the player */
3623 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3625 RemoveField(jx, jy); /* temporarily remove newly placed player */
3626 DrawLevelField(jx, jy);
3629 if (player->present)
3631 while (player->MovPos)
3633 ScrollPlayer(player, SCROLL_GO_ON);
3634 ScrollScreen(NULL, SCROLL_GO_ON);
3636 AdvanceFrameAndPlayerCounters(player->index_nr);
3641 Delay(wait_delay_value);
3644 DrawPlayer(player); /* needed here only to cleanup last field */
3645 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3647 player->is_moving = FALSE;
3650 if (IS_CUSTOM_ELEMENT(old_element))
3651 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3653 player->index_bit, leave_side);
3655 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3657 player->index_bit, leave_side);
3659 Feld[jx][jy] = el_player;
3660 InitPlayerField(jx, jy, el_player, TRUE);
3662 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3664 Feld[jx][jy] = element;
3665 InitField(jx, jy, FALSE);
3669 /* only visually relocate centered player */
3671 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3672 level.instant_relocation);
3674 if (player->index_nr == game.centered_player_nr)
3675 DrawRelocatePlayer(player, level.instant_relocation);
3678 if (player == local_player) /* only visually relocate local player */
3679 DrawRelocatePlayer(player, level.instant_relocation);
3682 TestIfPlayerTouchesBadThing(jx, jy);
3683 TestIfPlayerTouchesCustomElement(jx, jy);
3685 if (IS_CUSTOM_ELEMENT(element))
3686 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3687 player->index_bit, enter_side);
3689 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3690 player->index_bit, enter_side);
3693 void Explode(int ex, int ey, int phase, int mode)
3699 /* !!! eliminate this variable !!! */
3700 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3702 if (game.explosions_delayed)
3704 ExplodeField[ex][ey] = mode;
3708 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3710 int center_element = Feld[ex][ey];
3711 int artwork_element, explosion_element; /* set these values later */
3714 /* --- This is only really needed (and now handled) in "Impact()". --- */
3715 /* do not explode moving elements that left the explode field in time */
3716 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3717 center_element == EL_EMPTY &&
3718 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3723 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3724 if (mode == EX_TYPE_NORMAL ||
3725 mode == EX_TYPE_CENTER ||
3726 mode == EX_TYPE_CROSS)
3727 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3730 /* remove things displayed in background while burning dynamite */
3731 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3734 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3736 /* put moving element to center field (and let it explode there) */
3737 center_element = MovingOrBlocked2Element(ex, ey);
3738 RemoveMovingField(ex, ey);
3739 Feld[ex][ey] = center_element;
3742 /* now "center_element" is finally determined -- set related values now */
3743 artwork_element = center_element; /* for custom player artwork */
3744 explosion_element = center_element; /* for custom player artwork */
3746 if (IS_PLAYER(ex, ey))
3748 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3750 artwork_element = stored_player[player_nr].artwork_element;
3752 if (level.use_explosion_element[player_nr])
3754 explosion_element = level.explosion_element[player_nr];
3755 artwork_element = explosion_element;
3760 if (mode == EX_TYPE_NORMAL ||
3761 mode == EX_TYPE_CENTER ||
3762 mode == EX_TYPE_CROSS)
3763 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3767 last_phase = element_info[explosion_element].explosion_delay + 1;
3769 last_phase = element_info[center_element].explosion_delay + 1;
3772 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3774 int xx = x - ex + 1;
3775 int yy = y - ey + 1;
3778 if (!IN_LEV_FIELD(x, y) ||
3779 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3780 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3783 element = Feld[x][y];
3785 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3787 element = MovingOrBlocked2Element(x, y);
3789 if (!IS_EXPLOSION_PROOF(element))
3790 RemoveMovingField(x, y);
3793 /* indestructible elements can only explode in center (but not flames) */
3794 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3795 mode == EX_TYPE_BORDER)) ||
3796 element == EL_FLAMES)
3799 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3800 behaviour, for example when touching a yamyam that explodes to rocks
3801 with active deadly shield, a rock is created under the player !!! */
3802 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3804 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3805 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3806 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3808 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3811 if (IS_ACTIVE_BOMB(element))
3813 /* re-activate things under the bomb like gate or penguin */
3814 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3821 /* save walkable background elements while explosion on same tile */
3822 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3823 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3824 Back[x][y] = element;
3826 /* ignite explodable elements reached by other explosion */
3827 if (element == EL_EXPLOSION)
3828 element = Store2[x][y];
3830 if (AmoebaNr[x][y] &&
3831 (element == EL_AMOEBA_FULL ||
3832 element == EL_BD_AMOEBA ||
3833 element == EL_AMOEBA_GROWING))
3835 AmoebaCnt[AmoebaNr[x][y]]--;
3836 AmoebaCnt2[AmoebaNr[x][y]]--;
3841 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3844 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3846 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3848 switch(StorePlayer[ex][ey])
3851 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3854 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3857 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3861 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3866 if (PLAYERINFO(ex, ey)->use_murphy)
3867 Store[x][y] = EL_EMPTY;
3870 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3871 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3872 else if (ELEM_IS_PLAYER(center_element))
3873 Store[x][y] = EL_EMPTY;
3874 else if (center_element == EL_YAMYAM)
3875 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3876 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3877 Store[x][y] = element_info[center_element].content.e[xx][yy];
3879 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3880 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3881 otherwise) -- FIX THIS !!! */
3882 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3883 Store[x][y] = element_info[element].content.e[1][1];
3885 else if (!CAN_EXPLODE(element))
3886 Store[x][y] = element_info[element].content.e[1][1];
3889 Store[x][y] = EL_EMPTY;
3891 else if (center_element == EL_MOLE)
3892 Store[x][y] = EL_EMERALD_RED;
3893 else if (center_element == EL_PENGUIN)
3894 Store[x][y] = EL_EMERALD_PURPLE;
3895 else if (center_element == EL_BUG)
3896 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3897 else if (center_element == EL_BD_BUTTERFLY)
3898 Store[x][y] = EL_BD_DIAMOND;
3899 else if (center_element == EL_SP_ELECTRON)
3900 Store[x][y] = EL_SP_INFOTRON;
3901 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3902 Store[x][y] = level.amoeba_content;
3903 else if (center_element == EL_YAMYAM)
3904 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3905 else if (IS_CUSTOM_ELEMENT(center_element) &&
3906 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3907 Store[x][y] = element_info[center_element].content.e[xx][yy];
3908 else if (element == EL_WALL_EMERALD)
3909 Store[x][y] = EL_EMERALD;
3910 else if (element == EL_WALL_DIAMOND)
3911 Store[x][y] = EL_DIAMOND;
3912 else if (element == EL_WALL_BD_DIAMOND)
3913 Store[x][y] = EL_BD_DIAMOND;
3914 else if (element == EL_WALL_EMERALD_YELLOW)
3915 Store[x][y] = EL_EMERALD_YELLOW;
3916 else if (element == EL_WALL_EMERALD_RED)
3917 Store[x][y] = EL_EMERALD_RED;
3918 else if (element == EL_WALL_EMERALD_PURPLE)
3919 Store[x][y] = EL_EMERALD_PURPLE;
3920 else if (element == EL_WALL_PEARL)
3921 Store[x][y] = EL_PEARL;
3922 else if (element == EL_WALL_CRYSTAL)
3923 Store[x][y] = EL_CRYSTAL;
3924 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3925 Store[x][y] = element_info[element].content.e[1][1];
3927 Store[x][y] = EL_EMPTY;
3930 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3931 center_element == EL_AMOEBA_TO_DIAMOND)
3932 Store2[x][y] = element;
3934 Feld[x][y] = EL_EXPLOSION;
3935 GfxElement[x][y] = artwork_element;
3938 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3939 x, y, artwork_element, EL_NAME(artwork_element));
3942 ExplodePhase[x][y] = 1;
3943 ExplodeDelay[x][y] = last_phase;
3948 if (center_element == EL_YAMYAM)
3949 game.yamyam_content_nr =
3950 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3962 GfxFrame[x][y] = 0; /* restart explosion animation */
3964 last_phase = ExplodeDelay[x][y];
3966 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3970 /* activate this even in non-DEBUG version until cause for crash in
3971 getGraphicAnimationFrame() (see below) is found and eliminated */
3977 /* this can happen if the player leaves an explosion just in time */
3978 if (GfxElement[x][y] == EL_UNDEFINED)
3979 GfxElement[x][y] = EL_EMPTY;
3981 if (GfxElement[x][y] == EL_UNDEFINED)
3984 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3985 printf("Explode(): This should never happen!\n");
3988 GfxElement[x][y] = EL_EMPTY;
3994 border_element = Store2[x][y];
3995 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3996 border_element = StorePlayer[x][y];
3998 if (phase == element_info[border_element].ignition_delay ||
3999 phase == last_phase)
4001 boolean border_explosion = FALSE;
4003 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4004 !PLAYER_EXPLOSION_PROTECTED(x, y))
4006 KillPlayerUnlessExplosionProtected(x, y);
4007 border_explosion = TRUE;
4009 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4011 Feld[x][y] = Store2[x][y];
4014 border_explosion = TRUE;
4016 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4018 AmoebeUmwandeln(x, y);
4020 border_explosion = TRUE;
4023 /* if an element just explodes due to another explosion (chain-reaction),
4024 do not immediately end the new explosion when it was the last frame of
4025 the explosion (as it would be done in the following "if"-statement!) */
4026 if (border_explosion && phase == last_phase)
4030 if (phase == last_phase)
4034 element = Feld[x][y] = Store[x][y];
4035 Store[x][y] = Store2[x][y] = 0;
4036 GfxElement[x][y] = EL_UNDEFINED;
4038 /* player can escape from explosions and might therefore be still alive */
4039 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4040 element <= EL_PLAYER_IS_EXPLODING_4)
4042 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4043 int explosion_element = EL_PLAYER_1 + player_nr;
4044 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4045 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4047 if (level.use_explosion_element[player_nr])
4048 explosion_element = level.explosion_element[player_nr];
4050 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4051 element_info[explosion_element].content.e[xx][yy]);
4054 /* restore probably existing indestructible background element */
4055 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4056 element = Feld[x][y] = Back[x][y];
4059 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4060 GfxDir[x][y] = MV_NONE;
4061 ChangeDelay[x][y] = 0;
4062 ChangePage[x][y] = -1;
4064 #if USE_NEW_CUSTOM_VALUE
4065 CustomValue[x][y] = 0;
4068 InitField_WithBug2(x, y, FALSE);
4070 DrawLevelField(x, y);
4072 TestIfElementTouchesCustomElement(x, y);
4074 if (GFX_CRUMBLED(element))
4075 DrawLevelFieldCrumbledSandNeighbours(x, y);
4077 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4078 StorePlayer[x][y] = 0;
4080 if (ELEM_IS_PLAYER(element))
4081 RelocatePlayer(x, y, element);
4083 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4085 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4086 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4089 DrawLevelFieldCrumbledSand(x, y);
4091 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4093 DrawLevelElement(x, y, Back[x][y]);
4094 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4096 else if (IS_WALKABLE_UNDER(Back[x][y]))
4098 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4099 DrawLevelElementThruMask(x, y, Back[x][y]);
4101 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4102 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4106 void DynaExplode(int ex, int ey)
4109 int dynabomb_element = Feld[ex][ey];
4110 int dynabomb_size = 1;
4111 boolean dynabomb_xl = FALSE;
4112 struct PlayerInfo *player;
4113 static int xy[4][2] =
4121 if (IS_ACTIVE_BOMB(dynabomb_element))
4123 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4124 dynabomb_size = player->dynabomb_size;
4125 dynabomb_xl = player->dynabomb_xl;
4126 player->dynabombs_left++;
4129 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4131 for (i = 0; i < NUM_DIRECTIONS; i++)
4133 for (j = 1; j <= dynabomb_size; j++)
4135 int x = ex + j * xy[i][0];
4136 int y = ey + j * xy[i][1];
4139 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4142 element = Feld[x][y];
4144 /* do not restart explosions of fields with active bombs */
4145 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4148 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4150 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4151 !IS_DIGGABLE(element) && !dynabomb_xl)
4157 void Bang(int x, int y)
4159 int element = MovingOrBlocked2Element(x, y);
4160 int explosion_type = EX_TYPE_NORMAL;
4162 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4164 struct PlayerInfo *player = PLAYERINFO(x, y);
4166 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4167 player->element_nr);
4169 if (level.use_explosion_element[player->index_nr])
4171 int explosion_element = level.explosion_element[player->index_nr];
4173 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4174 explosion_type = EX_TYPE_CROSS;
4175 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4176 explosion_type = EX_TYPE_CENTER;
4184 case EL_BD_BUTTERFLY:
4187 case EL_DARK_YAMYAM:
4191 RaiseScoreElement(element);
4194 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4195 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4196 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4197 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4198 case EL_DYNABOMB_INCREASE_NUMBER:
4199 case EL_DYNABOMB_INCREASE_SIZE:
4200 case EL_DYNABOMB_INCREASE_POWER:
4201 explosion_type = EX_TYPE_DYNA;
4206 case EL_LAMP_ACTIVE:
4207 case EL_AMOEBA_TO_DIAMOND:
4208 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4209 explosion_type = EX_TYPE_CENTER;
4213 if (element_info[element].explosion_type == EXPLODES_CROSS)
4214 explosion_type = EX_TYPE_CROSS;
4215 else if (element_info[element].explosion_type == EXPLODES_1X1)
4216 explosion_type = EX_TYPE_CENTER;
4220 if (explosion_type == EX_TYPE_DYNA)
4223 Explode(x, y, EX_PHASE_START, explosion_type);
4225 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4228 void SplashAcid(int x, int y)
4230 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4231 (!IN_LEV_FIELD(x - 1, y - 2) ||
4232 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4233 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4235 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4236 (!IN_LEV_FIELD(x + 1, y - 2) ||
4237 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4238 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4240 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4243 static void InitBeltMovement()
4245 static int belt_base_element[4] =
4247 EL_CONVEYOR_BELT_1_LEFT,
4248 EL_CONVEYOR_BELT_2_LEFT,
4249 EL_CONVEYOR_BELT_3_LEFT,
4250 EL_CONVEYOR_BELT_4_LEFT
4252 static int belt_base_active_element[4] =
4254 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4255 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4256 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4257 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4262 /* set frame order for belt animation graphic according to belt direction */
4263 for (i = 0; i < NUM_BELTS; i++)
4267 for (j = 0; j < NUM_BELT_PARTS; j++)
4269 int element = belt_base_active_element[belt_nr] + j;
4270 int graphic = el2img(element);
4272 if (game.belt_dir[i] == MV_LEFT)
4273 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4275 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4280 SCAN_PLAYFIELD(x, y)
4282 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4285 int element = Feld[x][y];
4287 for (i = 0; i < NUM_BELTS; i++)
4289 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4291 int e_belt_nr = getBeltNrFromBeltElement(element);
4294 if (e_belt_nr == belt_nr)
4296 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4298 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4305 static void ToggleBeltSwitch(int x, int y)
4307 static int belt_base_element[4] =
4309 EL_CONVEYOR_BELT_1_LEFT,
4310 EL_CONVEYOR_BELT_2_LEFT,
4311 EL_CONVEYOR_BELT_3_LEFT,
4312 EL_CONVEYOR_BELT_4_LEFT
4314 static int belt_base_active_element[4] =
4316 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4317 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4318 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4319 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4321 static int belt_base_switch_element[4] =
4323 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4324 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4325 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4326 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4328 static int belt_move_dir[4] =
4336 int element = Feld[x][y];
4337 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4338 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4339 int belt_dir = belt_move_dir[belt_dir_nr];
4342 if (!IS_BELT_SWITCH(element))
4345 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4346 game.belt_dir[belt_nr] = belt_dir;
4348 if (belt_dir_nr == 3)
4351 /* set frame order for belt animation graphic according to belt direction */
4352 for (i = 0; i < NUM_BELT_PARTS; i++)
4354 int element = belt_base_active_element[belt_nr] + i;
4355 int graphic = el2img(element);
4357 if (belt_dir == MV_LEFT)
4358 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4360 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4364 SCAN_PLAYFIELD(xx, yy)
4366 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4369 int element = Feld[xx][yy];
4371 if (IS_BELT_SWITCH(element))
4373 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4375 if (e_belt_nr == belt_nr)
4377 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4378 DrawLevelField(xx, yy);
4381 else if (IS_BELT(element) && belt_dir != MV_NONE)
4383 int e_belt_nr = getBeltNrFromBeltElement(element);
4385 if (e_belt_nr == belt_nr)
4387 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4389 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4390 DrawLevelField(xx, yy);
4393 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4395 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4397 if (e_belt_nr == belt_nr)
4399 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4401 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4402 DrawLevelField(xx, yy);
4408 static void ToggleSwitchgateSwitch(int x, int y)
4412 game.switchgate_pos = !game.switchgate_pos;
4415 SCAN_PLAYFIELD(xx, yy)
4417 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4420 int element = Feld[xx][yy];
4422 if (element == EL_SWITCHGATE_SWITCH_UP ||
4423 element == EL_SWITCHGATE_SWITCH_DOWN)
4425 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4426 DrawLevelField(xx, yy);
4428 else if (element == EL_SWITCHGATE_OPEN ||
4429 element == EL_SWITCHGATE_OPENING)
4431 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4433 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4435 else if (element == EL_SWITCHGATE_CLOSED ||
4436 element == EL_SWITCHGATE_CLOSING)
4438 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4440 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4445 static int getInvisibleActiveFromInvisibleElement(int element)
4447 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4448 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4449 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4453 static int getInvisibleFromInvisibleActiveElement(int element)
4455 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4456 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4457 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4461 static void RedrawAllLightSwitchesAndInvisibleElements()
4466 SCAN_PLAYFIELD(x, y)
4468 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4471 int element = Feld[x][y];
4473 if (element == EL_LIGHT_SWITCH &&
4474 game.light_time_left > 0)
4476 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4477 DrawLevelField(x, y);
4479 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4480 game.light_time_left == 0)
4482 Feld[x][y] = EL_LIGHT_SWITCH;
4483 DrawLevelField(x, y);
4485 else if (element == EL_EMC_DRIPPER &&
4486 game.light_time_left > 0)
4488 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4489 DrawLevelField(x, y);
4491 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4492 game.light_time_left == 0)
4494 Feld[x][y] = EL_EMC_DRIPPER;
4495 DrawLevelField(x, y);
4497 else if (element == EL_INVISIBLE_STEELWALL ||
4498 element == EL_INVISIBLE_WALL ||
4499 element == EL_INVISIBLE_SAND)
4501 if (game.light_time_left > 0)
4502 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4504 DrawLevelField(x, y);
4506 /* uncrumble neighbour fields, if needed */
4507 if (element == EL_INVISIBLE_SAND)
4508 DrawLevelFieldCrumbledSandNeighbours(x, y);
4510 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4511 element == EL_INVISIBLE_WALL_ACTIVE ||
4512 element == EL_INVISIBLE_SAND_ACTIVE)
4514 if (game.light_time_left == 0)
4515 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4517 DrawLevelField(x, y);
4519 /* re-crumble neighbour fields, if needed */
4520 if (element == EL_INVISIBLE_SAND)
4521 DrawLevelFieldCrumbledSandNeighbours(x, y);
4526 static void RedrawAllInvisibleElementsForLenses()
4531 SCAN_PLAYFIELD(x, y)
4533 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4536 int element = Feld[x][y];
4538 if (element == EL_EMC_DRIPPER &&
4539 game.lenses_time_left > 0)
4541 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4542 DrawLevelField(x, y);
4544 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4545 game.lenses_time_left == 0)
4547 Feld[x][y] = EL_EMC_DRIPPER;
4548 DrawLevelField(x, y);
4550 else if (element == EL_INVISIBLE_STEELWALL ||
4551 element == EL_INVISIBLE_WALL ||
4552 element == EL_INVISIBLE_SAND)
4554 if (game.lenses_time_left > 0)
4555 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4557 DrawLevelField(x, y);
4559 /* uncrumble neighbour fields, if needed */
4560 if (element == EL_INVISIBLE_SAND)
4561 DrawLevelFieldCrumbledSandNeighbours(x, y);
4563 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4564 element == EL_INVISIBLE_WALL_ACTIVE ||
4565 element == EL_INVISIBLE_SAND_ACTIVE)
4567 if (game.lenses_time_left == 0)
4568 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4570 DrawLevelField(x, y);
4572 /* re-crumble neighbour fields, if needed */
4573 if (element == EL_INVISIBLE_SAND)
4574 DrawLevelFieldCrumbledSandNeighbours(x, y);
4579 static void RedrawAllInvisibleElementsForMagnifier()
4584 SCAN_PLAYFIELD(x, y)
4586 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4589 int element = Feld[x][y];
4591 if (element == EL_EMC_FAKE_GRASS &&
4592 game.magnify_time_left > 0)
4594 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4595 DrawLevelField(x, y);
4597 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4598 game.magnify_time_left == 0)
4600 Feld[x][y] = EL_EMC_FAKE_GRASS;
4601 DrawLevelField(x, y);
4603 else if (IS_GATE_GRAY(element) &&
4604 game.magnify_time_left > 0)
4606 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4607 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4608 IS_EM_GATE_GRAY(element) ?
4609 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4610 IS_EMC_GATE_GRAY(element) ?
4611 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4613 DrawLevelField(x, y);
4615 else if (IS_GATE_GRAY_ACTIVE(element) &&
4616 game.magnify_time_left == 0)
4618 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4619 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4620 IS_EM_GATE_GRAY_ACTIVE(element) ?
4621 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4622 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4623 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4625 DrawLevelField(x, y);
4630 static void ToggleLightSwitch(int x, int y)
4632 int element = Feld[x][y];
4634 game.light_time_left =
4635 (element == EL_LIGHT_SWITCH ?
4636 level.time_light * FRAMES_PER_SECOND : 0);
4638 RedrawAllLightSwitchesAndInvisibleElements();
4641 static void ActivateTimegateSwitch(int x, int y)
4645 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4648 SCAN_PLAYFIELD(xx, yy)
4650 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4653 int element = Feld[xx][yy];
4655 if (element == EL_TIMEGATE_CLOSED ||
4656 element == EL_TIMEGATE_CLOSING)
4658 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4659 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4663 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4665 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4666 DrawLevelField(xx, yy);
4672 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4675 void Impact(int x, int y)
4677 boolean last_line = (y == lev_fieldy - 1);
4678 boolean object_hit = FALSE;
4679 boolean impact = (last_line || object_hit);
4680 int element = Feld[x][y];
4681 int smashed = EL_STEELWALL;
4683 if (!last_line) /* check if element below was hit */
4685 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4688 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4689 MovDir[x][y + 1] != MV_DOWN ||
4690 MovPos[x][y + 1] <= TILEY / 2));
4692 /* do not smash moving elements that left the smashed field in time */
4693 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4694 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4697 #if USE_QUICKSAND_IMPACT_BUGFIX
4698 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4700 RemoveMovingField(x, y + 1);
4701 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4702 Feld[x][y + 2] = EL_ROCK;
4703 DrawLevelField(x, y + 2);
4710 smashed = MovingOrBlocked2Element(x, y + 1);
4712 impact = (last_line || object_hit);
4715 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4717 SplashAcid(x, y + 1);
4721 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4722 /* only reset graphic animation if graphic really changes after impact */
4724 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4726 ResetGfxAnimation(x, y);
4727 DrawLevelField(x, y);
4730 if (impact && CAN_EXPLODE_IMPACT(element))
4735 else if (impact && element == EL_PEARL)
4737 ResetGfxAnimation(x, y);
4739 Feld[x][y] = EL_PEARL_BREAKING;
4740 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4743 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4745 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4750 if (impact && element == EL_AMOEBA_DROP)
4752 if (object_hit && IS_PLAYER(x, y + 1))
4753 KillPlayerUnlessEnemyProtected(x, y + 1);
4754 else if (object_hit && smashed == EL_PENGUIN)
4758 Feld[x][y] = EL_AMOEBA_GROWING;
4759 Store[x][y] = EL_AMOEBA_WET;
4761 ResetRandomAnimationValue(x, y);
4766 if (object_hit) /* check which object was hit */
4768 if (CAN_PASS_MAGIC_WALL(element) &&
4769 (smashed == EL_MAGIC_WALL ||
4770 smashed == EL_BD_MAGIC_WALL))
4773 int activated_magic_wall =
4774 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4775 EL_BD_MAGIC_WALL_ACTIVE);
4777 /* activate magic wall / mill */
4779 SCAN_PLAYFIELD(xx, yy)
4781 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4783 if (Feld[xx][yy] == smashed)
4784 Feld[xx][yy] = activated_magic_wall;
4786 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4787 game.magic_wall_active = TRUE;
4789 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4790 SND_MAGIC_WALL_ACTIVATING :
4791 SND_BD_MAGIC_WALL_ACTIVATING));
4794 if (IS_PLAYER(x, y + 1))
4796 if (CAN_SMASH_PLAYER(element))
4798 KillPlayerUnlessEnemyProtected(x, y + 1);
4802 else if (smashed == EL_PENGUIN)
4804 if (CAN_SMASH_PLAYER(element))
4810 else if (element == EL_BD_DIAMOND)
4812 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4818 else if (((element == EL_SP_INFOTRON ||
4819 element == EL_SP_ZONK) &&
4820 (smashed == EL_SP_SNIKSNAK ||
4821 smashed == EL_SP_ELECTRON ||
4822 smashed == EL_SP_DISK_ORANGE)) ||
4823 (element == EL_SP_INFOTRON &&
4824 smashed == EL_SP_DISK_YELLOW))
4829 else if (CAN_SMASH_EVERYTHING(element))
4831 if (IS_CLASSIC_ENEMY(smashed) ||
4832 CAN_EXPLODE_SMASHED(smashed))
4837 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4839 if (smashed == EL_LAMP ||
4840 smashed == EL_LAMP_ACTIVE)
4845 else if (smashed == EL_NUT)
4847 Feld[x][y + 1] = EL_NUT_BREAKING;
4848 PlayLevelSound(x, y, SND_NUT_BREAKING);
4849 RaiseScoreElement(EL_NUT);
4852 else if (smashed == EL_PEARL)
4854 ResetGfxAnimation(x, y);
4856 Feld[x][y + 1] = EL_PEARL_BREAKING;
4857 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4860 else if (smashed == EL_DIAMOND)
4862 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4863 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4866 else if (IS_BELT_SWITCH(smashed))
4868 ToggleBeltSwitch(x, y + 1);
4870 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4871 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4873 ToggleSwitchgateSwitch(x, y + 1);
4875 else if (smashed == EL_LIGHT_SWITCH ||
4876 smashed == EL_LIGHT_SWITCH_ACTIVE)
4878 ToggleLightSwitch(x, y + 1);
4883 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4886 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4888 CheckElementChangeBySide(x, y + 1, smashed, element,
4889 CE_SWITCHED, CH_SIDE_TOP);
4890 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4896 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4901 /* play sound of magic wall / mill */
4903 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4904 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4906 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4907 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4908 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4909 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4914 /* play sound of object that hits the ground */
4915 if (last_line || object_hit)
4916 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4919 inline static void TurnRoundExt(int x, int y)
4931 { 0, 0 }, { 0, 0 }, { 0, 0 },
4936 int left, right, back;
4940 { MV_DOWN, MV_UP, MV_RIGHT },
4941 { MV_UP, MV_DOWN, MV_LEFT },
4943 { MV_LEFT, MV_RIGHT, MV_DOWN },
4947 { MV_RIGHT, MV_LEFT, MV_UP }
4950 int element = Feld[x][y];
4951 int move_pattern = element_info[element].move_pattern;
4953 int old_move_dir = MovDir[x][y];
4954 int left_dir = turn[old_move_dir].left;
4955 int right_dir = turn[old_move_dir].right;
4956 int back_dir = turn[old_move_dir].back;
4958 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4959 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4960 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4961 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4963 int left_x = x + left_dx, left_y = y + left_dy;
4964 int right_x = x + right_dx, right_y = y + right_dy;
4965 int move_x = x + move_dx, move_y = y + move_dy;
4969 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4971 TestIfBadThingTouchesOtherBadThing(x, y);
4973 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4974 MovDir[x][y] = right_dir;
4975 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4976 MovDir[x][y] = left_dir;
4978 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4980 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4983 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4985 TestIfBadThingTouchesOtherBadThing(x, y);
4987 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4988 MovDir[x][y] = left_dir;
4989 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4990 MovDir[x][y] = right_dir;
4992 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4994 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4997 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4999 TestIfBadThingTouchesOtherBadThing(x, y);
5001 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5002 MovDir[x][y] = left_dir;
5003 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5004 MovDir[x][y] = right_dir;
5006 if (MovDir[x][y] != old_move_dir)
5009 else if (element == EL_YAMYAM)
5011 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5012 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5014 if (can_turn_left && can_turn_right)
5015 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5016 else if (can_turn_left)
5017 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5018 else if (can_turn_right)
5019 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5021 MovDir[x][y] = back_dir;
5023 MovDelay[x][y] = 16 + 16 * RND(3);
5025 else if (element == EL_DARK_YAMYAM)
5027 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5029 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5032 if (can_turn_left && can_turn_right)
5033 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5034 else if (can_turn_left)
5035 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5036 else if (can_turn_right)
5037 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5039 MovDir[x][y] = back_dir;
5041 MovDelay[x][y] = 16 + 16 * RND(3);
5043 else if (element == EL_PACMAN)
5045 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5046 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5048 if (can_turn_left && can_turn_right)
5049 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5050 else if (can_turn_left)
5051 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5052 else if (can_turn_right)
5053 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5055 MovDir[x][y] = back_dir;
5057 MovDelay[x][y] = 6 + RND(40);
5059 else if (element == EL_PIG)
5061 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5062 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5063 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5064 boolean should_turn_left, should_turn_right, should_move_on;
5066 int rnd = RND(rnd_value);
5068 should_turn_left = (can_turn_left &&
5070 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5071 y + back_dy + left_dy)));
5072 should_turn_right = (can_turn_right &&
5074 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5075 y + back_dy + right_dy)));
5076 should_move_on = (can_move_on &&
5079 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5080 y + move_dy + left_dy) ||
5081 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5082 y + move_dy + right_dy)));
5084 if (should_turn_left || should_turn_right || should_move_on)
5086 if (should_turn_left && should_turn_right && should_move_on)
5087 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5088 rnd < 2 * rnd_value / 3 ? right_dir :
5090 else if (should_turn_left && should_turn_right)
5091 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5092 else if (should_turn_left && should_move_on)
5093 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5094 else if (should_turn_right && should_move_on)
5095 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5096 else if (should_turn_left)
5097 MovDir[x][y] = left_dir;
5098 else if (should_turn_right)
5099 MovDir[x][y] = right_dir;
5100 else if (should_move_on)
5101 MovDir[x][y] = old_move_dir;
5103 else if (can_move_on && rnd > rnd_value / 8)
5104 MovDir[x][y] = old_move_dir;
5105 else if (can_turn_left && can_turn_right)
5106 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5107 else if (can_turn_left && rnd > rnd_value / 8)
5108 MovDir[x][y] = left_dir;
5109 else if (can_turn_right && rnd > rnd_value/8)
5110 MovDir[x][y] = right_dir;
5112 MovDir[x][y] = back_dir;
5114 xx = x + move_xy[MovDir[x][y]].dx;
5115 yy = y + move_xy[MovDir[x][y]].dy;
5117 if (!IN_LEV_FIELD(xx, yy) ||
5118 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5119 MovDir[x][y] = old_move_dir;
5123 else if (element == EL_DRAGON)
5125 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5126 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5127 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5129 int rnd = RND(rnd_value);
5131 if (can_move_on && rnd > rnd_value / 8)
5132 MovDir[x][y] = old_move_dir;
5133 else if (can_turn_left && can_turn_right)
5134 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5135 else if (can_turn_left && rnd > rnd_value / 8)
5136 MovDir[x][y] = left_dir;
5137 else if (can_turn_right && rnd > rnd_value / 8)
5138 MovDir[x][y] = right_dir;
5140 MovDir[x][y] = back_dir;
5142 xx = x + move_xy[MovDir[x][y]].dx;
5143 yy = y + move_xy[MovDir[x][y]].dy;
5145 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5146 MovDir[x][y] = old_move_dir;
5150 else if (element == EL_MOLE)
5152 boolean can_move_on =
5153 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5154 IS_AMOEBOID(Feld[move_x][move_y]) ||
5155 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5158 boolean can_turn_left =
5159 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5160 IS_AMOEBOID(Feld[left_x][left_y])));
5162 boolean can_turn_right =
5163 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5164 IS_AMOEBOID(Feld[right_x][right_y])));
5166 if (can_turn_left && can_turn_right)
5167 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5168 else if (can_turn_left)
5169 MovDir[x][y] = left_dir;
5171 MovDir[x][y] = right_dir;
5174 if (MovDir[x][y] != old_move_dir)
5177 else if (element == EL_BALLOON)
5179 MovDir[x][y] = game.wind_direction;
5182 else if (element == EL_SPRING)
5184 #if USE_NEW_SPRING_BUMPER
5185 if (MovDir[x][y] & MV_HORIZONTAL)
5187 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5188 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5190 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5191 ResetGfxAnimation(move_x, move_y);
5192 DrawLevelField(move_x, move_y);
5194 MovDir[x][y] = back_dir;
5196 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5197 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5198 MovDir[x][y] = MV_NONE;
5201 if (MovDir[x][y] & MV_HORIZONTAL &&
5202 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5203 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5204 MovDir[x][y] = MV_NONE;
5209 else if (element == EL_ROBOT ||
5210 element == EL_SATELLITE ||
5211 element == EL_PENGUIN ||
5212 element == EL_EMC_ANDROID)
5214 int attr_x = -1, attr_y = -1;
5225 for (i = 0; i < MAX_PLAYERS; i++)
5227 struct PlayerInfo *player = &stored_player[i];
5228 int jx = player->jx, jy = player->jy;
5230 if (!player->active)
5234 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5242 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5243 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5244 game.engine_version < VERSION_IDENT(3,1,0,0)))
5250 if (element == EL_PENGUIN)
5253 static int xy[4][2] =
5261 for (i = 0; i < NUM_DIRECTIONS; i++)
5263 int ex = x + xy[i][0];
5264 int ey = y + xy[i][1];
5266 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5275 MovDir[x][y] = MV_NONE;
5277 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5278 else if (attr_x > x)
5279 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5281 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5282 else if (attr_y > y)
5283 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5285 if (element == EL_ROBOT)
5289 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5290 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5291 Moving2Blocked(x, y, &newx, &newy);
5293 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5294 MovDelay[x][y] = 8 + 8 * !RND(3);
5296 MovDelay[x][y] = 16;
5298 else if (element == EL_PENGUIN)
5304 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5306 boolean first_horiz = RND(2);
5307 int new_move_dir = MovDir[x][y];
5310 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5311 Moving2Blocked(x, y, &newx, &newy);
5313 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5317 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5318 Moving2Blocked(x, y, &newx, &newy);
5320 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5323 MovDir[x][y] = old_move_dir;
5327 else if (element == EL_SATELLITE)
5333 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5335 boolean first_horiz = RND(2);
5336 int new_move_dir = MovDir[x][y];
5339 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5340 Moving2Blocked(x, y, &newx, &newy);
5342 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5346 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5347 Moving2Blocked(x, y, &newx, &newy);
5349 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5352 MovDir[x][y] = old_move_dir;
5356 else if (element == EL_EMC_ANDROID)
5358 static int check_pos[16] =
5360 -1, /* 0 => (invalid) */
5361 7, /* 1 => MV_LEFT */
5362 3, /* 2 => MV_RIGHT */
5363 -1, /* 3 => (invalid) */
5365 0, /* 5 => MV_LEFT | MV_UP */
5366 2, /* 6 => MV_RIGHT | MV_UP */
5367 -1, /* 7 => (invalid) */
5368 5, /* 8 => MV_DOWN */
5369 6, /* 9 => MV_LEFT | MV_DOWN */
5370 4, /* 10 => MV_RIGHT | MV_DOWN */
5371 -1, /* 11 => (invalid) */
5372 -1, /* 12 => (invalid) */
5373 -1, /* 13 => (invalid) */
5374 -1, /* 14 => (invalid) */
5375 -1, /* 15 => (invalid) */
5383 { -1, -1, MV_LEFT | MV_UP },
5385 { +1, -1, MV_RIGHT | MV_UP },
5386 { +1, 0, MV_RIGHT },
5387 { +1, +1, MV_RIGHT | MV_DOWN },
5389 { -1, +1, MV_LEFT | MV_DOWN },
5392 int start_pos, check_order;
5393 boolean can_clone = FALSE;
5396 /* check if there is any free field around current position */
5397 for (i = 0; i < 8; i++)
5399 int newx = x + check_xy[i].dx;
5400 int newy = y + check_xy[i].dy;
5402 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5410 if (can_clone) /* randomly find an element to clone */
5414 start_pos = check_pos[RND(8)];
5415 check_order = (RND(2) ? -1 : +1);
5417 for (i = 0; i < 8; i++)
5419 int pos_raw = start_pos + i * check_order;
5420 int pos = (pos_raw + 8) % 8;
5421 int newx = x + check_xy[pos].dx;
5422 int newy = y + check_xy[pos].dy;
5424 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5426 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5427 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5429 Store[x][y] = Feld[newx][newy];
5438 if (can_clone) /* randomly find a direction to move */
5442 start_pos = check_pos[RND(8)];
5443 check_order = (RND(2) ? -1 : +1);
5445 for (i = 0; i < 8; i++)
5447 int pos_raw = start_pos + i * check_order;
5448 int pos = (pos_raw + 8) % 8;
5449 int newx = x + check_xy[pos].dx;
5450 int newy = y + check_xy[pos].dy;
5451 int new_move_dir = check_xy[pos].dir;
5453 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5455 MovDir[x][y] = new_move_dir;
5456 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5465 if (can_clone) /* cloning and moving successful */
5468 /* cannot clone -- try to move towards player */
5470 start_pos = check_pos[MovDir[x][y] & 0x0f];
5471 check_order = (RND(2) ? -1 : +1);
5473 for (i = 0; i < 3; i++)
5475 /* first check start_pos, then previous/next or (next/previous) pos */
5476 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5477 int pos = (pos_raw + 8) % 8;
5478 int newx = x + check_xy[pos].dx;
5479 int newy = y + check_xy[pos].dy;
5480 int new_move_dir = check_xy[pos].dir;
5482 if (IS_PLAYER(newx, newy))
5485 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5487 MovDir[x][y] = new_move_dir;
5488 MovDelay[x][y] = level.android_move_time * 8 + 1;
5495 else if (move_pattern == MV_TURNING_LEFT ||
5496 move_pattern == MV_TURNING_RIGHT ||
5497 move_pattern == MV_TURNING_LEFT_RIGHT ||
5498 move_pattern == MV_TURNING_RIGHT_LEFT ||
5499 move_pattern == MV_TURNING_RANDOM ||
5500 move_pattern == MV_ALL_DIRECTIONS)
5502 boolean can_turn_left =
5503 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5504 boolean can_turn_right =
5505 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5507 if (element_info[element].move_stepsize == 0) /* "not moving" */
5510 if (move_pattern == MV_TURNING_LEFT)
5511 MovDir[x][y] = left_dir;
5512 else if (move_pattern == MV_TURNING_RIGHT)
5513 MovDir[x][y] = right_dir;
5514 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5515 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5516 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5517 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5518 else if (move_pattern == MV_TURNING_RANDOM)
5519 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5520 can_turn_right && !can_turn_left ? right_dir :
5521 RND(2) ? left_dir : right_dir);
5522 else if (can_turn_left && can_turn_right)
5523 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5524 else if (can_turn_left)
5525 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5526 else if (can_turn_right)
5527 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5529 MovDir[x][y] = back_dir;
5531 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5533 else if (move_pattern == MV_HORIZONTAL ||
5534 move_pattern == MV_VERTICAL)
5536 if (move_pattern & old_move_dir)
5537 MovDir[x][y] = back_dir;
5538 else if (move_pattern == MV_HORIZONTAL)
5539 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5540 else if (move_pattern == MV_VERTICAL)
5541 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5543 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5545 else if (move_pattern & MV_ANY_DIRECTION)
5547 MovDir[x][y] = move_pattern;
5548 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5550 else if (move_pattern & MV_WIND_DIRECTION)
5552 MovDir[x][y] = game.wind_direction;
5553 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5555 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5557 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5558 MovDir[x][y] = left_dir;
5559 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5560 MovDir[x][y] = right_dir;
5562 if (MovDir[x][y] != old_move_dir)
5563 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5565 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5567 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5568 MovDir[x][y] = right_dir;
5569 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5570 MovDir[x][y] = left_dir;
5572 if (MovDir[x][y] != old_move_dir)
5573 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5575 else if (move_pattern == MV_TOWARDS_PLAYER ||
5576 move_pattern == MV_AWAY_FROM_PLAYER)
5578 int attr_x = -1, attr_y = -1;
5580 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5591 for (i = 0; i < MAX_PLAYERS; i++)
5593 struct PlayerInfo *player = &stored_player[i];
5594 int jx = player->jx, jy = player->jy;
5596 if (!player->active)
5600 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5608 MovDir[x][y] = MV_NONE;
5610 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5611 else if (attr_x > x)
5612 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5614 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5615 else if (attr_y > y)
5616 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5618 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5620 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5622 boolean first_horiz = RND(2);
5623 int new_move_dir = MovDir[x][y];
5625 if (element_info[element].move_stepsize == 0) /* "not moving" */
5627 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5628 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5634 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5635 Moving2Blocked(x, y, &newx, &newy);
5637 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5641 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5642 Moving2Blocked(x, y, &newx, &newy);
5644 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5647 MovDir[x][y] = old_move_dir;
5650 else if (move_pattern == MV_WHEN_PUSHED ||
5651 move_pattern == MV_WHEN_DROPPED)
5653 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5654 MovDir[x][y] = MV_NONE;
5658 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5660 static int test_xy[7][2] =
5670 static int test_dir[7] =
5680 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5681 int move_preference = -1000000; /* start with very low preference */
5682 int new_move_dir = MV_NONE;
5683 int start_test = RND(4);
5686 for (i = 0; i < NUM_DIRECTIONS; i++)
5688 int move_dir = test_dir[start_test + i];
5689 int move_dir_preference;
5691 xx = x + test_xy[start_test + i][0];
5692 yy = y + test_xy[start_test + i][1];
5694 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5695 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5697 new_move_dir = move_dir;
5702 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5705 move_dir_preference = -1 * RunnerVisit[xx][yy];
5706 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5707 move_dir_preference = PlayerVisit[xx][yy];
5709 if (move_dir_preference > move_preference)
5711 /* prefer field that has not been visited for the longest time */
5712 move_preference = move_dir_preference;
5713 new_move_dir = move_dir;
5715 else if (move_dir_preference == move_preference &&
5716 move_dir == old_move_dir)
5718 /* prefer last direction when all directions are preferred equally */
5719 move_preference = move_dir_preference;
5720 new_move_dir = move_dir;
5724 MovDir[x][y] = new_move_dir;
5725 if (old_move_dir != new_move_dir)
5726 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5730 static void TurnRound(int x, int y)
5732 int direction = MovDir[x][y];
5734 int element, graphic;
5739 GfxDir[x][y] = MovDir[x][y];
5741 if (direction != MovDir[x][y])
5745 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5748 ResetGfxFrame(x, y, FALSE);
5750 element = Feld[x][y];
5751 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5753 if (graphic_info[graphic].anim_global_sync)
5754 GfxFrame[x][y] = FrameCounter;
5755 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5756 GfxFrame[x][y] = CustomValue[x][y];
5757 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5758 GfxFrame[x][y] = element_info[element].collect_score;
5762 static boolean JustBeingPushed(int x, int y)
5766 for (i = 0; i < MAX_PLAYERS; i++)
5768 struct PlayerInfo *player = &stored_player[i];
5770 if (player->active && player->is_pushing && player->MovPos)
5772 int next_jx = player->jx + (player->jx - player->last_jx);
5773 int next_jy = player->jy + (player->jy - player->last_jy);
5775 if (x == next_jx && y == next_jy)
5783 void StartMoving(int x, int y)
5785 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5786 int element = Feld[x][y];
5791 if (MovDelay[x][y] == 0)
5792 GfxAction[x][y] = ACTION_DEFAULT;
5794 if (CAN_FALL(element) && y < lev_fieldy - 1)
5796 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5797 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5798 if (JustBeingPushed(x, y))
5801 if (element == EL_QUICKSAND_FULL)
5803 if (IS_FREE(x, y + 1))
5805 InitMovingField(x, y, MV_DOWN);
5806 started_moving = TRUE;
5808 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5809 Store[x][y] = EL_ROCK;
5811 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5813 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5815 if (!MovDelay[x][y])
5816 MovDelay[x][y] = TILEY + 1;
5825 Feld[x][y] = EL_QUICKSAND_EMPTY;
5826 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5827 Store[x][y + 1] = Store[x][y];
5830 PlayLevelSoundAction(x, y, ACTION_FILLING);
5833 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5834 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5836 InitMovingField(x, y, MV_DOWN);
5837 started_moving = TRUE;
5839 Feld[x][y] = EL_QUICKSAND_FILLING;
5840 Store[x][y] = element;
5842 PlayLevelSoundAction(x, y, ACTION_FILLING);
5844 else if (element == EL_MAGIC_WALL_FULL)
5846 if (IS_FREE(x, y + 1))
5848 InitMovingField(x, y, MV_DOWN);
5849 started_moving = TRUE;
5851 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5852 Store[x][y] = EL_CHANGED(Store[x][y]);
5854 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5856 if (!MovDelay[x][y])
5857 MovDelay[x][y] = TILEY/4 + 1;
5866 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5867 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5868 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5872 else if (element == EL_BD_MAGIC_WALL_FULL)
5874 if (IS_FREE(x, y + 1))
5876 InitMovingField(x, y, MV_DOWN);
5877 started_moving = TRUE;
5879 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5880 Store[x][y] = EL_CHANGED2(Store[x][y]);
5882 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5884 if (!MovDelay[x][y])
5885 MovDelay[x][y] = TILEY/4 + 1;
5894 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5895 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5896 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5900 else if (CAN_PASS_MAGIC_WALL(element) &&
5901 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5902 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5904 InitMovingField(x, y, MV_DOWN);
5905 started_moving = TRUE;
5908 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5909 EL_BD_MAGIC_WALL_FILLING);
5910 Store[x][y] = element;
5912 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5914 SplashAcid(x, y + 1);
5916 InitMovingField(x, y, MV_DOWN);
5917 started_moving = TRUE;
5919 Store[x][y] = EL_ACID;
5921 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5922 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5924 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5925 CAN_FALL(element) && WasJustFalling[x][y] &&
5926 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5928 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5929 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5930 (Feld[x][y + 1] == EL_BLOCKED)))
5932 /* this is needed for a special case not covered by calling "Impact()"
5933 from "ContinueMoving()": if an element moves to a tile directly below
5934 another element which was just falling on that tile (which was empty
5935 in the previous frame), the falling element above would just stop
5936 instead of smashing the element below (in previous version, the above
5937 element was just checked for "moving" instead of "falling", resulting
5938 in incorrect smashes caused by horizontal movement of the above
5939 element; also, the case of the player being the element to smash was
5940 simply not covered here... :-/ ) */
5942 CheckCollision[x][y] = 0;
5946 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5948 if (MovDir[x][y] == MV_NONE)
5950 InitMovingField(x, y, MV_DOWN);
5951 started_moving = TRUE;
5954 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5956 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5957 MovDir[x][y] = MV_DOWN;
5959 InitMovingField(x, y, MV_DOWN);
5960 started_moving = TRUE;
5962 else if (element == EL_AMOEBA_DROP)
5964 Feld[x][y] = EL_AMOEBA_GROWING;
5965 Store[x][y] = EL_AMOEBA_WET;
5967 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5968 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5969 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5970 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5972 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5973 (IS_FREE(x - 1, y + 1) ||
5974 Feld[x - 1][y + 1] == EL_ACID));
5975 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5976 (IS_FREE(x + 1, y + 1) ||
5977 Feld[x + 1][y + 1] == EL_ACID));
5978 boolean can_fall_any = (can_fall_left || can_fall_right);
5979 boolean can_fall_both = (can_fall_left && can_fall_right);
5980 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5982 #if USE_NEW_ALL_SLIPPERY
5983 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5985 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5986 can_fall_right = FALSE;
5987 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5988 can_fall_left = FALSE;
5989 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5990 can_fall_right = FALSE;
5991 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5992 can_fall_left = FALSE;
5994 can_fall_any = (can_fall_left || can_fall_right);
5995 can_fall_both = FALSE;
5998 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6000 if (slippery_type == SLIPPERY_ONLY_LEFT)
6001 can_fall_right = FALSE;
6002 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6003 can_fall_left = FALSE;
6004 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6005 can_fall_right = FALSE;
6006 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6007 can_fall_left = FALSE;
6009 can_fall_any = (can_fall_left || can_fall_right);
6010 can_fall_both = (can_fall_left && can_fall_right);
6014 #if USE_NEW_ALL_SLIPPERY
6016 #if USE_NEW_SP_SLIPPERY
6017 /* !!! better use the same properties as for custom elements here !!! */
6018 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6019 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6021 can_fall_right = FALSE; /* slip down on left side */
6022 can_fall_both = FALSE;
6027 #if USE_NEW_ALL_SLIPPERY
6030 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6031 can_fall_right = FALSE; /* slip down on left side */
6033 can_fall_left = !(can_fall_right = RND(2));
6035 can_fall_both = FALSE;
6040 if (game.emulation == EMU_BOULDERDASH ||
6041 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6042 can_fall_right = FALSE; /* slip down on left side */
6044 can_fall_left = !(can_fall_right = RND(2));
6046 can_fall_both = FALSE;
6052 /* if not determined otherwise, prefer left side for slipping down */
6053 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6054 started_moving = TRUE;
6058 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6060 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6063 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6064 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6065 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6066 int belt_dir = game.belt_dir[belt_nr];
6068 if ((belt_dir == MV_LEFT && left_is_free) ||
6069 (belt_dir == MV_RIGHT && right_is_free))
6071 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6073 InitMovingField(x, y, belt_dir);
6074 started_moving = TRUE;
6076 Pushed[x][y] = TRUE;
6077 Pushed[nextx][y] = TRUE;
6079 GfxAction[x][y] = ACTION_DEFAULT;
6083 MovDir[x][y] = 0; /* if element was moving, stop it */
6088 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6090 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6092 if (CAN_MOVE(element) && !started_moving)
6095 int move_pattern = element_info[element].move_pattern;
6100 if (MovDir[x][y] == MV_NONE)
6102 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6103 x, y, element, element_info[element].token_name);
6104 printf("StartMoving(): This should never happen!\n");
6109 Moving2Blocked(x, y, &newx, &newy);
6111 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6114 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6115 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6117 WasJustMoving[x][y] = 0;
6118 CheckCollision[x][y] = 0;
6120 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6122 if (Feld[x][y] != element) /* element has changed */
6126 if (!MovDelay[x][y]) /* start new movement phase */
6128 /* all objects that can change their move direction after each step
6129 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6131 if (element != EL_YAMYAM &&
6132 element != EL_DARK_YAMYAM &&
6133 element != EL_PACMAN &&
6134 !(move_pattern & MV_ANY_DIRECTION) &&
6135 move_pattern != MV_TURNING_LEFT &&
6136 move_pattern != MV_TURNING_RIGHT &&
6137 move_pattern != MV_TURNING_LEFT_RIGHT &&
6138 move_pattern != MV_TURNING_RIGHT_LEFT &&
6139 move_pattern != MV_TURNING_RANDOM)
6143 if (MovDelay[x][y] && (element == EL_BUG ||
6144 element == EL_SPACESHIP ||
6145 element == EL_SP_SNIKSNAK ||
6146 element == EL_SP_ELECTRON ||
6147 element == EL_MOLE))
6148 DrawLevelField(x, y);
6152 if (MovDelay[x][y]) /* wait some time before next movement */
6156 if (element == EL_ROBOT ||
6157 element == EL_YAMYAM ||
6158 element == EL_DARK_YAMYAM)
6160 DrawLevelElementAnimationIfNeeded(x, y, element);
6161 PlayLevelSoundAction(x, y, ACTION_WAITING);
6163 else if (element == EL_SP_ELECTRON)
6164 DrawLevelElementAnimationIfNeeded(x, y, element);
6165 else if (element == EL_DRAGON)
6168 int dir = MovDir[x][y];
6169 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6170 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6171 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6172 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6173 dir == MV_UP ? IMG_FLAMES_1_UP :
6174 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6175 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6177 GfxAction[x][y] = ACTION_ATTACKING;
6179 if (IS_PLAYER(x, y))
6180 DrawPlayerField(x, y);
6182 DrawLevelField(x, y);
6184 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6186 for (i = 1; i <= 3; i++)
6188 int xx = x + i * dx;
6189 int yy = y + i * dy;
6190 int sx = SCREENX(xx);
6191 int sy = SCREENY(yy);
6192 int flame_graphic = graphic + (i - 1);
6194 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6199 int flamed = MovingOrBlocked2Element(xx, yy);
6203 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6205 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6206 RemoveMovingField(xx, yy);
6208 RemoveField(xx, yy);
6210 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6213 RemoveMovingField(xx, yy);
6216 ChangeDelay[xx][yy] = 0;
6218 Feld[xx][yy] = EL_FLAMES;
6220 if (IN_SCR_FIELD(sx, sy))
6222 DrawLevelFieldCrumbledSand(xx, yy);
6223 DrawGraphic(sx, sy, flame_graphic, frame);
6228 if (Feld[xx][yy] == EL_FLAMES)
6229 Feld[xx][yy] = EL_EMPTY;
6230 DrawLevelField(xx, yy);
6235 if (MovDelay[x][y]) /* element still has to wait some time */
6237 PlayLevelSoundAction(x, y, ACTION_WAITING);
6243 /* now make next step */
6245 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6247 if (DONT_COLLIDE_WITH(element) &&
6248 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6249 !PLAYER_ENEMY_PROTECTED(newx, newy))
6251 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6256 else if (CAN_MOVE_INTO_ACID(element) &&
6257 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6258 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6259 (MovDir[x][y] == MV_DOWN ||
6260 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6262 SplashAcid(newx, newy);
6263 Store[x][y] = EL_ACID;
6265 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6267 if (Feld[newx][newy] == EL_EXIT_OPEN)
6270 DrawLevelField(x, y);
6272 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6273 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6274 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6276 local_player->friends_still_needed--;
6277 if (!local_player->friends_still_needed &&
6278 !local_player->GameOver && AllPlayersGone)
6279 local_player->LevelSolved = local_player->GameOver = TRUE;
6283 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6285 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6286 DrawLevelField(newx, newy);
6288 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6290 else if (!IS_FREE(newx, newy))
6292 GfxAction[x][y] = ACTION_WAITING;
6294 if (IS_PLAYER(x, y))
6295 DrawPlayerField(x, y);
6297 DrawLevelField(x, y);
6302 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6304 if (IS_FOOD_PIG(Feld[newx][newy]))
6306 if (IS_MOVING(newx, newy))
6307 RemoveMovingField(newx, newy);
6310 Feld[newx][newy] = EL_EMPTY;
6311 DrawLevelField(newx, newy);
6314 PlayLevelSound(x, y, SND_PIG_DIGGING);
6316 else if (!IS_FREE(newx, newy))
6318 if (IS_PLAYER(x, y))
6319 DrawPlayerField(x, y);
6321 DrawLevelField(x, y);
6326 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6328 if (Store[x][y] != EL_EMPTY)
6330 boolean can_clone = FALSE;
6333 /* check if element to clone is still there */
6334 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6336 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6344 /* cannot clone or target field not free anymore -- do not clone */
6345 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6346 Store[x][y] = EL_EMPTY;
6349 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6351 if (IS_MV_DIAGONAL(MovDir[x][y]))
6353 int diagonal_move_dir = MovDir[x][y];
6354 int stored = Store[x][y];
6355 int change_delay = 8;
6358 /* android is moving diagonally */
6360 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6362 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6363 GfxElement[x][y] = EL_EMC_ANDROID;
6364 GfxAction[x][y] = ACTION_SHRINKING;
6365 GfxDir[x][y] = diagonal_move_dir;
6366 ChangeDelay[x][y] = change_delay;
6368 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6371 DrawLevelGraphicAnimation(x, y, graphic);
6372 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6374 if (Feld[newx][newy] == EL_ACID)
6376 SplashAcid(newx, newy);
6381 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6383 Store[newx][newy] = EL_EMC_ANDROID;
6384 GfxElement[newx][newy] = EL_EMC_ANDROID;
6385 GfxAction[newx][newy] = ACTION_GROWING;
6386 GfxDir[newx][newy] = diagonal_move_dir;
6387 ChangeDelay[newx][newy] = change_delay;
6389 graphic = el_act_dir2img(GfxElement[newx][newy],
6390 GfxAction[newx][newy], GfxDir[newx][newy]);
6392 DrawLevelGraphicAnimation(newx, newy, graphic);
6393 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6399 Feld[newx][newy] = EL_EMPTY;
6400 DrawLevelField(newx, newy);
6402 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6405 else if (!IS_FREE(newx, newy))
6408 if (IS_PLAYER(x, y))
6409 DrawPlayerField(x, y);
6411 DrawLevelField(x, y);
6417 else if (IS_CUSTOM_ELEMENT(element) &&
6418 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6420 int new_element = Feld[newx][newy];
6422 if (!IS_FREE(newx, newy))
6424 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6425 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6428 /* no element can dig solid indestructible elements */
6429 if (IS_INDESTRUCTIBLE(new_element) &&
6430 !IS_DIGGABLE(new_element) &&
6431 !IS_COLLECTIBLE(new_element))
6434 if (AmoebaNr[newx][newy] &&
6435 (new_element == EL_AMOEBA_FULL ||
6436 new_element == EL_BD_AMOEBA ||
6437 new_element == EL_AMOEBA_GROWING))
6439 AmoebaCnt[AmoebaNr[newx][newy]]--;
6440 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6443 if (IS_MOVING(newx, newy))
6444 RemoveMovingField(newx, newy);
6447 RemoveField(newx, newy);
6448 DrawLevelField(newx, newy);
6451 /* if digged element was about to explode, prevent the explosion */
6452 ExplodeField[newx][newy] = EX_TYPE_NONE;
6454 PlayLevelSoundAction(x, y, action);
6457 Store[newx][newy] = EL_EMPTY;
6459 /* this makes it possible to leave the removed element again */
6460 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6461 Store[newx][newy] = new_element;
6463 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6465 int move_leave_element = element_info[element].move_leave_element;
6467 /* this makes it possible to leave the removed element again */
6468 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6469 new_element : move_leave_element);
6473 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6475 RunnerVisit[x][y] = FrameCounter;
6476 PlayerVisit[x][y] /= 8; /* expire player visit path */
6479 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6481 if (!IS_FREE(newx, newy))
6483 if (IS_PLAYER(x, y))
6484 DrawPlayerField(x, y);
6486 DrawLevelField(x, y);
6492 boolean wanna_flame = !RND(10);
6493 int dx = newx - x, dy = newy - y;
6494 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6495 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6496 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6497 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6498 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6499 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6502 IS_CLASSIC_ENEMY(element1) ||
6503 IS_CLASSIC_ENEMY(element2)) &&
6504 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6505 element1 != EL_FLAMES && element2 != EL_FLAMES)
6507 ResetGfxAnimation(x, y);
6508 GfxAction[x][y] = ACTION_ATTACKING;
6510 if (IS_PLAYER(x, y))
6511 DrawPlayerField(x, y);
6513 DrawLevelField(x, y);
6515 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6517 MovDelay[x][y] = 50;
6521 RemoveField(newx, newy);
6523 Feld[newx][newy] = EL_FLAMES;
6524 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6527 RemoveField(newx1, newy1);
6529 Feld[newx1][newy1] = EL_FLAMES;
6531 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6534 RemoveField(newx2, newy2);
6536 Feld[newx2][newy2] = EL_FLAMES;
6543 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6544 Feld[newx][newy] == EL_DIAMOND)
6546 if (IS_MOVING(newx, newy))
6547 RemoveMovingField(newx, newy);
6550 Feld[newx][newy] = EL_EMPTY;
6551 DrawLevelField(newx, newy);
6554 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6556 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6557 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6559 if (AmoebaNr[newx][newy])
6561 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6562 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6563 Feld[newx][newy] == EL_BD_AMOEBA)
6564 AmoebaCnt[AmoebaNr[newx][newy]]--;
6569 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6571 RemoveMovingField(newx, newy);
6574 if (IS_MOVING(newx, newy))
6576 RemoveMovingField(newx, newy);
6581 Feld[newx][newy] = EL_EMPTY;
6582 DrawLevelField(newx, newy);
6585 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6587 else if ((element == EL_PACMAN || element == EL_MOLE)
6588 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6590 if (AmoebaNr[newx][newy])
6592 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6593 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6594 Feld[newx][newy] == EL_BD_AMOEBA)
6595 AmoebaCnt[AmoebaNr[newx][newy]]--;
6598 if (element == EL_MOLE)
6600 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6601 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6603 ResetGfxAnimation(x, y);
6604 GfxAction[x][y] = ACTION_DIGGING;
6605 DrawLevelField(x, y);
6607 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6609 return; /* wait for shrinking amoeba */
6611 else /* element == EL_PACMAN */
6613 Feld[newx][newy] = EL_EMPTY;
6614 DrawLevelField(newx, newy);
6615 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6618 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6619 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6620 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6622 /* wait for shrinking amoeba to completely disappear */
6625 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6627 /* object was running against a wall */
6632 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6633 if (move_pattern & MV_ANY_DIRECTION &&
6634 move_pattern == MovDir[x][y])
6636 int blocking_element =
6637 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6639 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6642 element = Feld[x][y]; /* element might have changed */
6646 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6647 DrawLevelElementAnimation(x, y, element);
6649 if (DONT_TOUCH(element))
6650 TestIfBadThingTouchesPlayer(x, y);
6655 InitMovingField(x, y, MovDir[x][y]);
6657 PlayLevelSoundAction(x, y, ACTION_MOVING);
6661 ContinueMoving(x, y);
6664 void ContinueMoving(int x, int y)
6666 int element = Feld[x][y];
6667 struct ElementInfo *ei = &element_info[element];
6668 int direction = MovDir[x][y];
6669 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6670 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6671 int newx = x + dx, newy = y + dy;
6672 int stored = Store[x][y];
6673 int stored_new = Store[newx][newy];
6674 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6675 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6676 boolean last_line = (newy == lev_fieldy - 1);
6678 MovPos[x][y] += getElementMoveStepsize(x, y);
6680 if (pushed_by_player) /* special case: moving object pushed by player */
6681 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6683 if (ABS(MovPos[x][y]) < TILEX)
6685 DrawLevelField(x, y);
6687 return; /* element is still moving */
6690 /* element reached destination field */
6692 Feld[x][y] = EL_EMPTY;
6693 Feld[newx][newy] = element;
6694 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6696 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6698 element = Feld[newx][newy] = EL_ACID;
6700 else if (element == EL_MOLE)
6702 Feld[x][y] = EL_SAND;
6704 DrawLevelFieldCrumbledSandNeighbours(x, y);
6706 else if (element == EL_QUICKSAND_FILLING)
6708 element = Feld[newx][newy] = get_next_element(element);
6709 Store[newx][newy] = Store[x][y];
6711 else if (element == EL_QUICKSAND_EMPTYING)
6713 Feld[x][y] = get_next_element(element);
6714 element = Feld[newx][newy] = Store[x][y];
6716 else if (element == EL_MAGIC_WALL_FILLING)
6718 element = Feld[newx][newy] = get_next_element(element);
6719 if (!game.magic_wall_active)
6720 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6721 Store[newx][newy] = Store[x][y];
6723 else if (element == EL_MAGIC_WALL_EMPTYING)
6725 Feld[x][y] = get_next_element(element);
6726 if (!game.magic_wall_active)
6727 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6728 element = Feld[newx][newy] = Store[x][y];
6730 #if USE_NEW_CUSTOM_VALUE
6731 InitField(newx, newy, FALSE);
6734 else if (element == EL_BD_MAGIC_WALL_FILLING)
6736 element = Feld[newx][newy] = get_next_element(element);
6737 if (!game.magic_wall_active)
6738 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6739 Store[newx][newy] = Store[x][y];
6741 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6743 Feld[x][y] = get_next_element(element);
6744 if (!game.magic_wall_active)
6745 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6746 element = Feld[newx][newy] = Store[x][y];
6748 #if USE_NEW_CUSTOM_VALUE
6749 InitField(newx, newy, FALSE);
6752 else if (element == EL_AMOEBA_DROPPING)
6754 Feld[x][y] = get_next_element(element);
6755 element = Feld[newx][newy] = Store[x][y];
6757 else if (element == EL_SOKOBAN_OBJECT)
6760 Feld[x][y] = Back[x][y];
6762 if (Back[newx][newy])
6763 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6765 Back[x][y] = Back[newx][newy] = 0;
6768 Store[x][y] = EL_EMPTY;
6773 MovDelay[newx][newy] = 0;
6776 if (CAN_CHANGE_OR_HAS_ACTION(element))
6778 if (CAN_CHANGE(element))
6781 /* copy element change control values to new field */
6782 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6783 ChangePage[newx][newy] = ChangePage[x][y];
6784 ChangeCount[newx][newy] = ChangeCount[x][y];
6785 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6788 #if USE_NEW_CUSTOM_VALUE
6789 CustomValue[newx][newy] = CustomValue[x][y];
6795 #if USE_NEW_CUSTOM_VALUE
6796 CustomValue[newx][newy] = CustomValue[x][y];
6800 ChangeDelay[x][y] = 0;
6801 ChangePage[x][y] = -1;
6802 ChangeCount[x][y] = 0;
6803 ChangeEvent[x][y] = -1;
6805 #if USE_NEW_CUSTOM_VALUE
6806 CustomValue[x][y] = 0;
6809 /* copy animation control values to new field */
6810 GfxFrame[newx][newy] = GfxFrame[x][y];
6811 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6812 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6813 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6815 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6817 /* some elements can leave other elements behind after moving */
6819 if (ei->move_leave_element != EL_EMPTY &&
6820 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6821 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6823 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6824 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6825 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6828 int move_leave_element = ei->move_leave_element;
6832 /* this makes it possible to leave the removed element again */
6833 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6834 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6836 /* this makes it possible to leave the removed element again */
6837 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6838 move_leave_element = stored;
6841 /* this makes it possible to leave the removed element again */
6842 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6843 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6844 move_leave_element = stored;
6847 Feld[x][y] = move_leave_element;
6849 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6850 MovDir[x][y] = direction;
6852 InitField(x, y, FALSE);
6854 if (GFX_CRUMBLED(Feld[x][y]))
6855 DrawLevelFieldCrumbledSandNeighbours(x, y);
6857 if (ELEM_IS_PLAYER(move_leave_element))
6858 RelocatePlayer(x, y, move_leave_element);
6861 /* do this after checking for left-behind element */
6862 ResetGfxAnimation(x, y); /* reset animation values for old field */
6864 if (!CAN_MOVE(element) ||
6865 (CAN_FALL(element) && direction == MV_DOWN &&
6866 (element == EL_SPRING ||
6867 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6868 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6869 GfxDir[x][y] = MovDir[newx][newy] = 0;
6871 DrawLevelField(x, y);
6872 DrawLevelField(newx, newy);
6874 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6876 /* prevent pushed element from moving on in pushed direction */
6877 if (pushed_by_player && CAN_MOVE(element) &&
6878 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6879 !(element_info[element].move_pattern & direction))
6880 TurnRound(newx, newy);
6882 /* prevent elements on conveyor belt from moving on in last direction */
6883 if (pushed_by_conveyor && CAN_FALL(element) &&
6884 direction & MV_HORIZONTAL)
6885 MovDir[newx][newy] = 0;
6887 if (!pushed_by_player)
6889 int nextx = newx + dx, nexty = newy + dy;
6890 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6892 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6894 if (CAN_FALL(element) && direction == MV_DOWN)
6895 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6897 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6898 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6901 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6903 TestIfBadThingTouchesPlayer(newx, newy);
6904 TestIfBadThingTouchesFriend(newx, newy);
6906 if (!IS_CUSTOM_ELEMENT(element))
6907 TestIfBadThingTouchesOtherBadThing(newx, newy);
6909 else if (element == EL_PENGUIN)
6910 TestIfFriendTouchesBadThing(newx, newy);
6912 /* give the player one last chance (one more frame) to move away */
6913 if (CAN_FALL(element) && direction == MV_DOWN &&
6914 (last_line || (!IS_FREE(x, newy + 1) &&
6915 (!IS_PLAYER(x, newy + 1) ||
6916 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6919 if (pushed_by_player && !game.use_change_when_pushing_bug)
6921 int push_side = MV_DIR_OPPOSITE(direction);
6922 struct PlayerInfo *player = PLAYERINFO(x, y);
6924 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6925 player->index_bit, push_side);
6926 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6927 player->index_bit, push_side);
6930 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6931 MovDelay[newx][newy] = 1;
6933 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6935 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6938 if (ChangePage[newx][newy] != -1) /* delayed change */
6940 int page = ChangePage[newx][newy];
6941 struct ElementChangeInfo *change = &ei->change_page[page];
6943 ChangePage[newx][newy] = -1;
6945 if (change->can_change)
6947 if (ChangeElement(newx, newy, element, page))
6949 if (change->post_change_function)
6950 change->post_change_function(newx, newy);
6954 if (change->has_action)
6955 ExecuteCustomElementAction(newx, newy, element, page);
6959 TestIfElementHitsCustomElement(newx, newy, direction);
6960 TestIfPlayerTouchesCustomElement(newx, newy);
6961 TestIfElementTouchesCustomElement(newx, newy);
6964 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6965 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6966 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6967 MV_DIR_OPPOSITE(direction));
6971 int AmoebeNachbarNr(int ax, int ay)
6974 int element = Feld[ax][ay];
6976 static int xy[4][2] =
6984 for (i = 0; i < NUM_DIRECTIONS; i++)
6986 int x = ax + xy[i][0];
6987 int y = ay + xy[i][1];
6989 if (!IN_LEV_FIELD(x, y))
6992 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6993 group_nr = AmoebaNr[x][y];
6999 void AmoebenVereinigen(int ax, int ay)
7001 int i, x, y, xx, yy;
7002 int new_group_nr = AmoebaNr[ax][ay];
7003 static int xy[4][2] =
7011 if (new_group_nr == 0)
7014 for (i = 0; i < NUM_DIRECTIONS; i++)
7019 if (!IN_LEV_FIELD(x, y))
7022 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7023 Feld[x][y] == EL_BD_AMOEBA ||
7024 Feld[x][y] == EL_AMOEBA_DEAD) &&
7025 AmoebaNr[x][y] != new_group_nr)
7027 int old_group_nr = AmoebaNr[x][y];
7029 if (old_group_nr == 0)
7032 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7033 AmoebaCnt[old_group_nr] = 0;
7034 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7035 AmoebaCnt2[old_group_nr] = 0;
7038 SCAN_PLAYFIELD(xx, yy)
7040 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7043 if (AmoebaNr[xx][yy] == old_group_nr)
7044 AmoebaNr[xx][yy] = new_group_nr;
7050 void AmoebeUmwandeln(int ax, int ay)
7054 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7056 int group_nr = AmoebaNr[ax][ay];
7061 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7062 printf("AmoebeUmwandeln(): This should never happen!\n");
7068 SCAN_PLAYFIELD(x, y)
7070 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7073 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7076 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7080 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7081 SND_AMOEBA_TURNING_TO_GEM :
7082 SND_AMOEBA_TURNING_TO_ROCK));
7087 static int xy[4][2] =
7095 for (i = 0; i < NUM_DIRECTIONS; i++)
7100 if (!IN_LEV_FIELD(x, y))
7103 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7105 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7106 SND_AMOEBA_TURNING_TO_GEM :
7107 SND_AMOEBA_TURNING_TO_ROCK));
7114 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7117 int group_nr = AmoebaNr[ax][ay];
7118 boolean done = FALSE;
7123 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7124 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7130 SCAN_PLAYFIELD(x, y)
7132 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7135 if (AmoebaNr[x][y] == group_nr &&
7136 (Feld[x][y] == EL_AMOEBA_DEAD ||
7137 Feld[x][y] == EL_BD_AMOEBA ||
7138 Feld[x][y] == EL_AMOEBA_GROWING))
7141 Feld[x][y] = new_element;
7142 InitField(x, y, FALSE);
7143 DrawLevelField(x, y);
7149 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7150 SND_BD_AMOEBA_TURNING_TO_ROCK :
7151 SND_BD_AMOEBA_TURNING_TO_GEM));
7154 void AmoebeWaechst(int x, int y)
7156 static unsigned long sound_delay = 0;
7157 static unsigned long sound_delay_value = 0;
7159 if (!MovDelay[x][y]) /* start new growing cycle */
7163 if (DelayReached(&sound_delay, sound_delay_value))
7165 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7166 sound_delay_value = 30;
7170 if (MovDelay[x][y]) /* wait some time before growing bigger */
7173 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7175 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7176 6 - MovDelay[x][y]);
7178 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7181 if (!MovDelay[x][y])
7183 Feld[x][y] = Store[x][y];
7185 DrawLevelField(x, y);
7190 void AmoebaDisappearing(int x, int y)
7192 static unsigned long sound_delay = 0;
7193 static unsigned long sound_delay_value = 0;
7195 if (!MovDelay[x][y]) /* start new shrinking cycle */
7199 if (DelayReached(&sound_delay, sound_delay_value))
7200 sound_delay_value = 30;
7203 if (MovDelay[x][y]) /* wait some time before shrinking */
7206 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7208 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7209 6 - MovDelay[x][y]);
7211 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7214 if (!MovDelay[x][y])
7216 Feld[x][y] = EL_EMPTY;
7217 DrawLevelField(x, y);
7219 /* don't let mole enter this field in this cycle;
7220 (give priority to objects falling to this field from above) */
7226 void AmoebeAbleger(int ax, int ay)
7229 int element = Feld[ax][ay];
7230 int graphic = el2img(element);
7231 int newax = ax, neway = ay;
7232 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7233 static int xy[4][2] =
7241 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7243 Feld[ax][ay] = EL_AMOEBA_DEAD;
7244 DrawLevelField(ax, ay);
7248 if (IS_ANIMATED(graphic))
7249 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7251 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7252 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7254 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7257 if (MovDelay[ax][ay])
7261 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7264 int x = ax + xy[start][0];
7265 int y = ay + xy[start][1];
7267 if (!IN_LEV_FIELD(x, y))
7270 if (IS_FREE(x, y) ||
7271 CAN_GROW_INTO(Feld[x][y]) ||
7272 Feld[x][y] == EL_QUICKSAND_EMPTY)
7278 if (newax == ax && neway == ay)
7281 else /* normal or "filled" (BD style) amoeba */
7284 boolean waiting_for_player = FALSE;
7286 for (i = 0; i < NUM_DIRECTIONS; i++)
7288 int j = (start + i) % 4;
7289 int x = ax + xy[j][0];
7290 int y = ay + xy[j][1];
7292 if (!IN_LEV_FIELD(x, y))
7295 if (IS_FREE(x, y) ||
7296 CAN_GROW_INTO(Feld[x][y]) ||
7297 Feld[x][y] == EL_QUICKSAND_EMPTY)
7303 else if (IS_PLAYER(x, y))
7304 waiting_for_player = TRUE;
7307 if (newax == ax && neway == ay) /* amoeba cannot grow */
7309 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7311 Feld[ax][ay] = EL_AMOEBA_DEAD;
7312 DrawLevelField(ax, ay);
7313 AmoebaCnt[AmoebaNr[ax][ay]]--;
7315 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7317 if (element == EL_AMOEBA_FULL)
7318 AmoebeUmwandeln(ax, ay);
7319 else if (element == EL_BD_AMOEBA)
7320 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7325 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7327 /* amoeba gets larger by growing in some direction */
7329 int new_group_nr = AmoebaNr[ax][ay];
7332 if (new_group_nr == 0)
7334 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7335 printf("AmoebeAbleger(): This should never happen!\n");
7340 AmoebaNr[newax][neway] = new_group_nr;
7341 AmoebaCnt[new_group_nr]++;
7342 AmoebaCnt2[new_group_nr]++;
7344 /* if amoeba touches other amoeba(s) after growing, unify them */
7345 AmoebenVereinigen(newax, neway);
7347 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7349 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7355 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7356 (neway == lev_fieldy - 1 && newax != ax))
7358 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7359 Store[newax][neway] = element;
7361 else if (neway == ay || element == EL_EMC_DRIPPER)
7363 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7365 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7369 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7370 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7371 Store[ax][ay] = EL_AMOEBA_DROP;
7372 ContinueMoving(ax, ay);
7376 DrawLevelField(newax, neway);
7379 void Life(int ax, int ay)
7383 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7386 int element = Feld[ax][ay];
7387 int graphic = el2img(element);
7388 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7390 boolean changed = FALSE;
7392 if (IS_ANIMATED(graphic))
7393 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7398 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7399 MovDelay[ax][ay] = life_time;
7401 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7404 if (MovDelay[ax][ay])
7408 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7410 int xx = ax+x1, yy = ay+y1;
7413 if (!IN_LEV_FIELD(xx, yy))
7416 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7418 int x = xx+x2, y = yy+y2;
7420 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7423 if (((Feld[x][y] == element ||
7424 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7426 (IS_FREE(x, y) && Stop[x][y]))
7430 if (xx == ax && yy == ay) /* field in the middle */
7432 if (nachbarn < life_parameter[0] ||
7433 nachbarn > life_parameter[1])
7435 Feld[xx][yy] = EL_EMPTY;
7437 DrawLevelField(xx, yy);
7438 Stop[xx][yy] = TRUE;
7442 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7443 { /* free border field */
7444 if (nachbarn >= life_parameter[2] &&
7445 nachbarn <= life_parameter[3])
7447 Feld[xx][yy] = element;
7448 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7450 DrawLevelField(xx, yy);
7451 Stop[xx][yy] = TRUE;
7458 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7459 SND_GAME_OF_LIFE_GROWING);
7462 static void InitRobotWheel(int x, int y)
7464 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7467 static void RunRobotWheel(int x, int y)
7469 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7472 static void StopRobotWheel(int x, int y)
7474 if (ZX == x && ZY == y)
7478 static void InitTimegateWheel(int x, int y)
7480 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7483 static void RunTimegateWheel(int x, int y)
7485 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7488 static void InitMagicBallDelay(int x, int y)
7491 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7493 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7497 static void ActivateMagicBall(int bx, int by)
7501 if (level.ball_random)
7503 int pos_border = RND(8); /* select one of the eight border elements */
7504 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7505 int xx = pos_content % 3;
7506 int yy = pos_content / 3;
7511 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7512 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7516 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7518 int xx = x - bx + 1;
7519 int yy = y - by + 1;
7521 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7522 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7526 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7529 static void InitDiagonalMovingElement(int x, int y)
7532 MovDelay[x][y] = level.android_move_time;
7536 void CheckExit(int x, int y)
7538 if (local_player->gems_still_needed > 0 ||
7539 local_player->sokobanfields_still_needed > 0 ||
7540 local_player->lights_still_needed > 0)
7542 int element = Feld[x][y];
7543 int graphic = el2img(element);
7545 if (IS_ANIMATED(graphic))
7546 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7551 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7554 Feld[x][y] = EL_EXIT_OPENING;
7556 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7559 void CheckExitSP(int x, int y)
7561 if (local_player->gems_still_needed > 0)
7563 int element = Feld[x][y];
7564 int graphic = el2img(element);
7566 if (IS_ANIMATED(graphic))
7567 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7572 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7575 Feld[x][y] = EL_SP_EXIT_OPENING;
7577 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7580 static void CloseAllOpenTimegates()
7585 SCAN_PLAYFIELD(x, y)
7587 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7590 int element = Feld[x][y];
7592 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7594 Feld[x][y] = EL_TIMEGATE_CLOSING;
7596 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7601 void EdelsteinFunkeln(int x, int y)
7603 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7606 if (Feld[x][y] == EL_BD_DIAMOND)
7609 if (MovDelay[x][y] == 0) /* next animation frame */
7610 MovDelay[x][y] = 11 * !SimpleRND(500);
7612 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7616 if (setup.direct_draw && MovDelay[x][y])
7617 SetDrawtoField(DRAW_BUFFERED);
7619 DrawLevelElementAnimation(x, y, Feld[x][y]);
7621 if (MovDelay[x][y] != 0)
7623 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7624 10 - MovDelay[x][y]);
7626 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7628 if (setup.direct_draw)
7632 dest_x = FX + SCREENX(x) * TILEX;
7633 dest_y = FY + SCREENY(y) * TILEY;
7635 BlitBitmap(drawto_field, window,
7636 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7637 SetDrawtoField(DRAW_DIRECT);
7643 void MauerWaechst(int x, int y)
7647 if (!MovDelay[x][y]) /* next animation frame */
7648 MovDelay[x][y] = 3 * delay;
7650 if (MovDelay[x][y]) /* wait some time before next frame */
7654 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7656 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7657 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7659 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7662 if (!MovDelay[x][y])
7664 if (MovDir[x][y] == MV_LEFT)
7666 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7667 DrawLevelField(x - 1, y);
7669 else if (MovDir[x][y] == MV_RIGHT)
7671 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7672 DrawLevelField(x + 1, y);
7674 else if (MovDir[x][y] == MV_UP)
7676 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7677 DrawLevelField(x, y - 1);
7681 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7682 DrawLevelField(x, y + 1);
7685 Feld[x][y] = Store[x][y];
7687 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7688 DrawLevelField(x, y);
7693 void MauerAbleger(int ax, int ay)
7695 int element = Feld[ax][ay];
7696 int graphic = el2img(element);
7697 boolean oben_frei = FALSE, unten_frei = FALSE;
7698 boolean links_frei = FALSE, rechts_frei = FALSE;
7699 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7700 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7701 boolean new_wall = FALSE;
7703 if (IS_ANIMATED(graphic))
7704 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7706 if (!MovDelay[ax][ay]) /* start building new wall */
7707 MovDelay[ax][ay] = 6;
7709 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7712 if (MovDelay[ax][ay])
7716 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7718 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7720 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7722 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7725 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7726 element == EL_EXPANDABLE_WALL_ANY)
7730 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7731 Store[ax][ay-1] = element;
7732 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7733 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7734 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7735 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7740 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7741 Store[ax][ay+1] = element;
7742 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7743 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7744 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7745 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7750 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7751 element == EL_EXPANDABLE_WALL_ANY ||
7752 element == EL_EXPANDABLE_WALL)
7756 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7757 Store[ax-1][ay] = element;
7758 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7759 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7760 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7761 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7767 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7768 Store[ax+1][ay] = element;
7769 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7770 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7771 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7772 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7777 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7778 DrawLevelField(ax, ay);
7780 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7782 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7783 unten_massiv = TRUE;
7784 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7785 links_massiv = TRUE;
7786 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7787 rechts_massiv = TRUE;
7789 if (((oben_massiv && unten_massiv) ||
7790 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7791 element == EL_EXPANDABLE_WALL) &&
7792 ((links_massiv && rechts_massiv) ||
7793 element == EL_EXPANDABLE_WALL_VERTICAL))
7794 Feld[ax][ay] = EL_WALL;
7797 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7800 void CheckForDragon(int x, int y)
7803 boolean dragon_found = FALSE;
7804 static int xy[4][2] =
7812 for (i = 0; i < NUM_DIRECTIONS; i++)
7814 for (j = 0; j < 4; j++)
7816 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7818 if (IN_LEV_FIELD(xx, yy) &&
7819 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7821 if (Feld[xx][yy] == EL_DRAGON)
7822 dragon_found = TRUE;
7831 for (i = 0; i < NUM_DIRECTIONS; i++)
7833 for (j = 0; j < 3; j++)
7835 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7837 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7839 Feld[xx][yy] = EL_EMPTY;
7840 DrawLevelField(xx, yy);
7849 static void InitBuggyBase(int x, int y)
7851 int element = Feld[x][y];
7852 int activating_delay = FRAMES_PER_SECOND / 4;
7855 (element == EL_SP_BUGGY_BASE ?
7856 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7857 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7859 element == EL_SP_BUGGY_BASE_ACTIVE ?
7860 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7863 static void WarnBuggyBase(int x, int y)
7866 static int xy[4][2] =
7874 for (i = 0; i < NUM_DIRECTIONS; i++)
7876 int xx = x + xy[i][0];
7877 int yy = y + xy[i][1];
7879 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7881 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7888 static void InitTrap(int x, int y)
7890 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7893 static void ActivateTrap(int x, int y)
7895 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7898 static void ChangeActiveTrap(int x, int y)
7900 int graphic = IMG_TRAP_ACTIVE;
7902 /* if new animation frame was drawn, correct crumbled sand border */
7903 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7904 DrawLevelFieldCrumbledSand(x, y);
7907 static int getSpecialActionElement(int element, int number, int base_element)
7909 return (element != EL_EMPTY ? element :
7910 number != -1 ? base_element + number - 1 :
7914 static int getModifiedActionNumber(int value_old, int operator, int operand,
7915 int value_min, int value_max)
7917 int value_new = (operator == CA_MODE_SET ? operand :
7918 operator == CA_MODE_ADD ? value_old + operand :
7919 operator == CA_MODE_SUBTRACT ? value_old - operand :
7920 operator == CA_MODE_MULTIPLY ? value_old * operand :
7921 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7922 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7925 return (value_new < value_min ? value_min :
7926 value_new > value_max ? value_max :
7930 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7932 struct ElementInfo *ei = &element_info[element];
7933 struct ElementChangeInfo *change = &ei->change_page[page];
7934 int target_element = change->target_element;
7935 int action_type = change->action_type;
7936 int action_mode = change->action_mode;
7937 int action_arg = change->action_arg;
7940 if (!change->has_action)
7943 /* ---------- determine action paramater values -------------------------- */
7945 int level_time_value =
7946 (level.time > 0 ? TimeLeft :
7949 int action_arg_element =
7950 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7951 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7952 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7955 int action_arg_direction =
7956 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7957 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7958 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7959 change->actual_trigger_side :
7960 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7961 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7964 int action_arg_number_min =
7965 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7968 int action_arg_number_max =
7969 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7970 action_type == CA_SET_LEVEL_GEMS ? 999 :
7971 action_type == CA_SET_LEVEL_TIME ? 9999 :
7972 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7973 action_type == CA_SET_CE_VALUE ? 9999 :
7974 action_type == CA_SET_CE_SCORE ? 9999 :
7977 int action_arg_number_reset =
7978 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7979 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7980 action_type == CA_SET_LEVEL_TIME ? level.time :
7981 action_type == CA_SET_LEVEL_SCORE ? 0 :
7983 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7985 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7987 action_type == CA_SET_CE_SCORE ? 0 :
7990 int action_arg_number =
7991 (action_arg <= CA_ARG_MAX ? action_arg :
7992 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7993 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7994 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7995 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7996 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7997 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7998 #if USE_NEW_CUSTOM_VALUE
7999 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8001 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8003 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8004 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8005 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8006 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8007 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8008 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8009 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8010 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8011 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8012 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8013 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8016 int action_arg_number_old =
8017 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8018 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8019 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8020 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8021 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8024 int action_arg_number_new =
8025 getModifiedActionNumber(action_arg_number_old,
8026 action_mode, action_arg_number,
8027 action_arg_number_min, action_arg_number_max);
8029 int trigger_player_bits =
8030 (change->actual_trigger_player >= EL_PLAYER_1 &&
8031 change->actual_trigger_player <= EL_PLAYER_4 ?
8032 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8035 int action_arg_player_bits =
8036 (action_arg >= CA_ARG_PLAYER_1 &&
8037 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8038 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8041 /* ---------- execute action -------------------------------------------- */
8050 /* ---------- level actions ------------------------------------------- */
8052 case CA_RESTART_LEVEL:
8054 game.restart_level = TRUE;
8059 case CA_SHOW_ENVELOPE:
8061 int element = getSpecialActionElement(action_arg_element,
8062 action_arg_number, EL_ENVELOPE_1);
8064 if (IS_ENVELOPE(element))
8065 local_player->show_envelope = element;
8070 case CA_SET_LEVEL_TIME:
8072 if (level.time > 0) /* only modify limited time value */
8074 TimeLeft = action_arg_number_new;
8076 DrawGameValue_Time(TimeLeft);
8078 if (!TimeLeft && setup.time_limit)
8079 for (i = 0; i < MAX_PLAYERS; i++)
8080 KillPlayer(&stored_player[i]);
8086 case CA_SET_LEVEL_SCORE:
8088 local_player->score = action_arg_number_new;
8090 DrawGameValue_Score(local_player->score);
8095 case CA_SET_LEVEL_GEMS:
8097 local_player->gems_still_needed = action_arg_number_new;
8099 DrawGameValue_Emeralds(local_player->gems_still_needed);
8104 case CA_SET_LEVEL_GRAVITY:
8106 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8107 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8108 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8113 case CA_SET_LEVEL_WIND:
8115 game.wind_direction = action_arg_direction;
8120 /* ---------- player actions ------------------------------------------ */
8122 case CA_MOVE_PLAYER:
8124 /* automatically move to the next field in specified direction */
8125 for (i = 0; i < MAX_PLAYERS; i++)
8126 if (trigger_player_bits & (1 << i))
8127 stored_player[i].programmed_action = action_arg_direction;
8132 case CA_EXIT_PLAYER:
8134 for (i = 0; i < MAX_PLAYERS; i++)
8135 if (action_arg_player_bits & (1 << i))
8136 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8141 case CA_KILL_PLAYER:
8143 for (i = 0; i < MAX_PLAYERS; i++)
8144 if (action_arg_player_bits & (1 << i))
8145 KillPlayer(&stored_player[i]);
8150 case CA_SET_PLAYER_KEYS:
8152 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8153 int element = getSpecialActionElement(action_arg_element,
8154 action_arg_number, EL_KEY_1);
8156 if (IS_KEY(element))
8158 for (i = 0; i < MAX_PLAYERS; i++)
8160 if (trigger_player_bits & (1 << i))
8162 stored_player[i].key[KEY_NR(element)] = key_state;
8165 DrawGameDoorValues();
8167 DrawGameValue_Keys(stored_player[i].key);
8170 redraw_mask |= REDRAW_DOOR_1;
8178 case CA_SET_PLAYER_SPEED:
8180 for (i = 0; i < MAX_PLAYERS; i++)
8182 if (trigger_player_bits & (1 << i))
8184 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8186 if (action_arg == CA_ARG_SPEED_FASTER &&
8187 stored_player[i].cannot_move)
8189 action_arg_number = STEPSIZE_VERY_SLOW;
8191 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8192 action_arg == CA_ARG_SPEED_FASTER)
8194 action_arg_number = 2;
8195 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8200 getModifiedActionNumber(move_stepsize,
8203 action_arg_number_min,
8204 action_arg_number_max);
8207 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8209 /* make sure that value is power of 2 */
8210 move_stepsize = (1 << log_2(move_stepsize));
8212 /* do no immediately change -- the player might just be moving */
8213 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8215 stored_player[i].cannot_move =
8216 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8224 case CA_SET_PLAYER_SHIELD:
8226 for (i = 0; i < MAX_PLAYERS; i++)
8228 if (trigger_player_bits & (1 << i))
8230 if (action_arg == CA_ARG_SHIELD_OFF)
8232 stored_player[i].shield_normal_time_left = 0;
8233 stored_player[i].shield_deadly_time_left = 0;
8235 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8237 stored_player[i].shield_normal_time_left = 999999;
8239 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8241 stored_player[i].shield_normal_time_left = 999999;
8242 stored_player[i].shield_deadly_time_left = 999999;
8250 case CA_SET_PLAYER_ARTWORK:
8252 for (i = 0; i < MAX_PLAYERS; i++)
8254 if (trigger_player_bits & (1 << i))
8256 int artwork_element = action_arg_element;
8258 if (action_arg == CA_ARG_ELEMENT_RESET)
8260 (level.use_artwork_element[i] ? level.artwork_element[i] :
8261 stored_player[i].element_nr);
8263 stored_player[i].artwork_element = artwork_element;
8265 SetPlayerWaiting(&stored_player[i], FALSE);
8267 /* set number of special actions for bored and sleeping animation */
8268 stored_player[i].num_special_action_bored =
8269 get_num_special_action(artwork_element,
8270 ACTION_BORING_1, ACTION_BORING_LAST);
8271 stored_player[i].num_special_action_sleeping =
8272 get_num_special_action(artwork_element,
8273 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8280 /* ---------- CE actions ---------------------------------------------- */
8282 case CA_SET_CE_VALUE:
8284 #if USE_NEW_CUSTOM_VALUE
8285 int last_custom_value = CustomValue[x][y];
8287 CustomValue[x][y] = action_arg_number_new;
8290 printf("::: Count == %d\n", CustomValue[x][y]);
8293 if (CustomValue[x][y] == 0 && last_custom_value > 0)
8296 printf("::: CE_VALUE_GETS_ZERO\n");
8299 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8300 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8303 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8311 case CA_SET_CE_SCORE:
8313 ei->collect_score = action_arg_number_new;
8318 /* ---------- engine actions ------------------------------------------ */
8320 case CA_SET_ENGINE_SCAN_MODE:
8322 InitPlayfieldScanMode(action_arg);
8332 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8334 int old_element = Feld[x][y];
8335 int new_element = get_element_from_group_element(element);
8336 int previous_move_direction = MovDir[x][y];
8337 #if USE_NEW_CUSTOM_VALUE
8338 int last_ce_value = CustomValue[x][y];
8340 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8341 IS_WALKABLE(old_element));
8344 /* check if element under player changes from accessible to unaccessible
8345 (needed for special case of dropping element which then changes) */
8346 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8347 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8357 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8358 RemoveMovingField(x, y);
8362 Feld[x][y] = new_element;
8364 #if !USE_GFX_RESET_GFX_ANIMATION
8365 ResetGfxAnimation(x, y);
8366 ResetRandomAnimationValue(x, y);
8369 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8370 MovDir[x][y] = previous_move_direction;
8372 #if USE_NEW_CUSTOM_VALUE
8373 if (element_info[new_element].use_last_ce_value)
8374 CustomValue[x][y] = last_ce_value;
8377 InitField_WithBug1(x, y, FALSE);
8379 new_element = Feld[x][y]; /* element may have changed */
8381 #if USE_GFX_RESET_GFX_ANIMATION
8382 ResetGfxAnimation(x, y);
8383 ResetRandomAnimationValue(x, y);
8386 DrawLevelField(x, y);
8388 if (GFX_CRUMBLED(new_element))
8389 DrawLevelFieldCrumbledSandNeighbours(x, y);
8393 /* check if element under player changes from accessible to unaccessible
8394 (needed for special case of dropping element which then changes) */
8395 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8396 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8404 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8405 if (ELEM_IS_PLAYER(new_element))
8406 RelocatePlayer(x, y, new_element);
8409 ChangeCount[x][y]++; /* count number of changes in the same frame */
8411 TestIfBadThingTouchesPlayer(x, y);
8412 TestIfPlayerTouchesCustomElement(x, y);
8413 TestIfElementTouchesCustomElement(x, y);
8416 static void CreateField(int x, int y, int element)
8418 CreateFieldExt(x, y, element, FALSE);
8421 static void CreateElementFromChange(int x, int y, int element)
8423 element = GET_VALID_RUNTIME_ELEMENT(element);
8425 #if USE_STOP_CHANGED_ELEMENTS
8426 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8428 int old_element = Feld[x][y];
8430 /* prevent changed element from moving in same engine frame
8431 unless both old and new element can either fall or move */
8432 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8433 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8438 CreateFieldExt(x, y, element, TRUE);
8441 static boolean ChangeElement(int x, int y, int element, int page)
8443 struct ElementInfo *ei = &element_info[element];
8444 struct ElementChangeInfo *change = &ei->change_page[page];
8445 int ce_value = CustomValue[x][y];
8446 int ce_score = ei->collect_score;
8448 int old_element = Feld[x][y];
8450 /* always use default change event to prevent running into a loop */
8451 if (ChangeEvent[x][y] == -1)
8452 ChangeEvent[x][y] = CE_DELAY;
8454 if (ChangeEvent[x][y] == CE_DELAY)
8456 /* reset actual trigger element, trigger player and action element */
8457 change->actual_trigger_element = EL_EMPTY;
8458 change->actual_trigger_player = EL_PLAYER_1;
8459 change->actual_trigger_side = CH_SIDE_NONE;
8460 change->actual_trigger_ce_value = 0;
8461 change->actual_trigger_ce_score = 0;
8464 /* do not change elements more than a specified maximum number of changes */
8465 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8468 ChangeCount[x][y]++; /* count number of changes in the same frame */
8470 if (change->explode)
8477 if (change->use_target_content)
8479 boolean complete_replace = TRUE;
8480 boolean can_replace[3][3];
8483 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8486 boolean is_walkable;
8487 boolean is_diggable;
8488 boolean is_collectible;
8489 boolean is_removable;
8490 boolean is_destructible;
8491 int ex = x + xx - 1;
8492 int ey = y + yy - 1;
8493 int content_element = change->target_content.e[xx][yy];
8496 can_replace[xx][yy] = TRUE;
8498 if (ex == x && ey == y) /* do not check changing element itself */
8501 if (content_element == EL_EMPTY_SPACE)
8503 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8508 if (!IN_LEV_FIELD(ex, ey))
8510 can_replace[xx][yy] = FALSE;
8511 complete_replace = FALSE;
8518 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8519 e = MovingOrBlocked2Element(ex, ey);
8521 is_empty = (IS_FREE(ex, ey) ||
8522 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8524 is_walkable = (is_empty || IS_WALKABLE(e));
8525 is_diggable = (is_empty || IS_DIGGABLE(e));
8526 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8527 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8528 is_removable = (is_diggable || is_collectible);
8530 can_replace[xx][yy] =
8531 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8532 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8533 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8534 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8535 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8536 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8537 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8539 if (!can_replace[xx][yy])
8540 complete_replace = FALSE;
8543 if (!change->only_if_complete || complete_replace)
8545 boolean something_has_changed = FALSE;
8547 if (change->only_if_complete && change->use_random_replace &&
8548 RND(100) < change->random_percentage)
8551 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8553 int ex = x + xx - 1;
8554 int ey = y + yy - 1;
8555 int content_element;
8557 if (can_replace[xx][yy] && (!change->use_random_replace ||
8558 RND(100) < change->random_percentage))
8560 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8561 RemoveMovingField(ex, ey);
8563 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8565 content_element = change->target_content.e[xx][yy];
8566 target_element = GET_TARGET_ELEMENT(content_element, change,
8567 ce_value, ce_score);
8569 CreateElementFromChange(ex, ey, target_element);
8571 something_has_changed = TRUE;
8573 /* for symmetry reasons, freeze newly created border elements */
8574 if (ex != x || ey != y)
8575 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8579 if (something_has_changed)
8581 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8582 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8588 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8589 ce_value, ce_score);
8591 if (element == EL_DIAGONAL_GROWING ||
8592 element == EL_DIAGONAL_SHRINKING)
8594 target_element = Store[x][y];
8596 Store[x][y] = EL_EMPTY;
8599 CreateElementFromChange(x, y, target_element);
8601 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8602 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8605 /* this uses direct change before indirect change */
8606 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8611 #if USE_NEW_DELAYED_ACTION
8613 static void HandleElementChange(int x, int y, int page)
8615 int element = MovingOrBlocked2Element(x, y);
8616 struct ElementInfo *ei = &element_info[element];
8617 struct ElementChangeInfo *change = &ei->change_page[page];
8620 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8621 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8624 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8625 x, y, element, element_info[element].token_name);
8626 printf("HandleElementChange(): This should never happen!\n");
8631 /* this can happen with classic bombs on walkable, changing elements */
8632 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8635 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8636 ChangeDelay[x][y] = 0;
8642 if (ChangeDelay[x][y] == 0) /* initialize element change */
8644 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8646 if (change->can_change)
8648 ResetGfxAnimation(x, y);
8649 ResetRandomAnimationValue(x, y);
8651 if (change->pre_change_function)
8652 change->pre_change_function(x, y);
8656 ChangeDelay[x][y]--;
8658 if (ChangeDelay[x][y] != 0) /* continue element change */
8660 if (change->can_change)
8662 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8664 if (IS_ANIMATED(graphic))
8665 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8667 if (change->change_function)
8668 change->change_function(x, y);
8671 else /* finish element change */
8673 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8675 page = ChangePage[x][y];
8676 ChangePage[x][y] = -1;
8678 change = &ei->change_page[page];
8681 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8683 ChangeDelay[x][y] = 1; /* try change after next move step */
8684 ChangePage[x][y] = page; /* remember page to use for change */
8689 if (change->can_change)
8691 if (ChangeElement(x, y, element, page))
8693 if (change->post_change_function)
8694 change->post_change_function(x, y);
8698 if (change->has_action)
8699 ExecuteCustomElementAction(x, y, element, page);
8705 static void HandleElementChange(int x, int y, int page)
8707 int element = MovingOrBlocked2Element(x, y);
8708 struct ElementInfo *ei = &element_info[element];
8709 struct ElementChangeInfo *change = &ei->change_page[page];
8712 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8715 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8716 x, y, element, element_info[element].token_name);
8717 printf("HandleElementChange(): This should never happen!\n");
8722 /* this can happen with classic bombs on walkable, changing elements */
8723 if (!CAN_CHANGE(element))
8726 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8727 ChangeDelay[x][y] = 0;
8733 if (ChangeDelay[x][y] == 0) /* initialize element change */
8735 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8737 ResetGfxAnimation(x, y);
8738 ResetRandomAnimationValue(x, y);
8740 if (change->pre_change_function)
8741 change->pre_change_function(x, y);
8744 ChangeDelay[x][y]--;
8746 if (ChangeDelay[x][y] != 0) /* continue element change */
8748 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8750 if (IS_ANIMATED(graphic))
8751 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8753 if (change->change_function)
8754 change->change_function(x, y);
8756 else /* finish element change */
8758 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8760 page = ChangePage[x][y];
8761 ChangePage[x][y] = -1;
8763 change = &ei->change_page[page];
8766 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8768 ChangeDelay[x][y] = 1; /* try change after next move step */
8769 ChangePage[x][y] = page; /* remember page to use for change */
8774 if (ChangeElement(x, y, element, page))
8776 if (change->post_change_function)
8777 change->post_change_function(x, y);
8784 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8785 int trigger_element,
8791 boolean change_done_any = FALSE;
8792 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8795 if (!(trigger_events[trigger_element][trigger_event]))
8798 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8800 int element = EL_CUSTOM_START + i;
8801 boolean change_done = FALSE;
8804 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8805 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8808 for (p = 0; p < element_info[element].num_change_pages; p++)
8810 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8812 if (change->can_change_or_has_action &&
8813 change->has_event[trigger_event] &&
8814 change->trigger_side & trigger_side &&
8815 change->trigger_player & trigger_player &&
8816 change->trigger_page & trigger_page_bits &&
8817 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8819 change->actual_trigger_element = trigger_element;
8820 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8821 change->actual_trigger_side = trigger_side;
8822 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8823 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8825 if ((change->can_change && !change_done) || change->has_action)
8830 SCAN_PLAYFIELD(x, y)
8832 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8835 if (Feld[x][y] == element)
8837 if (change->can_change && !change_done)
8839 ChangeDelay[x][y] = 1;
8840 ChangeEvent[x][y] = trigger_event;
8842 HandleElementChange(x, y, p);
8844 #if USE_NEW_DELAYED_ACTION
8845 else if (change->has_action)
8847 ExecuteCustomElementAction(x, y, element, p);
8848 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8851 if (change->has_action)
8853 ExecuteCustomElementAction(x, y, element, p);
8854 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8860 if (change->can_change)
8863 change_done_any = TRUE;
8870 return change_done_any;
8873 static boolean CheckElementChangeExt(int x, int y,
8875 int trigger_element,
8880 boolean change_done = FALSE;
8883 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8884 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8887 if (Feld[x][y] == EL_BLOCKED)
8889 Blocked2Moving(x, y, &x, &y);
8890 element = Feld[x][y];
8894 /* check if element has already changed */
8895 if (Feld[x][y] != element)
8898 /* check if element has already changed or is about to change after moving */
8899 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8900 Feld[x][y] != element) ||
8902 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8903 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8904 ChangePage[x][y] != -1)))
8908 for (p = 0; p < element_info[element].num_change_pages; p++)
8910 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8912 boolean check_trigger_element =
8913 (trigger_event == CE_TOUCHING_X ||
8914 trigger_event == CE_HITTING_X ||
8915 trigger_event == CE_HIT_BY_X);
8917 if (change->can_change_or_has_action &&
8918 change->has_event[trigger_event] &&
8919 change->trigger_side & trigger_side &&
8920 change->trigger_player & trigger_player &&
8921 (!check_trigger_element ||
8922 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8924 change->actual_trigger_element = trigger_element;
8925 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8926 change->actual_trigger_side = trigger_side;
8927 change->actual_trigger_ce_value = CustomValue[x][y];
8928 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8930 /* special case: trigger element not at (x,y) position for some events */
8931 if (check_trigger_element)
8943 { 0, 0 }, { 0, 0 }, { 0, 0 },
8947 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8948 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8950 change->actual_trigger_ce_value = CustomValue[xx][yy];
8951 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8954 if (change->can_change && !change_done)
8956 ChangeDelay[x][y] = 1;
8957 ChangeEvent[x][y] = trigger_event;
8959 HandleElementChange(x, y, p);
8963 #if USE_NEW_DELAYED_ACTION
8964 else if (change->has_action)
8966 ExecuteCustomElementAction(x, y, element, p);
8967 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8970 if (change->has_action)
8972 ExecuteCustomElementAction(x, y, element, p);
8973 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8982 static void PlayPlayerSound(struct PlayerInfo *player)
8984 int jx = player->jx, jy = player->jy;
8985 int sound_element = player->artwork_element;
8986 int last_action = player->last_action_waiting;
8987 int action = player->action_waiting;
8989 if (player->is_waiting)
8991 if (action != last_action)
8992 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8994 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8998 if (action != last_action)
8999 StopSound(element_info[sound_element].sound[last_action]);
9001 if (last_action == ACTION_SLEEPING)
9002 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9006 static void PlayAllPlayersSound()
9010 for (i = 0; i < MAX_PLAYERS; i++)
9011 if (stored_player[i].active)
9012 PlayPlayerSound(&stored_player[i]);
9015 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9017 boolean last_waiting = player->is_waiting;
9018 int move_dir = player->MovDir;
9020 player->dir_waiting = move_dir;
9021 player->last_action_waiting = player->action_waiting;
9025 if (!last_waiting) /* not waiting -> waiting */
9027 player->is_waiting = TRUE;
9029 player->frame_counter_bored =
9031 game.player_boring_delay_fixed +
9032 SimpleRND(game.player_boring_delay_random);
9033 player->frame_counter_sleeping =
9035 game.player_sleeping_delay_fixed +
9036 SimpleRND(game.player_sleeping_delay_random);
9039 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9041 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9045 if (game.player_sleeping_delay_fixed +
9046 game.player_sleeping_delay_random > 0 &&
9047 player->anim_delay_counter == 0 &&
9048 player->post_delay_counter == 0 &&
9049 FrameCounter >= player->frame_counter_sleeping)
9050 player->is_sleeping = TRUE;
9051 else if (game.player_boring_delay_fixed +
9052 game.player_boring_delay_random > 0 &&
9053 FrameCounter >= player->frame_counter_bored)
9054 player->is_bored = TRUE;
9056 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9057 player->is_bored ? ACTION_BORING :
9061 if (player->is_sleeping && player->use_murphy)
9063 /* special case for sleeping Murphy when leaning against non-free tile */
9065 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9066 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9067 !IS_MOVING(player->jx - 1, player->jy)))
9069 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9070 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9071 !IS_MOVING(player->jx + 1, player->jy)))
9072 move_dir = MV_RIGHT;
9074 player->is_sleeping = FALSE;
9076 player->dir_waiting = move_dir;
9080 if (player->is_sleeping)
9082 if (player->num_special_action_sleeping > 0)
9084 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9086 int last_special_action = player->special_action_sleeping;
9087 int num_special_action = player->num_special_action_sleeping;
9088 int special_action =
9089 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9090 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9091 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9092 last_special_action + 1 : ACTION_SLEEPING);
9093 int special_graphic =
9094 el_act_dir2img(player->artwork_element, special_action, move_dir);
9096 player->anim_delay_counter =
9097 graphic_info[special_graphic].anim_delay_fixed +
9098 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9099 player->post_delay_counter =
9100 graphic_info[special_graphic].post_delay_fixed +
9101 SimpleRND(graphic_info[special_graphic].post_delay_random);
9103 player->special_action_sleeping = special_action;
9106 if (player->anim_delay_counter > 0)
9108 player->action_waiting = player->special_action_sleeping;
9109 player->anim_delay_counter--;
9111 else if (player->post_delay_counter > 0)
9113 player->post_delay_counter--;
9117 else if (player->is_bored)
9119 if (player->num_special_action_bored > 0)
9121 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9123 int special_action =
9124 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9125 int special_graphic =
9126 el_act_dir2img(player->artwork_element, special_action, move_dir);
9128 player->anim_delay_counter =
9129 graphic_info[special_graphic].anim_delay_fixed +
9130 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9131 player->post_delay_counter =
9132 graphic_info[special_graphic].post_delay_fixed +
9133 SimpleRND(graphic_info[special_graphic].post_delay_random);
9135 player->special_action_bored = special_action;
9138 if (player->anim_delay_counter > 0)
9140 player->action_waiting = player->special_action_bored;
9141 player->anim_delay_counter--;
9143 else if (player->post_delay_counter > 0)
9145 player->post_delay_counter--;
9150 else if (last_waiting) /* waiting -> not waiting */
9152 player->is_waiting = FALSE;
9153 player->is_bored = FALSE;
9154 player->is_sleeping = FALSE;
9156 player->frame_counter_bored = -1;
9157 player->frame_counter_sleeping = -1;
9159 player->anim_delay_counter = 0;
9160 player->post_delay_counter = 0;
9162 player->dir_waiting = player->MovDir;
9163 player->action_waiting = ACTION_DEFAULT;
9165 player->special_action_bored = ACTION_DEFAULT;
9166 player->special_action_sleeping = ACTION_DEFAULT;
9170 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9172 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9173 int left = player_action & JOY_LEFT;
9174 int right = player_action & JOY_RIGHT;
9175 int up = player_action & JOY_UP;
9176 int down = player_action & JOY_DOWN;
9177 int button1 = player_action & JOY_BUTTON_1;
9178 int button2 = player_action & JOY_BUTTON_2;
9179 int dx = (left ? -1 : right ? 1 : 0);
9180 int dy = (up ? -1 : down ? 1 : 0);
9182 if (!player->active || tape.pausing)
9188 snapped = SnapField(player, dx, dy);
9192 dropped = DropElement(player);
9194 moved = MovePlayer(player, dx, dy);
9197 if (tape.single_step && tape.recording && !tape.pausing)
9199 if (button1 || (dropped && !moved))
9201 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9202 SnapField(player, 0, 0); /* stop snapping */
9206 SetPlayerWaiting(player, FALSE);
9208 return player_action;
9212 /* no actions for this player (no input at player's configured device) */
9214 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9215 SnapField(player, 0, 0);
9216 CheckGravityMovementWhenNotMoving(player);
9218 if (player->MovPos == 0)
9219 SetPlayerWaiting(player, TRUE);
9221 if (player->MovPos == 0) /* needed for tape.playing */
9222 player->is_moving = FALSE;
9224 player->is_dropping = FALSE;
9225 player->is_dropping_pressed = FALSE;
9226 player->drop_pressed_delay = 0;
9232 static void CheckLevelTime()
9236 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9238 if (level.native_em_level->lev->home == 0) /* all players at home */
9240 local_player->LevelSolved = TRUE;
9241 AllPlayersGone = TRUE;
9243 level.native_em_level->lev->home = -1;
9246 if (level.native_em_level->ply[0]->alive == 0 &&
9247 level.native_em_level->ply[1]->alive == 0 &&
9248 level.native_em_level->ply[2]->alive == 0 &&
9249 level.native_em_level->ply[3]->alive == 0) /* all dead */
9250 AllPlayersGone = TRUE;
9253 if (TimeFrames >= FRAMES_PER_SECOND)
9258 for (i = 0; i < MAX_PLAYERS; i++)
9260 struct PlayerInfo *player = &stored_player[i];
9262 if (SHIELD_ON(player))
9264 player->shield_normal_time_left--;
9266 if (player->shield_deadly_time_left > 0)
9267 player->shield_deadly_time_left--;
9271 if (!level.use_step_counter)
9279 if (TimeLeft <= 10 && setup.time_limit)
9280 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9282 DrawGameValue_Time(TimeLeft);
9284 if (!TimeLeft && setup.time_limit)
9286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9287 level.native_em_level->lev->killed_out_of_time = TRUE;
9289 for (i = 0; i < MAX_PLAYERS; i++)
9290 KillPlayer(&stored_player[i]);
9293 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9294 DrawGameValue_Time(TimePlayed);
9296 level.native_em_level->lev->time =
9297 (level.time == 0 ? TimePlayed : TimeLeft);
9300 if (tape.recording || tape.playing)
9301 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9305 void AdvanceFrameAndPlayerCounters(int player_nr)
9310 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9311 FrameCounter, FrameCounter + 1);
9314 /* advance frame counters (global frame counter and time frame counter) */
9318 /* advance player counters (counters for move delay, move animation etc.) */
9319 for (i = 0; i < MAX_PLAYERS; i++)
9321 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9322 int move_delay_value = stored_player[i].move_delay_value;
9323 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9325 if (!advance_player_counters) /* not all players may be affected */
9328 #if USE_NEW_PLAYER_ANIM
9329 if (move_frames == 0) /* less than one move per game frame */
9331 int stepsize = TILEX / move_delay_value;
9332 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9333 int count = (stored_player[i].is_moving ?
9334 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9336 if (count % delay == 0)
9341 stored_player[i].Frame += move_frames;
9343 if (stored_player[i].MovPos != 0)
9344 stored_player[i].StepFrame += move_frames;
9346 if (stored_player[i].move_delay > 0)
9347 stored_player[i].move_delay--;
9349 /* due to bugs in previous versions, counter must count up, not down */
9350 if (stored_player[i].push_delay != -1)
9351 stored_player[i].push_delay++;
9353 if (stored_player[i].drop_delay > 0)
9354 stored_player[i].drop_delay--;
9356 if (stored_player[i].is_dropping_pressed)
9357 stored_player[i].drop_pressed_delay++;
9361 void StartGameActions(boolean init_network_game, boolean record_tape,
9364 unsigned long new_random_seed = InitRND(random_seed);
9367 TapeStartRecording(new_random_seed);
9369 #if defined(NETWORK_AVALIABLE)
9370 if (init_network_game)
9372 SendToServer_StartPlaying();
9380 game_status = GAME_MODE_PLAYING;
9387 static unsigned long game_frame_delay = 0;
9388 unsigned long game_frame_delay_value;
9389 byte *recorded_player_action;
9390 byte summarized_player_action = 0;
9391 byte tape_action[MAX_PLAYERS];
9394 if (game.restart_level)
9395 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9397 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9399 if (level.native_em_level->lev->home == 0) /* all players at home */
9401 local_player->LevelSolved = TRUE;
9402 AllPlayersGone = TRUE;
9404 level.native_em_level->lev->home = -1;
9407 if (level.native_em_level->ply[0]->alive == 0 &&
9408 level.native_em_level->ply[1]->alive == 0 &&
9409 level.native_em_level->ply[2]->alive == 0 &&
9410 level.native_em_level->ply[3]->alive == 0) /* all dead */
9411 AllPlayersGone = TRUE;
9414 if (local_player->LevelSolved)
9417 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9420 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9423 game_frame_delay_value =
9424 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9426 if (tape.playing && tape.warp_forward && !tape.pausing)
9427 game_frame_delay_value = 0;
9429 /* ---------- main game synchronization point ---------- */
9431 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9433 if (network_playing && !network_player_action_received)
9435 /* try to get network player actions in time */
9437 #if defined(NETWORK_AVALIABLE)
9438 /* last chance to get network player actions without main loop delay */
9442 /* game was quit by network peer */
9443 if (game_status != GAME_MODE_PLAYING)
9446 if (!network_player_action_received)
9447 return; /* failed to get network player actions in time */
9449 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9455 /* at this point we know that we really continue executing the game */
9458 network_player_action_received = FALSE;
9461 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9463 if (tape.set_centered_player)
9465 game.centered_player_nr_next = tape.centered_player_nr_next;
9466 game.set_centered_player = TRUE;
9469 for (i = 0; i < MAX_PLAYERS; i++)
9471 summarized_player_action |= stored_player[i].action;
9473 if (!network_playing)
9474 stored_player[i].effective_action = stored_player[i].action;
9477 #if defined(NETWORK_AVALIABLE)
9478 if (network_playing)
9479 SendToServer_MovePlayer(summarized_player_action);
9482 if (!options.network && !setup.team_mode)
9483 local_player->effective_action = summarized_player_action;
9485 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9487 for (i = 0; i < MAX_PLAYERS; i++)
9488 stored_player[i].effective_action =
9489 (i == game.centered_player_nr ? summarized_player_action : 0);
9492 if (recorded_player_action != NULL)
9493 for (i = 0; i < MAX_PLAYERS; i++)
9494 stored_player[i].effective_action = recorded_player_action[i];
9496 for (i = 0; i < MAX_PLAYERS; i++)
9498 tape_action[i] = stored_player[i].effective_action;
9500 /* (this can only happen in the R'n'D game engine) */
9501 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9502 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9505 /* only record actions from input devices, but not programmed actions */
9507 TapeRecordAction(tape_action);
9509 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9511 GameActions_EM_Main();
9519 void GameActions_EM_Main()
9521 byte effective_action[MAX_PLAYERS];
9522 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9525 for (i = 0; i < MAX_PLAYERS; i++)
9526 effective_action[i] = stored_player[i].effective_action;
9528 GameActions_EM(effective_action, warp_mode);
9532 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9535 void GameActions_RND()
9537 int magic_wall_x = 0, magic_wall_y = 0;
9538 int i, x, y, element, graphic;
9540 InitPlayfieldScanModeVars();
9542 #if USE_ONE_MORE_CHANGE_PER_FRAME
9543 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9545 SCAN_PLAYFIELD(x, y)
9547 ChangeCount[x][y] = 0;
9548 ChangeEvent[x][y] = -1;
9554 if (game.set_centered_player)
9556 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9558 /* switching to "all players" only possible if all players fit to screen */
9559 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9561 game.centered_player_nr_next = game.centered_player_nr;
9562 game.set_centered_player = FALSE;
9565 /* do not switch focus to non-existing (or non-active) player */
9566 if (game.centered_player_nr_next >= 0 &&
9567 !stored_player[game.centered_player_nr_next].active)
9569 game.centered_player_nr_next = game.centered_player_nr;
9570 game.set_centered_player = FALSE;
9574 if (game.set_centered_player &&
9575 ScreenMovPos == 0) /* screen currently aligned at tile position */
9579 if (game.centered_player_nr_next == -1)
9581 setScreenCenteredToAllPlayers(&sx, &sy);
9585 sx = stored_player[game.centered_player_nr_next].jx;
9586 sy = stored_player[game.centered_player_nr_next].jy;
9589 game.centered_player_nr = game.centered_player_nr_next;
9590 game.set_centered_player = FALSE;
9592 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9593 DrawGameDoorValues();
9597 for (i = 0; i < MAX_PLAYERS; i++)
9599 int actual_player_action = stored_player[i].effective_action;
9602 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9603 - rnd_equinox_tetrachloride 048
9604 - rnd_equinox_tetrachloride_ii 096
9605 - rnd_emanuel_schmieg 002
9606 - doctor_sloan_ww 001, 020
9608 if (stored_player[i].MovPos == 0)
9609 CheckGravityMovement(&stored_player[i]);
9612 /* overwrite programmed action with tape action */
9613 if (stored_player[i].programmed_action)
9614 actual_player_action = stored_player[i].programmed_action;
9617 PlayerActions(&stored_player[i], actual_player_action);
9619 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9621 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9622 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9625 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9629 network_player_action_received = FALSE;
9632 ScrollScreen(NULL, SCROLL_GO_ON);
9634 /* for backwards compatibility, the following code emulates a fixed bug that
9635 occured when pushing elements (causing elements that just made their last
9636 pushing step to already (if possible) make their first falling step in the
9637 same game frame, which is bad); this code is also needed to use the famous
9638 "spring push bug" which is used in older levels and might be wanted to be
9639 used also in newer levels, but in this case the buggy pushing code is only
9640 affecting the "spring" element and no other elements */
9642 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9644 for (i = 0; i < MAX_PLAYERS; i++)
9646 struct PlayerInfo *player = &stored_player[i];
9650 if (player->active && player->is_pushing && player->is_moving &&
9652 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9653 Feld[x][y] == EL_SPRING))
9655 ContinueMoving(x, y);
9657 /* continue moving after pushing (this is actually a bug) */
9658 if (!IS_MOVING(x, y))
9667 SCAN_PLAYFIELD(x, y)
9669 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9672 ChangeCount[x][y] = 0;
9673 ChangeEvent[x][y] = -1;
9675 /* this must be handled before main playfield loop */
9676 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9679 if (MovDelay[x][y] <= 0)
9683 #if USE_NEW_SNAP_DELAY
9684 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9687 if (MovDelay[x][y] <= 0)
9690 DrawLevelField(x, y);
9692 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9698 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9700 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9701 printf("GameActions(): This should never happen!\n");
9703 ChangePage[x][y] = -1;
9708 if (WasJustMoving[x][y] > 0)
9709 WasJustMoving[x][y]--;
9710 if (WasJustFalling[x][y] > 0)
9711 WasJustFalling[x][y]--;
9712 if (CheckCollision[x][y] > 0)
9713 CheckCollision[x][y]--;
9717 /* reset finished pushing action (not done in ContinueMoving() to allow
9718 continuous pushing animation for elements with zero push delay) */
9719 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9721 ResetGfxAnimation(x, y);
9722 DrawLevelField(x, y);
9726 if (IS_BLOCKED(x, y))
9730 Blocked2Moving(x, y, &oldx, &oldy);
9731 if (!IS_MOVING(oldx, oldy))
9733 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9734 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9735 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9736 printf("GameActions(): This should never happen!\n");
9743 SCAN_PLAYFIELD(x, y)
9745 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9748 element = Feld[x][y];
9749 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9752 printf("::: %d,%d\n", x, y);
9754 if (element == EL_ROCK)
9755 printf("::: Yo man! Rocks can fall!\n");
9759 ResetGfxFrame(x, y, TRUE);
9761 if (graphic_info[graphic].anim_global_sync)
9762 GfxFrame[x][y] = FrameCounter;
9763 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9765 int old_gfx_frame = GfxFrame[x][y];
9767 GfxFrame[x][y] = CustomValue[x][y];
9770 if (GfxFrame[x][y] != old_gfx_frame)
9772 DrawLevelGraphicAnimation(x, y, graphic);
9774 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9776 int old_gfx_frame = GfxFrame[x][y];
9778 GfxFrame[x][y] = element_info[element].collect_score;
9781 if (GfxFrame[x][y] != old_gfx_frame)
9783 DrawLevelGraphicAnimation(x, y, graphic);
9787 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9788 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9789 ResetRandomAnimationValue(x, y);
9791 SetRandomAnimationValue(x, y);
9793 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9795 if (IS_INACTIVE(element))
9797 if (IS_ANIMATED(graphic))
9798 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9803 /* this may take place after moving, so 'element' may have changed */
9804 if (IS_CHANGING(x, y) &&
9805 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9807 int page = element_info[element].event_page_nr[CE_DELAY];
9809 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9813 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9817 if (element == EL_CUSTOM_255)
9818 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9822 HandleElementChange(x, y, page);
9824 if (CAN_CHANGE(element))
9825 HandleElementChange(x, y, page);
9827 if (HAS_ACTION(element))
9828 ExecuteCustomElementAction(x, y, element, page);
9833 element = Feld[x][y];
9834 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9837 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9841 element = Feld[x][y];
9842 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9844 if (IS_ANIMATED(graphic) &&
9847 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9849 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9850 EdelsteinFunkeln(x, y);
9852 else if ((element == EL_ACID ||
9853 element == EL_EXIT_OPEN ||
9854 element == EL_SP_EXIT_OPEN ||
9855 element == EL_SP_TERMINAL ||
9856 element == EL_SP_TERMINAL_ACTIVE ||
9857 element == EL_EXTRA_TIME ||
9858 element == EL_SHIELD_NORMAL ||
9859 element == EL_SHIELD_DEADLY) &&
9860 IS_ANIMATED(graphic))
9861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9862 else if (IS_MOVING(x, y))
9863 ContinueMoving(x, y);
9864 else if (IS_ACTIVE_BOMB(element))
9865 CheckDynamite(x, y);
9866 else if (element == EL_AMOEBA_GROWING)
9867 AmoebeWaechst(x, y);
9868 else if (element == EL_AMOEBA_SHRINKING)
9869 AmoebaDisappearing(x, y);
9871 #if !USE_NEW_AMOEBA_CODE
9872 else if (IS_AMOEBALIVE(element))
9873 AmoebeAbleger(x, y);
9876 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9878 else if (element == EL_EXIT_CLOSED)
9880 else if (element == EL_SP_EXIT_CLOSED)
9882 else if (element == EL_EXPANDABLE_WALL_GROWING)
9884 else if (element == EL_EXPANDABLE_WALL ||
9885 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9886 element == EL_EXPANDABLE_WALL_VERTICAL ||
9887 element == EL_EXPANDABLE_WALL_ANY)
9889 else if (element == EL_FLAMES)
9890 CheckForDragon(x, y);
9891 else if (element == EL_EXPLOSION)
9892 ; /* drawing of correct explosion animation is handled separately */
9893 else if (element == EL_ELEMENT_SNAPPING ||
9894 element == EL_DIAGONAL_SHRINKING ||
9895 element == EL_DIAGONAL_GROWING)
9898 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9900 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9903 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9904 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9907 if (element == EL_CUSTOM_255 ||
9908 element == EL_CUSTOM_256)
9909 DrawLevelGraphicAnimation(x, y, graphic);
9912 if (IS_BELT_ACTIVE(element))
9913 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9915 if (game.magic_wall_active)
9917 int jx = local_player->jx, jy = local_player->jy;
9919 /* play the element sound at the position nearest to the player */
9920 if ((element == EL_MAGIC_WALL_FULL ||
9921 element == EL_MAGIC_WALL_ACTIVE ||
9922 element == EL_MAGIC_WALL_EMPTYING ||
9923 element == EL_BD_MAGIC_WALL_FULL ||
9924 element == EL_BD_MAGIC_WALL_ACTIVE ||
9925 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9926 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9934 #if USE_NEW_AMOEBA_CODE
9935 /* new experimental amoeba growth stuff */
9936 if (!(FrameCounter % 8))
9938 static unsigned long random = 1684108901;
9940 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9942 x = RND(lev_fieldx);
9943 y = RND(lev_fieldy);
9944 element = Feld[x][y];
9946 if (!IS_PLAYER(x,y) &&
9947 (element == EL_EMPTY ||
9948 CAN_GROW_INTO(element) ||
9949 element == EL_QUICKSAND_EMPTY ||
9950 element == EL_ACID_SPLASH_LEFT ||
9951 element == EL_ACID_SPLASH_RIGHT))
9953 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9954 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9955 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9956 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9957 Feld[x][y] = EL_AMOEBA_DROP;
9960 random = random * 129 + 1;
9966 if (game.explosions_delayed)
9969 game.explosions_delayed = FALSE;
9972 SCAN_PLAYFIELD(x, y)
9974 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9977 element = Feld[x][y];
9979 if (ExplodeField[x][y])
9980 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9981 else if (element == EL_EXPLOSION)
9982 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9984 ExplodeField[x][y] = EX_TYPE_NONE;
9987 game.explosions_delayed = TRUE;
9990 if (game.magic_wall_active)
9992 if (!(game.magic_wall_time_left % 4))
9994 int element = Feld[magic_wall_x][magic_wall_y];
9996 if (element == EL_BD_MAGIC_WALL_FULL ||
9997 element == EL_BD_MAGIC_WALL_ACTIVE ||
9998 element == EL_BD_MAGIC_WALL_EMPTYING)
9999 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10001 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10004 if (game.magic_wall_time_left > 0)
10006 game.magic_wall_time_left--;
10007 if (!game.magic_wall_time_left)
10010 SCAN_PLAYFIELD(x, y)
10012 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10015 element = Feld[x][y];
10017 if (element == EL_MAGIC_WALL_ACTIVE ||
10018 element == EL_MAGIC_WALL_FULL)
10020 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10021 DrawLevelField(x, y);
10023 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10024 element == EL_BD_MAGIC_WALL_FULL)
10026 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10027 DrawLevelField(x, y);
10031 game.magic_wall_active = FALSE;
10036 if (game.light_time_left > 0)
10038 game.light_time_left--;
10040 if (game.light_time_left == 0)
10041 RedrawAllLightSwitchesAndInvisibleElements();
10044 if (game.timegate_time_left > 0)
10046 game.timegate_time_left--;
10048 if (game.timegate_time_left == 0)
10049 CloseAllOpenTimegates();
10052 if (game.lenses_time_left > 0)
10054 game.lenses_time_left--;
10056 if (game.lenses_time_left == 0)
10057 RedrawAllInvisibleElementsForLenses();
10060 if (game.magnify_time_left > 0)
10062 game.magnify_time_left--;
10064 if (game.magnify_time_left == 0)
10065 RedrawAllInvisibleElementsForMagnifier();
10068 for (i = 0; i < MAX_PLAYERS; i++)
10070 struct PlayerInfo *player = &stored_player[i];
10072 if (SHIELD_ON(player))
10074 if (player->shield_deadly_time_left)
10075 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10076 else if (player->shield_normal_time_left)
10077 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10084 PlayAllPlayersSound();
10086 if (options.debug) /* calculate frames per second */
10088 static unsigned long fps_counter = 0;
10089 static int fps_frames = 0;
10090 unsigned long fps_delay_ms = Counter() - fps_counter;
10094 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10096 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10099 fps_counter = Counter();
10102 redraw_mask |= REDRAW_FPS;
10105 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10107 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10109 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10111 local_player->show_envelope = 0;
10114 /* use random number generator in every frame to make it less predictable */
10115 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10119 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10121 int min_x = x, min_y = y, max_x = x, max_y = y;
10124 for (i = 0; i < MAX_PLAYERS; i++)
10126 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10128 if (!stored_player[i].active || &stored_player[i] == player)
10131 min_x = MIN(min_x, jx);
10132 min_y = MIN(min_y, jy);
10133 max_x = MAX(max_x, jx);
10134 max_y = MAX(max_y, jy);
10137 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10140 static boolean AllPlayersInVisibleScreen()
10144 for (i = 0; i < MAX_PLAYERS; i++)
10146 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10148 if (!stored_player[i].active)
10151 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10158 void ScrollLevel(int dx, int dy)
10160 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10163 BlitBitmap(drawto_field, drawto_field,
10164 FX + TILEX * (dx == -1) - softscroll_offset,
10165 FY + TILEY * (dy == -1) - softscroll_offset,
10166 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10167 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10168 FX + TILEX * (dx == 1) - softscroll_offset,
10169 FY + TILEY * (dy == 1) - softscroll_offset);
10173 x = (dx == 1 ? BX1 : BX2);
10174 for (y = BY1; y <= BY2; y++)
10175 DrawScreenField(x, y);
10180 y = (dy == 1 ? BY1 : BY2);
10181 for (x = BX1; x <= BX2; x++)
10182 DrawScreenField(x, y);
10185 redraw_mask |= REDRAW_FIELD;
10188 static boolean canFallDown(struct PlayerInfo *player)
10190 int jx = player->jx, jy = player->jy;
10192 return (IN_LEV_FIELD(jx, jy + 1) &&
10193 (IS_FREE(jx, jy + 1) ||
10194 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10195 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10196 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10199 static boolean canPassField(int x, int y, int move_dir)
10201 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10202 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10203 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10204 int nextx = x + dx;
10205 int nexty = y + dy;
10206 int element = Feld[x][y];
10208 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10209 !CAN_MOVE(element) &&
10210 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10211 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10212 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10215 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10217 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10218 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10219 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10223 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10224 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10225 (IS_DIGGABLE(Feld[newx][newy]) ||
10226 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10227 canPassField(newx, newy, move_dir)));
10230 static void CheckGravityMovement(struct PlayerInfo *player)
10232 if (game.gravity && !player->programmed_action)
10234 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10235 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10236 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10237 int jx = player->jx, jy = player->jy;
10238 boolean player_is_moving_to_valid_field =
10239 (!player_is_snapping &&
10240 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10241 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10242 boolean player_can_fall_down = canFallDown(player);
10244 if (player_can_fall_down &&
10245 !player_is_moving_to_valid_field)
10246 player->programmed_action = MV_DOWN;
10250 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10252 return CheckGravityMovement(player);
10254 if (game.gravity && !player->programmed_action)
10256 int jx = player->jx, jy = player->jy;
10257 boolean field_under_player_is_free =
10258 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10259 boolean player_is_standing_on_valid_field =
10260 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10261 (IS_WALKABLE(Feld[jx][jy]) &&
10262 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10264 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10265 player->programmed_action = MV_DOWN;
10270 MovePlayerOneStep()
10271 -----------------------------------------------------------------------------
10272 dx, dy: direction (non-diagonal) to try to move the player to
10273 real_dx, real_dy: direction as read from input device (can be diagonal)
10276 boolean MovePlayerOneStep(struct PlayerInfo *player,
10277 int dx, int dy, int real_dx, int real_dy)
10279 int jx = player->jx, jy = player->jy;
10280 int new_jx = jx + dx, new_jy = jy + dy;
10281 #if !USE_FIXED_DONT_RUN_INTO
10285 boolean player_can_move = !player->cannot_move;
10287 if (!player->active || (!dx && !dy))
10288 return MP_NO_ACTION;
10290 player->MovDir = (dx < 0 ? MV_LEFT :
10291 dx > 0 ? MV_RIGHT :
10293 dy > 0 ? MV_DOWN : MV_NONE);
10295 if (!IN_LEV_FIELD(new_jx, new_jy))
10296 return MP_NO_ACTION;
10298 if (!player_can_move)
10301 if (player->MovPos == 0)
10303 player->is_moving = FALSE;
10304 player->is_digging = FALSE;
10305 player->is_collecting = FALSE;
10306 player->is_snapping = FALSE;
10307 player->is_pushing = FALSE;
10310 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10311 SnapField(player, 0, 0);
10315 return MP_NO_ACTION;
10320 if (!options.network && game.centered_player_nr == -1 &&
10321 !AllPlayersInSight(player, new_jx, new_jy))
10322 return MP_NO_ACTION;
10324 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10325 return MP_NO_ACTION;
10328 #if !USE_FIXED_DONT_RUN_INTO
10329 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10331 /* (moved to DigField()) */
10332 if (player_can_move && DONT_RUN_INTO(element))
10334 if (element == EL_ACID && dx == 0 && dy == 1)
10336 SplashAcid(new_jx, new_jy);
10337 Feld[jx][jy] = EL_PLAYER_1;
10338 InitMovingField(jx, jy, MV_DOWN);
10339 Store[jx][jy] = EL_ACID;
10340 ContinueMoving(jx, jy);
10341 BuryPlayer(player);
10344 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10350 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10352 #if USE_FIXED_DONT_RUN_INTO
10353 if (can_move == MP_DONT_RUN_INTO)
10357 if (can_move != MP_MOVING)
10360 #if USE_FIXED_DONT_RUN_INTO
10363 /* check if DigField() has caused relocation of the player */
10364 if (player->jx != jx || player->jy != jy)
10365 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10367 StorePlayer[jx][jy] = 0;
10368 player->last_jx = jx;
10369 player->last_jy = jy;
10370 player->jx = new_jx;
10371 player->jy = new_jy;
10372 StorePlayer[new_jx][new_jy] = player->element_nr;
10374 if (player->move_delay_value_next != -1)
10376 player->move_delay_value = player->move_delay_value_next;
10377 player->move_delay_value_next = -1;
10381 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10383 player->step_counter++;
10385 PlayerVisit[jx][jy] = FrameCounter;
10387 ScrollPlayer(player, SCROLL_INIT);
10392 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10394 int jx = player->jx, jy = player->jy;
10395 int old_jx = jx, old_jy = jy;
10396 int moved = MP_NO_ACTION;
10398 if (!player->active)
10403 if (player->MovPos == 0)
10405 player->is_moving = FALSE;
10406 player->is_digging = FALSE;
10407 player->is_collecting = FALSE;
10408 player->is_snapping = FALSE;
10409 player->is_pushing = FALSE;
10415 if (player->move_delay > 0)
10418 player->move_delay = -1; /* set to "uninitialized" value */
10420 /* store if player is automatically moved to next field */
10421 player->is_auto_moving = (player->programmed_action != MV_NONE);
10423 /* remove the last programmed player action */
10424 player->programmed_action = 0;
10426 if (player->MovPos)
10428 /* should only happen if pre-1.2 tape recordings are played */
10429 /* this is only for backward compatibility */
10431 int original_move_delay_value = player->move_delay_value;
10434 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10438 /* scroll remaining steps with finest movement resolution */
10439 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10441 while (player->MovPos)
10443 ScrollPlayer(player, SCROLL_GO_ON);
10444 ScrollScreen(NULL, SCROLL_GO_ON);
10446 AdvanceFrameAndPlayerCounters(player->index_nr);
10452 player->move_delay_value = original_move_delay_value;
10455 if (player->last_move_dir & MV_HORIZONTAL)
10457 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10458 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10462 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10463 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10470 if (moved & MP_MOVING && !ScreenMovPos &&
10471 (player->index_nr == game.centered_player_nr ||
10472 game.centered_player_nr == -1))
10474 if (moved & MP_MOVING && !ScreenMovPos &&
10475 (player == local_player || !options.network))
10478 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10479 int offset = (setup.scroll_delay ? 3 : 0);
10481 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10483 /* actual player has left the screen -- scroll in that direction */
10484 if (jx != old_jx) /* player has moved horizontally */
10485 scroll_x += (jx - old_jx);
10486 else /* player has moved vertically */
10487 scroll_y += (jy - old_jy);
10491 if (jx != old_jx) /* player has moved horizontally */
10493 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10494 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10495 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10497 /* don't scroll over playfield boundaries */
10498 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10499 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10501 /* don't scroll more than one field at a time */
10502 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10504 /* don't scroll against the player's moving direction */
10505 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10506 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10507 scroll_x = old_scroll_x;
10509 else /* player has moved vertically */
10511 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10512 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10513 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10515 /* don't scroll over playfield boundaries */
10516 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10517 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10519 /* don't scroll more than one field at a time */
10520 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10522 /* don't scroll against the player's moving direction */
10523 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10524 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10525 scroll_y = old_scroll_y;
10529 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10532 if (!options.network && game.centered_player_nr == -1 &&
10533 !AllPlayersInVisibleScreen())
10535 scroll_x = old_scroll_x;
10536 scroll_y = old_scroll_y;
10540 if (!options.network && !AllPlayersInVisibleScreen())
10542 scroll_x = old_scroll_x;
10543 scroll_y = old_scroll_y;
10548 ScrollScreen(player, SCROLL_INIT);
10549 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10554 player->StepFrame = 0;
10556 if (moved & MP_MOVING)
10558 if (old_jx != jx && old_jy == jy)
10559 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10560 else if (old_jx == jx && old_jy != jy)
10561 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10563 DrawLevelField(jx, jy); /* for "crumbled sand" */
10565 player->last_move_dir = player->MovDir;
10566 player->is_moving = TRUE;
10567 player->is_snapping = FALSE;
10568 player->is_switching = FALSE;
10569 player->is_dropping = FALSE;
10570 player->is_dropping_pressed = FALSE;
10571 player->drop_pressed_delay = 0;
10575 CheckGravityMovementWhenNotMoving(player);
10577 player->is_moving = FALSE;
10579 /* at this point, the player is allowed to move, but cannot move right now
10580 (e.g. because of something blocking the way) -- ensure that the player
10581 is also allowed to move in the next frame (in old versions before 3.1.1,
10582 the player was forced to wait again for eight frames before next try) */
10584 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10585 player->move_delay = 0; /* allow direct movement in the next frame */
10588 if (player->move_delay == -1) /* not yet initialized by DigField() */
10589 player->move_delay = player->move_delay_value;
10591 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10593 TestIfPlayerTouchesBadThing(jx, jy);
10594 TestIfPlayerTouchesCustomElement(jx, jy);
10597 if (!player->active)
10598 RemovePlayer(player);
10603 void ScrollPlayer(struct PlayerInfo *player, int mode)
10605 int jx = player->jx, jy = player->jy;
10606 int last_jx = player->last_jx, last_jy = player->last_jy;
10607 int move_stepsize = TILEX / player->move_delay_value;
10609 #if USE_NEW_PLAYER_SPEED
10610 if (!player->active)
10613 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10616 if (!player->active || player->MovPos == 0)
10620 if (mode == SCROLL_INIT)
10622 player->actual_frame_counter = FrameCounter;
10623 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10625 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10626 Feld[last_jx][last_jy] == EL_EMPTY)
10628 int last_field_block_delay = 0; /* start with no blocking at all */
10629 int block_delay_adjustment = player->block_delay_adjustment;
10631 /* if player blocks last field, add delay for exactly one move */
10632 if (player->block_last_field)
10634 last_field_block_delay += player->move_delay_value;
10636 /* when blocking enabled, prevent moving up despite gravity */
10637 if (game.gravity && player->MovDir == MV_UP)
10638 block_delay_adjustment = -1;
10641 /* add block delay adjustment (also possible when not blocking) */
10642 last_field_block_delay += block_delay_adjustment;
10644 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10645 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10648 #if USE_NEW_PLAYER_SPEED
10649 if (player->MovPos != 0) /* player has not yet reached destination */
10655 else if (!FrameReached(&player->actual_frame_counter, 1))
10659 printf("::: player->MovPos: %d -> %d\n",
10661 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10664 #if USE_NEW_PLAYER_SPEED
10665 if (player->MovPos != 0)
10667 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10668 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10670 /* before DrawPlayer() to draw correct player graphic for this case */
10671 if (player->MovPos == 0)
10672 CheckGravityMovement(player);
10675 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10676 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10678 /* before DrawPlayer() to draw correct player graphic for this case */
10679 if (player->MovPos == 0)
10680 CheckGravityMovement(player);
10683 if (player->MovPos == 0) /* player reached destination field */
10686 printf("::: player reached destination field\n");
10689 if (player->move_delay_reset_counter > 0)
10691 player->move_delay_reset_counter--;
10693 if (player->move_delay_reset_counter == 0)
10695 /* continue with normal speed after quickly moving through gate */
10696 HALVE_PLAYER_SPEED(player);
10698 /* be able to make the next move without delay */
10699 player->move_delay = 0;
10703 player->last_jx = jx;
10704 player->last_jy = jy;
10706 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10707 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10708 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10710 DrawPlayer(player); /* needed here only to cleanup last field */
10711 RemovePlayer(player);
10713 if (local_player->friends_still_needed == 0 ||
10714 IS_SP_ELEMENT(Feld[jx][jy]))
10715 player->LevelSolved = player->GameOver = TRUE;
10718 /* this breaks one level: "machine", level 000 */
10720 int move_direction = player->MovDir;
10721 int enter_side = MV_DIR_OPPOSITE(move_direction);
10722 int leave_side = move_direction;
10723 int old_jx = last_jx;
10724 int old_jy = last_jy;
10725 int old_element = Feld[old_jx][old_jy];
10726 int new_element = Feld[jx][jy];
10728 if (IS_CUSTOM_ELEMENT(old_element))
10729 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10731 player->index_bit, leave_side);
10733 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10734 CE_PLAYER_LEAVES_X,
10735 player->index_bit, leave_side);
10737 if (IS_CUSTOM_ELEMENT(new_element))
10738 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10739 player->index_bit, enter_side);
10741 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10742 CE_PLAYER_ENTERS_X,
10743 player->index_bit, enter_side);
10745 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10746 CE_MOVE_OF_X, move_direction);
10749 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10751 TestIfPlayerTouchesBadThing(jx, jy);
10752 TestIfPlayerTouchesCustomElement(jx, jy);
10754 /* needed because pushed element has not yet reached its destination,
10755 so it would trigger a change event at its previous field location */
10756 if (!player->is_pushing)
10757 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10759 if (!player->active)
10760 RemovePlayer(player);
10763 if (level.use_step_counter)
10773 if (TimeLeft <= 10 && setup.time_limit)
10774 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10776 DrawGameValue_Time(TimeLeft);
10778 if (!TimeLeft && setup.time_limit)
10779 for (i = 0; i < MAX_PLAYERS; i++)
10780 KillPlayer(&stored_player[i]);
10782 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10783 DrawGameValue_Time(TimePlayed);
10786 if (tape.single_step && tape.recording && !tape.pausing &&
10787 !player->programmed_action)
10788 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10792 void ScrollScreen(struct PlayerInfo *player, int mode)
10794 static unsigned long screen_frame_counter = 0;
10796 if (mode == SCROLL_INIT)
10798 /* set scrolling step size according to actual player's moving speed */
10799 ScrollStepSize = TILEX / player->move_delay_value;
10801 screen_frame_counter = FrameCounter;
10802 ScreenMovDir = player->MovDir;
10803 ScreenMovPos = player->MovPos;
10804 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10807 else if (!FrameReached(&screen_frame_counter, 1))
10812 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10813 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10814 redraw_mask |= REDRAW_FIELD;
10817 ScreenMovDir = MV_NONE;
10820 void TestIfPlayerTouchesCustomElement(int x, int y)
10822 static int xy[4][2] =
10829 static int trigger_sides[4][2] =
10831 /* center side border side */
10832 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10833 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10834 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10835 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10837 static int touch_dir[4] =
10839 MV_LEFT | MV_RIGHT,
10844 int center_element = Feld[x][y]; /* should always be non-moving! */
10847 for (i = 0; i < NUM_DIRECTIONS; i++)
10849 int xx = x + xy[i][0];
10850 int yy = y + xy[i][1];
10851 int center_side = trigger_sides[i][0];
10852 int border_side = trigger_sides[i][1];
10853 int border_element;
10855 if (!IN_LEV_FIELD(xx, yy))
10858 if (IS_PLAYER(x, y))
10860 struct PlayerInfo *player = PLAYERINFO(x, y);
10862 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10863 border_element = Feld[xx][yy]; /* may be moving! */
10864 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10865 border_element = Feld[xx][yy];
10866 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10867 border_element = MovingOrBlocked2Element(xx, yy);
10869 continue; /* center and border element do not touch */
10871 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10872 player->index_bit, border_side);
10873 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10874 CE_PLAYER_TOUCHES_X,
10875 player->index_bit, border_side);
10877 else if (IS_PLAYER(xx, yy))
10879 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10881 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10883 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10884 continue; /* center and border element do not touch */
10887 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10888 player->index_bit, center_side);
10889 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10890 CE_PLAYER_TOUCHES_X,
10891 player->index_bit, center_side);
10897 #if USE_ELEMENT_TOUCHING_BUGFIX
10899 void TestIfElementTouchesCustomElement(int x, int y)
10901 static int xy[4][2] =
10908 static int trigger_sides[4][2] =
10910 /* center side border side */
10911 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10912 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10913 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10914 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10916 static int touch_dir[4] =
10918 MV_LEFT | MV_RIGHT,
10923 boolean change_center_element = FALSE;
10924 int center_element = Feld[x][y]; /* should always be non-moving! */
10925 int border_element_old[NUM_DIRECTIONS];
10928 for (i = 0; i < NUM_DIRECTIONS; i++)
10930 int xx = x + xy[i][0];
10931 int yy = y + xy[i][1];
10932 int border_element;
10934 border_element_old[i] = -1;
10936 if (!IN_LEV_FIELD(xx, yy))
10939 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10940 border_element = Feld[xx][yy]; /* may be moving! */
10941 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10942 border_element = Feld[xx][yy];
10943 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10944 border_element = MovingOrBlocked2Element(xx, yy);
10946 continue; /* center and border element do not touch */
10948 border_element_old[i] = border_element;
10951 for (i = 0; i < NUM_DIRECTIONS; i++)
10953 int xx = x + xy[i][0];
10954 int yy = y + xy[i][1];
10955 int center_side = trigger_sides[i][0];
10956 int border_element = border_element_old[i];
10958 if (border_element == -1)
10961 /* check for change of border element */
10962 CheckElementChangeBySide(xx, yy, border_element, center_element,
10963 CE_TOUCHING_X, center_side);
10966 for (i = 0; i < NUM_DIRECTIONS; i++)
10968 int border_side = trigger_sides[i][1];
10969 int border_element = border_element_old[i];
10971 if (border_element == -1)
10974 /* check for change of center element (but change it only once) */
10975 if (!change_center_element)
10976 change_center_element =
10977 CheckElementChangeBySide(x, y, center_element, border_element,
10978 CE_TOUCHING_X, border_side);
10984 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10986 static int xy[4][2] =
10993 static int trigger_sides[4][2] =
10995 /* center side border side */
10996 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10997 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10998 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10999 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11001 static int touch_dir[4] =
11003 MV_LEFT | MV_RIGHT,
11008 boolean change_center_element = FALSE;
11009 int center_element = Feld[x][y]; /* should always be non-moving! */
11012 for (i = 0; i < NUM_DIRECTIONS; i++)
11014 int xx = x + xy[i][0];
11015 int yy = y + xy[i][1];
11016 int center_side = trigger_sides[i][0];
11017 int border_side = trigger_sides[i][1];
11018 int border_element;
11020 if (!IN_LEV_FIELD(xx, yy))
11023 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11024 border_element = Feld[xx][yy]; /* may be moving! */
11025 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11026 border_element = Feld[xx][yy];
11027 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11028 border_element = MovingOrBlocked2Element(xx, yy);
11030 continue; /* center and border element do not touch */
11032 /* check for change of center element (but change it only once) */
11033 if (!change_center_element)
11034 change_center_element =
11035 CheckElementChangeBySide(x, y, center_element, border_element,
11036 CE_TOUCHING_X, border_side);
11038 /* check for change of border element */
11039 CheckElementChangeBySide(xx, yy, border_element, center_element,
11040 CE_TOUCHING_X, center_side);
11046 void TestIfElementHitsCustomElement(int x, int y, int direction)
11048 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11049 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11050 int hitx = x + dx, hity = y + dy;
11051 int hitting_element = Feld[x][y];
11052 int touched_element;
11054 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11057 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11058 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11060 if (IN_LEV_FIELD(hitx, hity))
11062 int opposite_direction = MV_DIR_OPPOSITE(direction);
11063 int hitting_side = direction;
11064 int touched_side = opposite_direction;
11065 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11066 MovDir[hitx][hity] != direction ||
11067 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11073 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11074 CE_HITTING_X, touched_side);
11076 CheckElementChangeBySide(hitx, hity, touched_element,
11077 hitting_element, CE_HIT_BY_X, hitting_side);
11079 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11080 CE_HIT_BY_SOMETHING, opposite_direction);
11084 /* "hitting something" is also true when hitting the playfield border */
11085 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11086 CE_HITTING_SOMETHING, direction);
11090 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11092 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11093 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11094 int hitx = x + dx, hity = y + dy;
11095 int hitting_element = Feld[x][y];
11096 int touched_element;
11098 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11099 !IS_FREE(hitx, hity) &&
11100 (!IS_MOVING(hitx, hity) ||
11101 MovDir[hitx][hity] != direction ||
11102 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11105 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11109 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11113 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11114 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11116 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11117 EP_CAN_SMASH_EVERYTHING, direction);
11119 if (IN_LEV_FIELD(hitx, hity))
11121 int opposite_direction = MV_DIR_OPPOSITE(direction);
11122 int hitting_side = direction;
11123 int touched_side = opposite_direction;
11125 int touched_element = MovingOrBlocked2Element(hitx, hity);
11128 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11129 MovDir[hitx][hity] != direction ||
11130 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11139 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11140 CE_SMASHED_BY_SOMETHING, opposite_direction);
11142 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11143 CE_OTHER_IS_SMASHING, touched_side);
11145 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11146 CE_OTHER_GETS_SMASHED, hitting_side);
11152 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11154 int i, kill_x = -1, kill_y = -1;
11156 int bad_element = -1;
11157 static int test_xy[4][2] =
11164 static int test_dir[4] =
11172 for (i = 0; i < NUM_DIRECTIONS; i++)
11174 int test_x, test_y, test_move_dir, test_element;
11176 test_x = good_x + test_xy[i][0];
11177 test_y = good_y + test_xy[i][1];
11179 if (!IN_LEV_FIELD(test_x, test_y))
11183 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11185 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11187 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11188 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11190 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11191 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11195 bad_element = test_element;
11201 if (kill_x != -1 || kill_y != -1)
11203 if (IS_PLAYER(good_x, good_y))
11205 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11207 if (player->shield_deadly_time_left > 0 &&
11208 !IS_INDESTRUCTIBLE(bad_element))
11209 Bang(kill_x, kill_y);
11210 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11211 KillPlayer(player);
11214 Bang(good_x, good_y);
11218 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11220 int i, kill_x = -1, kill_y = -1;
11221 int bad_element = Feld[bad_x][bad_y];
11222 static int test_xy[4][2] =
11229 static int touch_dir[4] =
11231 MV_LEFT | MV_RIGHT,
11236 static int test_dir[4] =
11244 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11247 for (i = 0; i < NUM_DIRECTIONS; i++)
11249 int test_x, test_y, test_move_dir, test_element;
11251 test_x = bad_x + test_xy[i][0];
11252 test_y = bad_y + test_xy[i][1];
11253 if (!IN_LEV_FIELD(test_x, test_y))
11257 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11259 test_element = Feld[test_x][test_y];
11261 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11262 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11264 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11265 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11267 /* good thing is player or penguin that does not move away */
11268 if (IS_PLAYER(test_x, test_y))
11270 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11272 if (bad_element == EL_ROBOT && player->is_moving)
11273 continue; /* robot does not kill player if he is moving */
11275 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11277 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11278 continue; /* center and border element do not touch */
11285 else if (test_element == EL_PENGUIN)
11294 if (kill_x != -1 || kill_y != -1)
11296 if (IS_PLAYER(kill_x, kill_y))
11298 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11300 if (player->shield_deadly_time_left > 0 &&
11301 !IS_INDESTRUCTIBLE(bad_element))
11302 Bang(bad_x, bad_y);
11303 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11304 KillPlayer(player);
11307 Bang(kill_x, kill_y);
11311 void TestIfPlayerTouchesBadThing(int x, int y)
11313 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11316 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11318 TestIfGoodThingHitsBadThing(x, y, move_dir);
11321 void TestIfBadThingTouchesPlayer(int x, int y)
11323 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11326 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11328 TestIfBadThingHitsGoodThing(x, y, move_dir);
11331 void TestIfFriendTouchesBadThing(int x, int y)
11333 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11336 void TestIfBadThingTouchesFriend(int x, int y)
11338 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11341 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11343 int i, kill_x = bad_x, kill_y = bad_y;
11344 static int xy[4][2] =
11352 for (i = 0; i < NUM_DIRECTIONS; i++)
11356 x = bad_x + xy[i][0];
11357 y = bad_y + xy[i][1];
11358 if (!IN_LEV_FIELD(x, y))
11361 element = Feld[x][y];
11362 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11363 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11371 if (kill_x != bad_x || kill_y != bad_y)
11372 Bang(bad_x, bad_y);
11375 void KillPlayer(struct PlayerInfo *player)
11377 int jx = player->jx, jy = player->jy;
11379 if (!player->active)
11382 /* remove accessible field at the player's position */
11383 Feld[jx][jy] = EL_EMPTY;
11385 /* deactivate shield (else Bang()/Explode() would not work right) */
11386 player->shield_normal_time_left = 0;
11387 player->shield_deadly_time_left = 0;
11390 BuryPlayer(player);
11393 static void KillPlayerUnlessEnemyProtected(int x, int y)
11395 if (!PLAYER_ENEMY_PROTECTED(x, y))
11396 KillPlayer(PLAYERINFO(x, y));
11399 static void KillPlayerUnlessExplosionProtected(int x, int y)
11401 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11402 KillPlayer(PLAYERINFO(x, y));
11405 void BuryPlayer(struct PlayerInfo *player)
11407 int jx = player->jx, jy = player->jy;
11409 if (!player->active)
11412 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11413 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11415 player->GameOver = TRUE;
11416 RemovePlayer(player);
11419 void RemovePlayer(struct PlayerInfo *player)
11421 int jx = player->jx, jy = player->jy;
11422 int i, found = FALSE;
11424 player->present = FALSE;
11425 player->active = FALSE;
11427 if (!ExplodeField[jx][jy])
11428 StorePlayer[jx][jy] = 0;
11430 if (player->is_moving)
11431 DrawLevelField(player->last_jx, player->last_jy);
11433 for (i = 0; i < MAX_PLAYERS; i++)
11434 if (stored_player[i].active)
11438 AllPlayersGone = TRUE;
11444 #if USE_NEW_SNAP_DELAY
11445 static void setFieldForSnapping(int x, int y, int element, int direction)
11447 struct ElementInfo *ei = &element_info[element];
11448 int direction_bit = MV_DIR_TO_BIT(direction);
11449 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11450 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11451 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11453 Feld[x][y] = EL_ELEMENT_SNAPPING;
11454 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11456 ResetGfxAnimation(x, y);
11458 GfxElement[x][y] = element;
11459 GfxAction[x][y] = action;
11460 GfxDir[x][y] = direction;
11461 GfxFrame[x][y] = -1;
11466 =============================================================================
11467 checkDiagonalPushing()
11468 -----------------------------------------------------------------------------
11469 check if diagonal input device direction results in pushing of object
11470 (by checking if the alternative direction is walkable, diggable, ...)
11471 =============================================================================
11474 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11475 int x, int y, int real_dx, int real_dy)
11477 int jx, jy, dx, dy, xx, yy;
11479 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11482 /* diagonal direction: check alternative direction */
11487 xx = jx + (dx == 0 ? real_dx : 0);
11488 yy = jy + (dy == 0 ? real_dy : 0);
11490 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11494 =============================================================================
11496 -----------------------------------------------------------------------------
11497 x, y: field next to player (non-diagonal) to try to dig to
11498 real_dx, real_dy: direction as read from input device (can be diagonal)
11499 =============================================================================
11502 int DigField(struct PlayerInfo *player,
11503 int oldx, int oldy, int x, int y,
11504 int real_dx, int real_dy, int mode)
11506 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11507 boolean player_was_pushing = player->is_pushing;
11508 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11509 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11510 int jx = oldx, jy = oldy;
11511 int dx = x - jx, dy = y - jy;
11512 int nextx = x + dx, nexty = y + dy;
11513 int move_direction = (dx == -1 ? MV_LEFT :
11514 dx == +1 ? MV_RIGHT :
11516 dy == +1 ? MV_DOWN : MV_NONE);
11517 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11518 int dig_side = MV_DIR_OPPOSITE(move_direction);
11519 int old_element = Feld[jx][jy];
11520 #if USE_FIXED_DONT_RUN_INTO
11521 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11527 if (is_player) /* function can also be called by EL_PENGUIN */
11529 if (player->MovPos == 0)
11531 player->is_digging = FALSE;
11532 player->is_collecting = FALSE;
11535 if (player->MovPos == 0) /* last pushing move finished */
11536 player->is_pushing = FALSE;
11538 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11540 player->is_switching = FALSE;
11541 player->push_delay = -1;
11543 return MP_NO_ACTION;
11547 #if !USE_FIXED_DONT_RUN_INTO
11548 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11549 return MP_NO_ACTION;
11552 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11553 old_element = Back[jx][jy];
11555 /* in case of element dropped at player position, check background */
11556 else if (Back[jx][jy] != EL_EMPTY &&
11557 game.engine_version >= VERSION_IDENT(2,2,0,0))
11558 old_element = Back[jx][jy];
11561 #if USE_FIXED_DONT_RUN_INTO
11562 if (player_can_move && DONT_RUN_INTO(element))
11564 if (element == EL_ACID && dx == 0 && dy == 1)
11567 Feld[jx][jy] = EL_PLAYER_1;
11568 InitMovingField(jx, jy, MV_DOWN);
11569 Store[jx][jy] = EL_ACID;
11570 ContinueMoving(jx, jy);
11571 BuryPlayer(player);
11574 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11576 return MP_DONT_RUN_INTO;
11582 #if USE_FIXED_DONT_RUN_INTO
11583 if (player_can_move && DONT_RUN_INTO(element))
11585 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11587 return MP_DONT_RUN_INTO;
11592 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11593 return MP_NO_ACTION; /* field has no opening in this direction */
11595 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11596 return MP_NO_ACTION; /* field has no opening in this direction */
11599 #if USE_FIXED_DONT_RUN_INTO
11600 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11603 Feld[jx][jy] = EL_PLAYER_1;
11604 InitMovingField(jx, jy, MV_DOWN);
11605 Store[jx][jy] = EL_ACID;
11606 ContinueMoving(jx, jy);
11607 BuryPlayer(player);
11609 return MP_DONT_RUN_INTO;
11615 #if USE_FIXED_DONT_RUN_INTO
11616 if (player_can_move && DONT_RUN_INTO(element))
11618 if (element == EL_ACID && dx == 0 && dy == 1)
11621 Feld[jx][jy] = EL_PLAYER_1;
11622 InitMovingField(jx, jy, MV_DOWN);
11623 Store[jx][jy] = EL_ACID;
11624 ContinueMoving(jx, jy);
11625 BuryPlayer(player);
11628 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11630 return MP_DONT_RUN_INTO;
11635 #if USE_FIXED_DONT_RUN_INTO
11636 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11637 return MP_NO_ACTION;
11640 #if !USE_FIXED_DONT_RUN_INTO
11641 element = Feld[x][y];
11644 collect_count = element_info[element].collect_count_initial;
11646 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11647 return MP_NO_ACTION;
11649 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11650 player_can_move = player_can_move_or_snap;
11652 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11653 game.engine_version >= VERSION_IDENT(2,2,0,0))
11655 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11656 player->index_bit, dig_side);
11657 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11658 player->index_bit, dig_side);
11660 if (Feld[x][y] != element) /* field changed by snapping */
11663 return MP_NO_ACTION;
11666 if (game.gravity && is_player && !player->is_auto_moving &&
11667 canFallDown(player) && move_direction != MV_DOWN &&
11668 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11669 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11671 if (player_can_move &&
11672 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11674 int sound_element = SND_ELEMENT(element);
11675 int sound_action = ACTION_WALKING;
11677 if (IS_RND_GATE(element))
11679 if (!player->key[RND_GATE_NR(element)])
11680 return MP_NO_ACTION;
11682 else if (IS_RND_GATE_GRAY(element))
11684 if (!player->key[RND_GATE_GRAY_NR(element)])
11685 return MP_NO_ACTION;
11687 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11689 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11690 return MP_NO_ACTION;
11692 else if (element == EL_EXIT_OPEN ||
11693 element == EL_SP_EXIT_OPEN ||
11694 element == EL_SP_EXIT_OPENING)
11696 sound_action = ACTION_PASSING; /* player is passing exit */
11698 else if (element == EL_EMPTY)
11700 sound_action = ACTION_MOVING; /* nothing to walk on */
11703 /* play sound from background or player, whatever is available */
11704 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11705 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11707 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11709 else if (player_can_move &&
11710 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11712 if (!ACCESS_FROM(element, opposite_direction))
11713 return MP_NO_ACTION; /* field not accessible from this direction */
11715 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11716 return MP_NO_ACTION;
11718 if (IS_EM_GATE(element))
11720 if (!player->key[EM_GATE_NR(element)])
11721 return MP_NO_ACTION;
11723 else if (IS_EM_GATE_GRAY(element))
11725 if (!player->key[EM_GATE_GRAY_NR(element)])
11726 return MP_NO_ACTION;
11728 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11730 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11731 return MP_NO_ACTION;
11733 else if (IS_SP_PORT(element))
11735 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11736 element == EL_SP_GRAVITY_PORT_RIGHT ||
11737 element == EL_SP_GRAVITY_PORT_UP ||
11738 element == EL_SP_GRAVITY_PORT_DOWN)
11739 game.gravity = !game.gravity;
11740 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11741 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11742 element == EL_SP_GRAVITY_ON_PORT_UP ||
11743 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11744 game.gravity = TRUE;
11745 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11746 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11747 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11748 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11749 game.gravity = FALSE;
11752 /* automatically move to the next field with double speed */
11753 player->programmed_action = move_direction;
11755 if (player->move_delay_reset_counter == 0)
11757 player->move_delay_reset_counter = 2; /* two double speed steps */
11759 DOUBLE_PLAYER_SPEED(player);
11762 PlayLevelSoundAction(x, y, ACTION_PASSING);
11764 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11768 if (mode != DF_SNAP)
11770 GfxElement[x][y] = GFX_ELEMENT(element);
11771 player->is_digging = TRUE;
11774 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11776 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11777 player->index_bit, dig_side);
11779 if (mode == DF_SNAP)
11781 #if USE_NEW_SNAP_DELAY
11782 if (level.block_snap_field)
11783 setFieldForSnapping(x, y, element, move_direction);
11785 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11787 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11790 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11791 player->index_bit, dig_side);
11794 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11798 if (is_player && mode != DF_SNAP)
11800 GfxElement[x][y] = element;
11801 player->is_collecting = TRUE;
11804 if (element == EL_SPEED_PILL)
11806 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11808 else if (element == EL_EXTRA_TIME && level.time > 0)
11810 TimeLeft += level.extra_time;
11811 DrawGameValue_Time(TimeLeft);
11813 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11815 player->shield_normal_time_left += level.shield_normal_time;
11816 if (element == EL_SHIELD_DEADLY)
11817 player->shield_deadly_time_left += level.shield_deadly_time;
11819 else if (element == EL_DYNAMITE ||
11820 element == EL_EM_DYNAMITE ||
11821 element == EL_SP_DISK_RED)
11823 if (player->inventory_size < MAX_INVENTORY_SIZE)
11824 player->inventory_element[player->inventory_size++] = element;
11827 DrawGameDoorValues();
11829 DrawGameValue_Dynamite(local_player->inventory_size);
11832 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11834 player->dynabomb_count++;
11835 player->dynabombs_left++;
11837 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11839 player->dynabomb_size++;
11841 else if (element == EL_DYNABOMB_INCREASE_POWER)
11843 player->dynabomb_xl = TRUE;
11845 else if (IS_KEY(element))
11847 player->key[KEY_NR(element)] = TRUE;
11850 DrawGameDoorValues();
11852 DrawGameValue_Keys(player->key);
11855 redraw_mask |= REDRAW_DOOR_1;
11857 else if (IS_ENVELOPE(element))
11859 player->show_envelope = element;
11861 else if (element == EL_EMC_LENSES)
11863 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11865 RedrawAllInvisibleElementsForLenses();
11867 else if (element == EL_EMC_MAGNIFIER)
11869 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11871 RedrawAllInvisibleElementsForMagnifier();
11873 else if (IS_DROPPABLE(element) ||
11874 IS_THROWABLE(element)) /* can be collected and dropped */
11878 if (collect_count == 0)
11879 player->inventory_infinite_element = element;
11881 for (i = 0; i < collect_count; i++)
11882 if (player->inventory_size < MAX_INVENTORY_SIZE)
11883 player->inventory_element[player->inventory_size++] = element;
11886 DrawGameDoorValues();
11888 DrawGameValue_Dynamite(local_player->inventory_size);
11891 else if (collect_count > 0)
11893 local_player->gems_still_needed -= collect_count;
11894 if (local_player->gems_still_needed < 0)
11895 local_player->gems_still_needed = 0;
11897 DrawGameValue_Emeralds(local_player->gems_still_needed);
11900 RaiseScoreElement(element);
11901 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11904 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11905 player->index_bit, dig_side);
11907 if (mode == DF_SNAP)
11909 #if USE_NEW_SNAP_DELAY
11910 if (level.block_snap_field)
11911 setFieldForSnapping(x, y, element, move_direction);
11913 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11915 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11918 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11919 player->index_bit, dig_side);
11922 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11924 if (mode == DF_SNAP && element != EL_BD_ROCK)
11925 return MP_NO_ACTION;
11927 if (CAN_FALL(element) && dy)
11928 return MP_NO_ACTION;
11930 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11931 !(element == EL_SPRING && level.use_spring_bug))
11932 return MP_NO_ACTION;
11934 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11935 ((move_direction & MV_VERTICAL &&
11936 ((element_info[element].move_pattern & MV_LEFT &&
11937 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11938 (element_info[element].move_pattern & MV_RIGHT &&
11939 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11940 (move_direction & MV_HORIZONTAL &&
11941 ((element_info[element].move_pattern & MV_UP &&
11942 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11943 (element_info[element].move_pattern & MV_DOWN &&
11944 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11945 return MP_NO_ACTION;
11947 /* do not push elements already moving away faster than player */
11948 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11949 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11950 return MP_NO_ACTION;
11952 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11954 if (player->push_delay_value == -1 || !player_was_pushing)
11955 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11957 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11959 if (player->push_delay_value == -1)
11960 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11962 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11964 if (!player->is_pushing)
11965 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11968 player->is_pushing = TRUE;
11970 if (!(IN_LEV_FIELD(nextx, nexty) &&
11971 (IS_FREE(nextx, nexty) ||
11972 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11973 IS_SB_ELEMENT(element)))))
11974 return MP_NO_ACTION;
11976 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11977 return MP_NO_ACTION;
11979 if (player->push_delay == -1) /* new pushing; restart delay */
11980 player->push_delay = 0;
11982 if (player->push_delay < player->push_delay_value &&
11983 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11984 element != EL_SPRING && element != EL_BALLOON)
11986 /* make sure that there is no move delay before next try to push */
11987 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11988 player->move_delay = 0;
11990 return MP_NO_ACTION;
11993 if (IS_SB_ELEMENT(element))
11995 if (element == EL_SOKOBAN_FIELD_FULL)
11997 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11998 local_player->sokobanfields_still_needed++;
12001 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12003 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12004 local_player->sokobanfields_still_needed--;
12007 Feld[x][y] = EL_SOKOBAN_OBJECT;
12009 if (Back[x][y] == Back[nextx][nexty])
12010 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12011 else if (Back[x][y] != 0)
12012 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12015 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12018 if (local_player->sokobanfields_still_needed == 0 &&
12019 game.emulation == EMU_SOKOBAN)
12021 player->LevelSolved = player->GameOver = TRUE;
12022 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12026 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12028 InitMovingField(x, y, move_direction);
12029 GfxAction[x][y] = ACTION_PUSHING;
12031 if (mode == DF_SNAP)
12032 ContinueMoving(x, y);
12034 MovPos[x][y] = (dx != 0 ? dx : dy);
12036 Pushed[x][y] = TRUE;
12037 Pushed[nextx][nexty] = TRUE;
12039 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12040 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12042 player->push_delay_value = -1; /* get new value later */
12044 /* check for element change _after_ element has been pushed */
12045 if (game.use_change_when_pushing_bug)
12047 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12048 player->index_bit, dig_side);
12049 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12050 player->index_bit, dig_side);
12053 else if (IS_SWITCHABLE(element))
12055 if (PLAYER_SWITCHING(player, x, y))
12057 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12058 player->index_bit, dig_side);
12063 player->is_switching = TRUE;
12064 player->switch_x = x;
12065 player->switch_y = y;
12067 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12069 if (element == EL_ROBOT_WHEEL)
12071 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12075 DrawLevelField(x, y);
12077 else if (element == EL_SP_TERMINAL)
12082 SCAN_PLAYFIELD(xx, yy)
12084 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12087 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12089 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12090 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12093 else if (IS_BELT_SWITCH(element))
12095 ToggleBeltSwitch(x, y);
12097 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12098 element == EL_SWITCHGATE_SWITCH_DOWN)
12100 ToggleSwitchgateSwitch(x, y);
12102 else if (element == EL_LIGHT_SWITCH ||
12103 element == EL_LIGHT_SWITCH_ACTIVE)
12105 ToggleLightSwitch(x, y);
12107 else if (element == EL_TIMEGATE_SWITCH)
12109 ActivateTimegateSwitch(x, y);
12111 else if (element == EL_BALLOON_SWITCH_LEFT ||
12112 element == EL_BALLOON_SWITCH_RIGHT ||
12113 element == EL_BALLOON_SWITCH_UP ||
12114 element == EL_BALLOON_SWITCH_DOWN ||
12115 element == EL_BALLOON_SWITCH_NONE ||
12116 element == EL_BALLOON_SWITCH_ANY)
12118 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12119 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12120 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12121 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12122 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12125 else if (element == EL_LAMP)
12127 Feld[x][y] = EL_LAMP_ACTIVE;
12128 local_player->lights_still_needed--;
12130 ResetGfxAnimation(x, y);
12131 DrawLevelField(x, y);
12133 else if (element == EL_TIME_ORB_FULL)
12135 Feld[x][y] = EL_TIME_ORB_EMPTY;
12137 if (level.time > 0 || level.use_time_orb_bug)
12139 TimeLeft += level.time_orb_time;
12140 DrawGameValue_Time(TimeLeft);
12143 ResetGfxAnimation(x, y);
12144 DrawLevelField(x, y);
12146 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12147 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12151 game.ball_state = !game.ball_state;
12154 SCAN_PLAYFIELD(xx, yy)
12156 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12159 int e = Feld[xx][yy];
12161 if (game.ball_state)
12163 if (e == EL_EMC_MAGIC_BALL)
12164 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12165 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12166 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12170 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12171 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12172 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12173 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12179 player->index_bit, dig_side);
12181 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12182 player->index_bit, dig_side);
12184 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12185 player->index_bit, dig_side);
12191 if (!PLAYER_SWITCHING(player, x, y))
12193 player->is_switching = TRUE;
12194 player->switch_x = x;
12195 player->switch_y = y;
12197 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12198 player->index_bit, dig_side);
12199 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12200 player->index_bit, dig_side);
12202 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12203 player->index_bit, dig_side);
12204 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12205 player->index_bit, dig_side);
12208 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12209 player->index_bit, dig_side);
12210 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12211 player->index_bit, dig_side);
12213 return MP_NO_ACTION;
12216 player->push_delay = -1;
12218 if (is_player) /* function can also be called by EL_PENGUIN */
12220 if (Feld[x][y] != element) /* really digged/collected something */
12221 player->is_collecting = !player->is_digging;
12227 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12229 int jx = player->jx, jy = player->jy;
12230 int x = jx + dx, y = jy + dy;
12231 int snap_direction = (dx == -1 ? MV_LEFT :
12232 dx == +1 ? MV_RIGHT :
12234 dy == +1 ? MV_DOWN : MV_NONE);
12235 boolean can_continue_snapping = (level.continuous_snapping &&
12236 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12238 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12241 if (!player->active || !IN_LEV_FIELD(x, y))
12249 if (player->MovPos == 0)
12250 player->is_pushing = FALSE;
12252 player->is_snapping = FALSE;
12254 if (player->MovPos == 0)
12256 player->is_moving = FALSE;
12257 player->is_digging = FALSE;
12258 player->is_collecting = FALSE;
12264 #if USE_NEW_CONTINUOUS_SNAPPING
12265 /* prevent snapping with already pressed snap key when not allowed */
12266 if (player->is_snapping && !can_continue_snapping)
12269 if (player->is_snapping)
12273 player->MovDir = snap_direction;
12275 if (player->MovPos == 0)
12277 player->is_moving = FALSE;
12278 player->is_digging = FALSE;
12279 player->is_collecting = FALSE;
12282 player->is_dropping = FALSE;
12283 player->is_dropping_pressed = FALSE;
12284 player->drop_pressed_delay = 0;
12286 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12289 player->is_snapping = TRUE;
12291 if (player->MovPos == 0)
12293 player->is_moving = FALSE;
12294 player->is_digging = FALSE;
12295 player->is_collecting = FALSE;
12298 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12299 DrawLevelField(player->last_jx, player->last_jy);
12301 DrawLevelField(x, y);
12306 boolean DropElement(struct PlayerInfo *player)
12308 int old_element, new_element;
12309 int dropx = player->jx, dropy = player->jy;
12310 int drop_direction = player->MovDir;
12311 int drop_side = drop_direction;
12312 int drop_element = (player->inventory_size > 0 ?
12313 player->inventory_element[player->inventory_size - 1] :
12314 player->inventory_infinite_element != EL_UNDEFINED ?
12315 player->inventory_infinite_element :
12316 player->dynabombs_left > 0 ?
12317 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12320 player->is_dropping_pressed = TRUE;
12322 /* do not drop an element on top of another element; when holding drop key
12323 pressed without moving, dropped element must move away before the next
12324 element can be dropped (this is especially important if the next element
12325 is dynamite, which can be placed on background for historical reasons) */
12326 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12329 if (IS_THROWABLE(drop_element))
12331 dropx += GET_DX_FROM_DIR(drop_direction);
12332 dropy += GET_DY_FROM_DIR(drop_direction);
12334 if (!IN_LEV_FIELD(dropx, dropy))
12338 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12339 new_element = drop_element; /* default: no change when dropping */
12341 /* check if player is active, not moving and ready to drop */
12342 if (!player->active || player->MovPos || player->drop_delay > 0)
12345 /* check if player has anything that can be dropped */
12346 if (new_element == EL_UNDEFINED)
12349 /* check if drop key was pressed long enough for EM style dynamite */
12350 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12353 /* check if anything can be dropped at the current position */
12354 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12357 /* collected custom elements can only be dropped on empty fields */
12358 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12361 if (old_element != EL_EMPTY)
12362 Back[dropx][dropy] = old_element; /* store old element on this field */
12364 ResetGfxAnimation(dropx, dropy);
12365 ResetRandomAnimationValue(dropx, dropy);
12367 if (player->inventory_size > 0 ||
12368 player->inventory_infinite_element != EL_UNDEFINED)
12370 if (player->inventory_size > 0)
12372 player->inventory_size--;
12375 DrawGameDoorValues();
12377 DrawGameValue_Dynamite(local_player->inventory_size);
12380 if (new_element == EL_DYNAMITE)
12381 new_element = EL_DYNAMITE_ACTIVE;
12382 else if (new_element == EL_EM_DYNAMITE)
12383 new_element = EL_EM_DYNAMITE_ACTIVE;
12384 else if (new_element == EL_SP_DISK_RED)
12385 new_element = EL_SP_DISK_RED_ACTIVE;
12388 Feld[dropx][dropy] = new_element;
12390 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12391 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12392 el2img(Feld[dropx][dropy]), 0);
12394 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12396 /* needed if previous element just changed to "empty" in the last frame */
12397 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12399 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12400 player->index_bit, drop_side);
12401 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12403 player->index_bit, drop_side);
12405 TestIfElementTouchesCustomElement(dropx, dropy);
12407 else /* player is dropping a dyna bomb */
12409 player->dynabombs_left--;
12411 Feld[dropx][dropy] = new_element;
12413 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12414 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12415 el2img(Feld[dropx][dropy]), 0);
12417 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12420 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12421 InitField_WithBug1(dropx, dropy, FALSE);
12423 new_element = Feld[dropx][dropy]; /* element might have changed */
12425 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12426 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12428 int move_direction, nextx, nexty;
12430 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12431 MovDir[dropx][dropy] = drop_direction;
12433 move_direction = MovDir[dropx][dropy];
12434 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12435 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12437 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12438 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12441 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12442 player->is_dropping = TRUE;
12444 player->drop_pressed_delay = 0;
12445 player->is_dropping_pressed = FALSE;
12447 player->drop_x = dropx;
12448 player->drop_y = dropy;
12453 /* ------------------------------------------------------------------------- */
12454 /* game sound playing functions */
12455 /* ------------------------------------------------------------------------- */
12457 static int *loop_sound_frame = NULL;
12458 static int *loop_sound_volume = NULL;
12460 void InitPlayLevelSound()
12462 int num_sounds = getSoundListSize();
12464 checked_free(loop_sound_frame);
12465 checked_free(loop_sound_volume);
12467 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12468 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12471 static void PlayLevelSound(int x, int y, int nr)
12473 int sx = SCREENX(x), sy = SCREENY(y);
12474 int volume, stereo_position;
12475 int max_distance = 8;
12476 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12478 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12479 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12482 if (!IN_LEV_FIELD(x, y) ||
12483 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12484 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12487 volume = SOUND_MAX_VOLUME;
12489 if (!IN_SCR_FIELD(sx, sy))
12491 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12492 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12494 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12497 stereo_position = (SOUND_MAX_LEFT +
12498 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12499 (SCR_FIELDX + 2 * max_distance));
12501 if (IS_LOOP_SOUND(nr))
12503 /* This assures that quieter loop sounds do not overwrite louder ones,
12504 while restarting sound volume comparison with each new game frame. */
12506 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12509 loop_sound_volume[nr] = volume;
12510 loop_sound_frame[nr] = FrameCounter;
12513 PlaySoundExt(nr, volume, stereo_position, type);
12516 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12518 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12519 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12520 y < LEVELY(BY1) ? LEVELY(BY1) :
12521 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12525 static void PlayLevelSoundAction(int x, int y, int action)
12527 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12530 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12532 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12534 if (sound_effect != SND_UNDEFINED)
12535 PlayLevelSound(x, y, sound_effect);
12538 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12541 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12543 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12544 PlayLevelSound(x, y, sound_effect);
12547 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12549 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12551 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12552 PlayLevelSound(x, y, sound_effect);
12555 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12557 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12559 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12560 StopSound(sound_effect);
12563 static void PlayLevelMusic()
12565 if (levelset.music[level_nr] != MUS_UNDEFINED)
12566 PlayMusic(levelset.music[level_nr]); /* from config file */
12568 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12571 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12573 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12578 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12582 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12586 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12590 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12594 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12598 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12602 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12605 case SAMPLE_android_clone:
12606 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12609 case SAMPLE_android_move:
12610 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12613 case SAMPLE_spring:
12614 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12618 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12622 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12625 case SAMPLE_eater_eat:
12626 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12630 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12633 case SAMPLE_collect:
12634 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12637 case SAMPLE_diamond:
12638 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12641 case SAMPLE_squash:
12642 /* !!! CHECK THIS !!! */
12644 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12646 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12650 case SAMPLE_wonderfall:
12651 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12655 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12659 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12663 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12667 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12671 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12675 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12678 case SAMPLE_wonder:
12679 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12683 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12686 case SAMPLE_exit_open:
12687 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12690 case SAMPLE_exit_leave:
12691 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12694 case SAMPLE_dynamite:
12695 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12699 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12703 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12707 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12711 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12715 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12719 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12723 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12728 void RaiseScore(int value)
12730 local_player->score += value;
12732 DrawGameValue_Score(local_player->score);
12735 void RaiseScoreElement(int element)
12740 case EL_BD_DIAMOND:
12741 case EL_EMERALD_YELLOW:
12742 case EL_EMERALD_RED:
12743 case EL_EMERALD_PURPLE:
12744 case EL_SP_INFOTRON:
12745 RaiseScore(level.score[SC_EMERALD]);
12748 RaiseScore(level.score[SC_DIAMOND]);
12751 RaiseScore(level.score[SC_CRYSTAL]);
12754 RaiseScore(level.score[SC_PEARL]);
12757 case EL_BD_BUTTERFLY:
12758 case EL_SP_ELECTRON:
12759 RaiseScore(level.score[SC_BUG]);
12762 case EL_BD_FIREFLY:
12763 case EL_SP_SNIKSNAK:
12764 RaiseScore(level.score[SC_SPACESHIP]);
12767 case EL_DARK_YAMYAM:
12768 RaiseScore(level.score[SC_YAMYAM]);
12771 RaiseScore(level.score[SC_ROBOT]);
12774 RaiseScore(level.score[SC_PACMAN]);
12777 RaiseScore(level.score[SC_NUT]);
12780 case EL_EM_DYNAMITE:
12781 case EL_SP_DISK_RED:
12782 case EL_DYNABOMB_INCREASE_NUMBER:
12783 case EL_DYNABOMB_INCREASE_SIZE:
12784 case EL_DYNABOMB_INCREASE_POWER:
12785 RaiseScore(level.score[SC_DYNAMITE]);
12787 case EL_SHIELD_NORMAL:
12788 case EL_SHIELD_DEADLY:
12789 RaiseScore(level.score[SC_SHIELD]);
12791 case EL_EXTRA_TIME:
12792 RaiseScore(level.extra_time_score);
12806 RaiseScore(level.score[SC_KEY]);
12809 RaiseScore(element_info[element].collect_score);
12814 void RequestQuitGame(boolean ask_if_really_quit)
12816 if (AllPlayersGone ||
12817 !ask_if_really_quit ||
12818 level_editor_test_game ||
12819 Request("Do you really want to quit the game ?",
12820 REQ_ASK | REQ_STAY_CLOSED))
12822 #if defined(NETWORK_AVALIABLE)
12823 if (options.network)
12824 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12828 game_status = GAME_MODE_MAIN;
12834 if (tape.playing && tape.deactivate_display)
12835 TapeDeactivateDisplayOff(TRUE);
12837 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12839 if (tape.playing && tape.deactivate_display)
12840 TapeDeactivateDisplayOn();
12845 /* ---------- new game button stuff ---------------------------------------- */
12847 /* graphic position values for game buttons */
12848 #define GAME_BUTTON_XSIZE 30
12849 #define GAME_BUTTON_YSIZE 30
12850 #define GAME_BUTTON_XPOS 5
12851 #define GAME_BUTTON_YPOS 215
12852 #define SOUND_BUTTON_XPOS 5
12853 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12855 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12856 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12857 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12858 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12859 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12860 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12867 } gamebutton_info[NUM_GAME_BUTTONS] =
12870 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12875 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12876 GAME_CTRL_ID_PAUSE,
12880 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12885 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12886 SOUND_CTRL_ID_MUSIC,
12887 "background music on/off"
12890 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12891 SOUND_CTRL_ID_LOOPS,
12892 "sound loops on/off"
12895 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12896 SOUND_CTRL_ID_SIMPLE,
12897 "normal sounds on/off"
12901 void CreateGameButtons()
12905 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12907 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12908 struct GadgetInfo *gi;
12911 unsigned long event_mask;
12912 int gd_xoffset, gd_yoffset;
12913 int gd_x1, gd_x2, gd_y1, gd_y2;
12916 gd_xoffset = gamebutton_info[i].x;
12917 gd_yoffset = gamebutton_info[i].y;
12918 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12919 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12921 if (id == GAME_CTRL_ID_STOP ||
12922 id == GAME_CTRL_ID_PAUSE ||
12923 id == GAME_CTRL_ID_PLAY)
12925 button_type = GD_TYPE_NORMAL_BUTTON;
12927 event_mask = GD_EVENT_RELEASED;
12928 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12929 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12933 button_type = GD_TYPE_CHECK_BUTTON;
12935 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12936 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12937 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12938 event_mask = GD_EVENT_PRESSED;
12939 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12940 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12943 gi = CreateGadget(GDI_CUSTOM_ID, id,
12944 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12945 GDI_X, DX + gd_xoffset,
12946 GDI_Y, DY + gd_yoffset,
12947 GDI_WIDTH, GAME_BUTTON_XSIZE,
12948 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12949 GDI_TYPE, button_type,
12950 GDI_STATE, GD_BUTTON_UNPRESSED,
12951 GDI_CHECKED, checked,
12952 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12953 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12954 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12955 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12956 GDI_EVENT_MASK, event_mask,
12957 GDI_CALLBACK_ACTION, HandleGameButtons,
12961 Error(ERR_EXIT, "cannot create gadget");
12963 game_gadget[id] = gi;
12967 void FreeGameButtons()
12971 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12972 FreeGadget(game_gadget[i]);
12975 static void MapGameButtons()
12979 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12980 MapGadget(game_gadget[i]);
12983 void UnmapGameButtons()
12987 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12988 UnmapGadget(game_gadget[i]);
12991 static void HandleGameButtons(struct GadgetInfo *gi)
12993 int id = gi->custom_id;
12995 if (game_status != GAME_MODE_PLAYING)
13000 case GAME_CTRL_ID_STOP:
13004 RequestQuitGame(TRUE);
13007 case GAME_CTRL_ID_PAUSE:
13008 if (options.network)
13010 #if defined(NETWORK_AVALIABLE)
13012 SendToServer_ContinuePlaying();
13014 SendToServer_PausePlaying();
13018 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13021 case GAME_CTRL_ID_PLAY:
13024 #if defined(NETWORK_AVALIABLE)
13025 if (options.network)
13026 SendToServer_ContinuePlaying();
13030 tape.pausing = FALSE;
13031 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13036 case SOUND_CTRL_ID_MUSIC:
13037 if (setup.sound_music)
13039 setup.sound_music = FALSE;
13042 else if (audio.music_available)
13044 setup.sound = setup.sound_music = TRUE;
13046 SetAudioMode(setup.sound);
13052 case SOUND_CTRL_ID_LOOPS:
13053 if (setup.sound_loops)
13054 setup.sound_loops = FALSE;
13055 else if (audio.loops_available)
13057 setup.sound = setup.sound_loops = TRUE;
13058 SetAudioMode(setup.sound);
13062 case SOUND_CTRL_ID_SIMPLE:
13063 if (setup.sound_simple)
13064 setup.sound_simple = FALSE;
13065 else if (audio.sound_available)
13067 setup.sound = setup.sound_simple = TRUE;
13068 SetAudioMode(setup.sound);