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_ce_value = CustomValue[x][y];
8287 CustomValue[x][y] = action_arg_number_new;
8290 printf("::: CE value == %d\n", CustomValue[x][y]);
8293 if (CustomValue[x][y] == 0 && last_ce_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 #if USE_NEW_CUSTOM_VALUE
8314 int last_ce_score = ei->collect_score;
8316 ei->collect_score = action_arg_number_new;
8319 printf("::: CE score == %d\n", ei->collect_score);
8322 if (ei->collect_score == 0 && last_ce_score > 0)
8325 printf("::: CE_SCORE_GETS_ZERO\n");
8328 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8329 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8332 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8340 /* ---------- engine actions ------------------------------------------ */
8342 case CA_SET_ENGINE_SCAN_MODE:
8344 InitPlayfieldScanMode(action_arg);
8354 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8356 int old_element = Feld[x][y];
8357 int new_element = get_element_from_group_element(element);
8358 int previous_move_direction = MovDir[x][y];
8359 #if USE_NEW_CUSTOM_VALUE
8360 int last_ce_value = CustomValue[x][y];
8362 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8363 IS_WALKABLE(old_element));
8366 /* check if element under player changes from accessible to unaccessible
8367 (needed for special case of dropping element which then changes) */
8368 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8369 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8379 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8380 RemoveMovingField(x, y);
8384 Feld[x][y] = new_element;
8386 #if !USE_GFX_RESET_GFX_ANIMATION
8387 ResetGfxAnimation(x, y);
8388 ResetRandomAnimationValue(x, y);
8391 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8392 MovDir[x][y] = previous_move_direction;
8394 #if USE_NEW_CUSTOM_VALUE
8395 if (element_info[new_element].use_last_ce_value)
8396 CustomValue[x][y] = last_ce_value;
8399 InitField_WithBug1(x, y, FALSE);
8401 new_element = Feld[x][y]; /* element may have changed */
8403 #if USE_GFX_RESET_GFX_ANIMATION
8404 ResetGfxAnimation(x, y);
8405 ResetRandomAnimationValue(x, y);
8408 DrawLevelField(x, y);
8410 if (GFX_CRUMBLED(new_element))
8411 DrawLevelFieldCrumbledSandNeighbours(x, y);
8415 /* check if element under player changes from accessible to unaccessible
8416 (needed for special case of dropping element which then changes) */
8417 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8418 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8426 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8427 if (ELEM_IS_PLAYER(new_element))
8428 RelocatePlayer(x, y, new_element);
8431 ChangeCount[x][y]++; /* count number of changes in the same frame */
8433 TestIfBadThingTouchesPlayer(x, y);
8434 TestIfPlayerTouchesCustomElement(x, y);
8435 TestIfElementTouchesCustomElement(x, y);
8438 static void CreateField(int x, int y, int element)
8440 CreateFieldExt(x, y, element, FALSE);
8443 static void CreateElementFromChange(int x, int y, int element)
8445 element = GET_VALID_RUNTIME_ELEMENT(element);
8447 #if USE_STOP_CHANGED_ELEMENTS
8448 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8450 int old_element = Feld[x][y];
8452 /* prevent changed element from moving in same engine frame
8453 unless both old and new element can either fall or move */
8454 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8455 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8460 CreateFieldExt(x, y, element, TRUE);
8463 static boolean ChangeElement(int x, int y, int element, int page)
8465 struct ElementInfo *ei = &element_info[element];
8466 struct ElementChangeInfo *change = &ei->change_page[page];
8467 int ce_value = CustomValue[x][y];
8468 int ce_score = ei->collect_score;
8470 int old_element = Feld[x][y];
8472 /* always use default change event to prevent running into a loop */
8473 if (ChangeEvent[x][y] == -1)
8474 ChangeEvent[x][y] = CE_DELAY;
8476 if (ChangeEvent[x][y] == CE_DELAY)
8478 /* reset actual trigger element, trigger player and action element */
8479 change->actual_trigger_element = EL_EMPTY;
8480 change->actual_trigger_player = EL_PLAYER_1;
8481 change->actual_trigger_side = CH_SIDE_NONE;
8482 change->actual_trigger_ce_value = 0;
8483 change->actual_trigger_ce_score = 0;
8486 /* do not change elements more than a specified maximum number of changes */
8487 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8490 ChangeCount[x][y]++; /* count number of changes in the same frame */
8492 if (change->explode)
8499 if (change->use_target_content)
8501 boolean complete_replace = TRUE;
8502 boolean can_replace[3][3];
8505 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8508 boolean is_walkable;
8509 boolean is_diggable;
8510 boolean is_collectible;
8511 boolean is_removable;
8512 boolean is_destructible;
8513 int ex = x + xx - 1;
8514 int ey = y + yy - 1;
8515 int content_element = change->target_content.e[xx][yy];
8518 can_replace[xx][yy] = TRUE;
8520 if (ex == x && ey == y) /* do not check changing element itself */
8523 if (content_element == EL_EMPTY_SPACE)
8525 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8530 if (!IN_LEV_FIELD(ex, ey))
8532 can_replace[xx][yy] = FALSE;
8533 complete_replace = FALSE;
8540 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8541 e = MovingOrBlocked2Element(ex, ey);
8543 is_empty = (IS_FREE(ex, ey) ||
8544 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8546 is_walkable = (is_empty || IS_WALKABLE(e));
8547 is_diggable = (is_empty || IS_DIGGABLE(e));
8548 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8549 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8550 is_removable = (is_diggable || is_collectible);
8552 can_replace[xx][yy] =
8553 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8554 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8555 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8556 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8557 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8558 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8559 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8561 if (!can_replace[xx][yy])
8562 complete_replace = FALSE;
8565 if (!change->only_if_complete || complete_replace)
8567 boolean something_has_changed = FALSE;
8569 if (change->only_if_complete && change->use_random_replace &&
8570 RND(100) < change->random_percentage)
8573 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8575 int ex = x + xx - 1;
8576 int ey = y + yy - 1;
8577 int content_element;
8579 if (can_replace[xx][yy] && (!change->use_random_replace ||
8580 RND(100) < change->random_percentage))
8582 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8583 RemoveMovingField(ex, ey);
8585 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8587 content_element = change->target_content.e[xx][yy];
8588 target_element = GET_TARGET_ELEMENT(content_element, change,
8589 ce_value, ce_score);
8591 CreateElementFromChange(ex, ey, target_element);
8593 something_has_changed = TRUE;
8595 /* for symmetry reasons, freeze newly created border elements */
8596 if (ex != x || ey != y)
8597 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8601 if (something_has_changed)
8603 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8604 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8610 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8611 ce_value, ce_score);
8613 if (element == EL_DIAGONAL_GROWING ||
8614 element == EL_DIAGONAL_SHRINKING)
8616 target_element = Store[x][y];
8618 Store[x][y] = EL_EMPTY;
8621 CreateElementFromChange(x, y, target_element);
8623 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8624 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8627 /* this uses direct change before indirect change */
8628 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8633 #if USE_NEW_DELAYED_ACTION
8635 static void HandleElementChange(int x, int y, int page)
8637 int element = MovingOrBlocked2Element(x, y);
8638 struct ElementInfo *ei = &element_info[element];
8639 struct ElementChangeInfo *change = &ei->change_page[page];
8642 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8643 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8646 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8647 x, y, element, element_info[element].token_name);
8648 printf("HandleElementChange(): This should never happen!\n");
8653 /* this can happen with classic bombs on walkable, changing elements */
8654 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8657 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8658 ChangeDelay[x][y] = 0;
8664 if (ChangeDelay[x][y] == 0) /* initialize element change */
8666 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8668 if (change->can_change)
8670 ResetGfxAnimation(x, y);
8671 ResetRandomAnimationValue(x, y);
8673 if (change->pre_change_function)
8674 change->pre_change_function(x, y);
8678 ChangeDelay[x][y]--;
8680 if (ChangeDelay[x][y] != 0) /* continue element change */
8682 if (change->can_change)
8684 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8686 if (IS_ANIMATED(graphic))
8687 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8689 if (change->change_function)
8690 change->change_function(x, y);
8693 else /* finish element change */
8695 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8697 page = ChangePage[x][y];
8698 ChangePage[x][y] = -1;
8700 change = &ei->change_page[page];
8703 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8705 ChangeDelay[x][y] = 1; /* try change after next move step */
8706 ChangePage[x][y] = page; /* remember page to use for change */
8711 if (change->can_change)
8713 if (ChangeElement(x, y, element, page))
8715 if (change->post_change_function)
8716 change->post_change_function(x, y);
8720 if (change->has_action)
8721 ExecuteCustomElementAction(x, y, element, page);
8727 static void HandleElementChange(int x, int y, int page)
8729 int element = MovingOrBlocked2Element(x, y);
8730 struct ElementInfo *ei = &element_info[element];
8731 struct ElementChangeInfo *change = &ei->change_page[page];
8734 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8737 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8738 x, y, element, element_info[element].token_name);
8739 printf("HandleElementChange(): This should never happen!\n");
8744 /* this can happen with classic bombs on walkable, changing elements */
8745 if (!CAN_CHANGE(element))
8748 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8749 ChangeDelay[x][y] = 0;
8755 if (ChangeDelay[x][y] == 0) /* initialize element change */
8757 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8759 ResetGfxAnimation(x, y);
8760 ResetRandomAnimationValue(x, y);
8762 if (change->pre_change_function)
8763 change->pre_change_function(x, y);
8766 ChangeDelay[x][y]--;
8768 if (ChangeDelay[x][y] != 0) /* continue element change */
8770 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8772 if (IS_ANIMATED(graphic))
8773 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8775 if (change->change_function)
8776 change->change_function(x, y);
8778 else /* finish element change */
8780 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8782 page = ChangePage[x][y];
8783 ChangePage[x][y] = -1;
8785 change = &ei->change_page[page];
8788 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8790 ChangeDelay[x][y] = 1; /* try change after next move step */
8791 ChangePage[x][y] = page; /* remember page to use for change */
8796 if (ChangeElement(x, y, element, page))
8798 if (change->post_change_function)
8799 change->post_change_function(x, y);
8806 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8807 int trigger_element,
8813 boolean change_done_any = FALSE;
8814 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8817 if (!(trigger_events[trigger_element][trigger_event]))
8820 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8822 int element = EL_CUSTOM_START + i;
8823 boolean change_done = FALSE;
8826 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8827 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8830 for (p = 0; p < element_info[element].num_change_pages; p++)
8832 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8834 if (change->can_change_or_has_action &&
8835 change->has_event[trigger_event] &&
8836 change->trigger_side & trigger_side &&
8837 change->trigger_player & trigger_player &&
8838 change->trigger_page & trigger_page_bits &&
8839 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8841 change->actual_trigger_element = trigger_element;
8842 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8843 change->actual_trigger_side = trigger_side;
8844 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8845 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8847 if ((change->can_change && !change_done) || change->has_action)
8852 SCAN_PLAYFIELD(x, y)
8854 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8857 if (Feld[x][y] == element)
8859 if (change->can_change && !change_done)
8861 ChangeDelay[x][y] = 1;
8862 ChangeEvent[x][y] = trigger_event;
8864 HandleElementChange(x, y, p);
8866 #if USE_NEW_DELAYED_ACTION
8867 else if (change->has_action)
8869 ExecuteCustomElementAction(x, y, element, p);
8870 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8873 if (change->has_action)
8875 ExecuteCustomElementAction(x, y, element, p);
8876 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8882 if (change->can_change)
8885 change_done_any = TRUE;
8892 return change_done_any;
8895 static boolean CheckElementChangeExt(int x, int y,
8897 int trigger_element,
8902 boolean change_done = FALSE;
8905 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8906 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8909 if (Feld[x][y] == EL_BLOCKED)
8911 Blocked2Moving(x, y, &x, &y);
8912 element = Feld[x][y];
8916 /* check if element has already changed */
8917 if (Feld[x][y] != element)
8920 /* check if element has already changed or is about to change after moving */
8921 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8922 Feld[x][y] != element) ||
8924 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8925 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8926 ChangePage[x][y] != -1)))
8930 for (p = 0; p < element_info[element].num_change_pages; p++)
8932 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8934 boolean check_trigger_element =
8935 (trigger_event == CE_TOUCHING_X ||
8936 trigger_event == CE_HITTING_X ||
8937 trigger_event == CE_HIT_BY_X);
8939 if (change->can_change_or_has_action &&
8940 change->has_event[trigger_event] &&
8941 change->trigger_side & trigger_side &&
8942 change->trigger_player & trigger_player &&
8943 (!check_trigger_element ||
8944 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8946 change->actual_trigger_element = trigger_element;
8947 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8948 change->actual_trigger_side = trigger_side;
8949 change->actual_trigger_ce_value = CustomValue[x][y];
8950 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8952 /* special case: trigger element not at (x,y) position for some events */
8953 if (check_trigger_element)
8965 { 0, 0 }, { 0, 0 }, { 0, 0 },
8969 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8970 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8972 change->actual_trigger_ce_value = CustomValue[xx][yy];
8973 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8976 if (change->can_change && !change_done)
8978 ChangeDelay[x][y] = 1;
8979 ChangeEvent[x][y] = trigger_event;
8981 HandleElementChange(x, y, p);
8985 #if USE_NEW_DELAYED_ACTION
8986 else if (change->has_action)
8988 ExecuteCustomElementAction(x, y, element, p);
8989 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8992 if (change->has_action)
8994 ExecuteCustomElementAction(x, y, element, p);
8995 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9004 static void PlayPlayerSound(struct PlayerInfo *player)
9006 int jx = player->jx, jy = player->jy;
9007 int sound_element = player->artwork_element;
9008 int last_action = player->last_action_waiting;
9009 int action = player->action_waiting;
9011 if (player->is_waiting)
9013 if (action != last_action)
9014 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9016 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9020 if (action != last_action)
9021 StopSound(element_info[sound_element].sound[last_action]);
9023 if (last_action == ACTION_SLEEPING)
9024 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9028 static void PlayAllPlayersSound()
9032 for (i = 0; i < MAX_PLAYERS; i++)
9033 if (stored_player[i].active)
9034 PlayPlayerSound(&stored_player[i]);
9037 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9039 boolean last_waiting = player->is_waiting;
9040 int move_dir = player->MovDir;
9042 player->dir_waiting = move_dir;
9043 player->last_action_waiting = player->action_waiting;
9047 if (!last_waiting) /* not waiting -> waiting */
9049 player->is_waiting = TRUE;
9051 player->frame_counter_bored =
9053 game.player_boring_delay_fixed +
9054 SimpleRND(game.player_boring_delay_random);
9055 player->frame_counter_sleeping =
9057 game.player_sleeping_delay_fixed +
9058 SimpleRND(game.player_sleeping_delay_random);
9061 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9063 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9067 if (game.player_sleeping_delay_fixed +
9068 game.player_sleeping_delay_random > 0 &&
9069 player->anim_delay_counter == 0 &&
9070 player->post_delay_counter == 0 &&
9071 FrameCounter >= player->frame_counter_sleeping)
9072 player->is_sleeping = TRUE;
9073 else if (game.player_boring_delay_fixed +
9074 game.player_boring_delay_random > 0 &&
9075 FrameCounter >= player->frame_counter_bored)
9076 player->is_bored = TRUE;
9078 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9079 player->is_bored ? ACTION_BORING :
9083 if (player->is_sleeping && player->use_murphy)
9085 /* special case for sleeping Murphy when leaning against non-free tile */
9087 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9088 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9089 !IS_MOVING(player->jx - 1, player->jy)))
9091 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9092 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9093 !IS_MOVING(player->jx + 1, player->jy)))
9094 move_dir = MV_RIGHT;
9096 player->is_sleeping = FALSE;
9098 player->dir_waiting = move_dir;
9102 if (player->is_sleeping)
9104 if (player->num_special_action_sleeping > 0)
9106 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9108 int last_special_action = player->special_action_sleeping;
9109 int num_special_action = player->num_special_action_sleeping;
9110 int special_action =
9111 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9112 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9113 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9114 last_special_action + 1 : ACTION_SLEEPING);
9115 int special_graphic =
9116 el_act_dir2img(player->artwork_element, special_action, move_dir);
9118 player->anim_delay_counter =
9119 graphic_info[special_graphic].anim_delay_fixed +
9120 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9121 player->post_delay_counter =
9122 graphic_info[special_graphic].post_delay_fixed +
9123 SimpleRND(graphic_info[special_graphic].post_delay_random);
9125 player->special_action_sleeping = special_action;
9128 if (player->anim_delay_counter > 0)
9130 player->action_waiting = player->special_action_sleeping;
9131 player->anim_delay_counter--;
9133 else if (player->post_delay_counter > 0)
9135 player->post_delay_counter--;
9139 else if (player->is_bored)
9141 if (player->num_special_action_bored > 0)
9143 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9145 int special_action =
9146 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9147 int special_graphic =
9148 el_act_dir2img(player->artwork_element, special_action, move_dir);
9150 player->anim_delay_counter =
9151 graphic_info[special_graphic].anim_delay_fixed +
9152 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9153 player->post_delay_counter =
9154 graphic_info[special_graphic].post_delay_fixed +
9155 SimpleRND(graphic_info[special_graphic].post_delay_random);
9157 player->special_action_bored = special_action;
9160 if (player->anim_delay_counter > 0)
9162 player->action_waiting = player->special_action_bored;
9163 player->anim_delay_counter--;
9165 else if (player->post_delay_counter > 0)
9167 player->post_delay_counter--;
9172 else if (last_waiting) /* waiting -> not waiting */
9174 player->is_waiting = FALSE;
9175 player->is_bored = FALSE;
9176 player->is_sleeping = FALSE;
9178 player->frame_counter_bored = -1;
9179 player->frame_counter_sleeping = -1;
9181 player->anim_delay_counter = 0;
9182 player->post_delay_counter = 0;
9184 player->dir_waiting = player->MovDir;
9185 player->action_waiting = ACTION_DEFAULT;
9187 player->special_action_bored = ACTION_DEFAULT;
9188 player->special_action_sleeping = ACTION_DEFAULT;
9192 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9194 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9195 int left = player_action & JOY_LEFT;
9196 int right = player_action & JOY_RIGHT;
9197 int up = player_action & JOY_UP;
9198 int down = player_action & JOY_DOWN;
9199 int button1 = player_action & JOY_BUTTON_1;
9200 int button2 = player_action & JOY_BUTTON_2;
9201 int dx = (left ? -1 : right ? 1 : 0);
9202 int dy = (up ? -1 : down ? 1 : 0);
9204 if (!player->active || tape.pausing)
9210 snapped = SnapField(player, dx, dy);
9214 dropped = DropElement(player);
9216 moved = MovePlayer(player, dx, dy);
9219 if (tape.single_step && tape.recording && !tape.pausing)
9221 if (button1 || (dropped && !moved))
9223 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9224 SnapField(player, 0, 0); /* stop snapping */
9228 SetPlayerWaiting(player, FALSE);
9230 return player_action;
9234 /* no actions for this player (no input at player's configured device) */
9236 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9237 SnapField(player, 0, 0);
9238 CheckGravityMovementWhenNotMoving(player);
9240 if (player->MovPos == 0)
9241 SetPlayerWaiting(player, TRUE);
9243 if (player->MovPos == 0) /* needed for tape.playing */
9244 player->is_moving = FALSE;
9246 player->is_dropping = FALSE;
9247 player->is_dropping_pressed = FALSE;
9248 player->drop_pressed_delay = 0;
9254 static void CheckLevelTime()
9258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9260 if (level.native_em_level->lev->home == 0) /* all players at home */
9262 local_player->LevelSolved = TRUE;
9263 AllPlayersGone = TRUE;
9265 level.native_em_level->lev->home = -1;
9268 if (level.native_em_level->ply[0]->alive == 0 &&
9269 level.native_em_level->ply[1]->alive == 0 &&
9270 level.native_em_level->ply[2]->alive == 0 &&
9271 level.native_em_level->ply[3]->alive == 0) /* all dead */
9272 AllPlayersGone = TRUE;
9275 if (TimeFrames >= FRAMES_PER_SECOND)
9280 for (i = 0; i < MAX_PLAYERS; i++)
9282 struct PlayerInfo *player = &stored_player[i];
9284 if (SHIELD_ON(player))
9286 player->shield_normal_time_left--;
9288 if (player->shield_deadly_time_left > 0)
9289 player->shield_deadly_time_left--;
9293 if (!level.use_step_counter)
9301 if (TimeLeft <= 10 && setup.time_limit)
9302 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9304 DrawGameValue_Time(TimeLeft);
9306 if (!TimeLeft && setup.time_limit)
9308 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9309 level.native_em_level->lev->killed_out_of_time = TRUE;
9311 for (i = 0; i < MAX_PLAYERS; i++)
9312 KillPlayer(&stored_player[i]);
9315 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9316 DrawGameValue_Time(TimePlayed);
9318 level.native_em_level->lev->time =
9319 (level.time == 0 ? TimePlayed : TimeLeft);
9322 if (tape.recording || tape.playing)
9323 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9327 void AdvanceFrameAndPlayerCounters(int player_nr)
9332 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9333 FrameCounter, FrameCounter + 1);
9336 /* advance frame counters (global frame counter and time frame counter) */
9340 /* advance player counters (counters for move delay, move animation etc.) */
9341 for (i = 0; i < MAX_PLAYERS; i++)
9343 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9344 int move_delay_value = stored_player[i].move_delay_value;
9345 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9347 if (!advance_player_counters) /* not all players may be affected */
9350 #if USE_NEW_PLAYER_ANIM
9351 if (move_frames == 0) /* less than one move per game frame */
9353 int stepsize = TILEX / move_delay_value;
9354 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9355 int count = (stored_player[i].is_moving ?
9356 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9358 if (count % delay == 0)
9363 stored_player[i].Frame += move_frames;
9365 if (stored_player[i].MovPos != 0)
9366 stored_player[i].StepFrame += move_frames;
9368 if (stored_player[i].move_delay > 0)
9369 stored_player[i].move_delay--;
9371 /* due to bugs in previous versions, counter must count up, not down */
9372 if (stored_player[i].push_delay != -1)
9373 stored_player[i].push_delay++;
9375 if (stored_player[i].drop_delay > 0)
9376 stored_player[i].drop_delay--;
9378 if (stored_player[i].is_dropping_pressed)
9379 stored_player[i].drop_pressed_delay++;
9383 void StartGameActions(boolean init_network_game, boolean record_tape,
9386 unsigned long new_random_seed = InitRND(random_seed);
9389 TapeStartRecording(new_random_seed);
9391 #if defined(NETWORK_AVALIABLE)
9392 if (init_network_game)
9394 SendToServer_StartPlaying();
9402 game_status = GAME_MODE_PLAYING;
9409 static unsigned long game_frame_delay = 0;
9410 unsigned long game_frame_delay_value;
9411 byte *recorded_player_action;
9412 byte summarized_player_action = 0;
9413 byte tape_action[MAX_PLAYERS];
9416 if (game.restart_level)
9417 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9419 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9421 if (level.native_em_level->lev->home == 0) /* all players at home */
9423 local_player->LevelSolved = TRUE;
9424 AllPlayersGone = TRUE;
9426 level.native_em_level->lev->home = -1;
9429 if (level.native_em_level->ply[0]->alive == 0 &&
9430 level.native_em_level->ply[1]->alive == 0 &&
9431 level.native_em_level->ply[2]->alive == 0 &&
9432 level.native_em_level->ply[3]->alive == 0) /* all dead */
9433 AllPlayersGone = TRUE;
9436 if (local_player->LevelSolved)
9439 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9442 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9445 game_frame_delay_value =
9446 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9448 if (tape.playing && tape.warp_forward && !tape.pausing)
9449 game_frame_delay_value = 0;
9451 /* ---------- main game synchronization point ---------- */
9453 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9455 if (network_playing && !network_player_action_received)
9457 /* try to get network player actions in time */
9459 #if defined(NETWORK_AVALIABLE)
9460 /* last chance to get network player actions without main loop delay */
9464 /* game was quit by network peer */
9465 if (game_status != GAME_MODE_PLAYING)
9468 if (!network_player_action_received)
9469 return; /* failed to get network player actions in time */
9471 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9477 /* at this point we know that we really continue executing the game */
9480 network_player_action_received = FALSE;
9483 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9485 if (tape.set_centered_player)
9487 game.centered_player_nr_next = tape.centered_player_nr_next;
9488 game.set_centered_player = TRUE;
9491 for (i = 0; i < MAX_PLAYERS; i++)
9493 summarized_player_action |= stored_player[i].action;
9495 if (!network_playing)
9496 stored_player[i].effective_action = stored_player[i].action;
9499 #if defined(NETWORK_AVALIABLE)
9500 if (network_playing)
9501 SendToServer_MovePlayer(summarized_player_action);
9504 if (!options.network && !setup.team_mode)
9505 local_player->effective_action = summarized_player_action;
9507 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9509 for (i = 0; i < MAX_PLAYERS; i++)
9510 stored_player[i].effective_action =
9511 (i == game.centered_player_nr ? summarized_player_action : 0);
9514 if (recorded_player_action != NULL)
9515 for (i = 0; i < MAX_PLAYERS; i++)
9516 stored_player[i].effective_action = recorded_player_action[i];
9518 for (i = 0; i < MAX_PLAYERS; i++)
9520 tape_action[i] = stored_player[i].effective_action;
9522 /* (this can only happen in the R'n'D game engine) */
9523 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9524 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9527 /* only record actions from input devices, but not programmed actions */
9529 TapeRecordAction(tape_action);
9531 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9533 GameActions_EM_Main();
9541 void GameActions_EM_Main()
9543 byte effective_action[MAX_PLAYERS];
9544 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9547 for (i = 0; i < MAX_PLAYERS; i++)
9548 effective_action[i] = stored_player[i].effective_action;
9550 GameActions_EM(effective_action, warp_mode);
9554 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9557 void GameActions_RND()
9559 int magic_wall_x = 0, magic_wall_y = 0;
9560 int i, x, y, element, graphic;
9562 InitPlayfieldScanModeVars();
9564 #if USE_ONE_MORE_CHANGE_PER_FRAME
9565 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9567 SCAN_PLAYFIELD(x, y)
9569 ChangeCount[x][y] = 0;
9570 ChangeEvent[x][y] = -1;
9576 if (game.set_centered_player)
9578 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9580 /* switching to "all players" only possible if all players fit to screen */
9581 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9583 game.centered_player_nr_next = game.centered_player_nr;
9584 game.set_centered_player = FALSE;
9587 /* do not switch focus to non-existing (or non-active) player */
9588 if (game.centered_player_nr_next >= 0 &&
9589 !stored_player[game.centered_player_nr_next].active)
9591 game.centered_player_nr_next = game.centered_player_nr;
9592 game.set_centered_player = FALSE;
9596 if (game.set_centered_player &&
9597 ScreenMovPos == 0) /* screen currently aligned at tile position */
9601 if (game.centered_player_nr_next == -1)
9603 setScreenCenteredToAllPlayers(&sx, &sy);
9607 sx = stored_player[game.centered_player_nr_next].jx;
9608 sy = stored_player[game.centered_player_nr_next].jy;
9611 game.centered_player_nr = game.centered_player_nr_next;
9612 game.set_centered_player = FALSE;
9614 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9615 DrawGameDoorValues();
9619 for (i = 0; i < MAX_PLAYERS; i++)
9621 int actual_player_action = stored_player[i].effective_action;
9624 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9625 - rnd_equinox_tetrachloride 048
9626 - rnd_equinox_tetrachloride_ii 096
9627 - rnd_emanuel_schmieg 002
9628 - doctor_sloan_ww 001, 020
9630 if (stored_player[i].MovPos == 0)
9631 CheckGravityMovement(&stored_player[i]);
9634 /* overwrite programmed action with tape action */
9635 if (stored_player[i].programmed_action)
9636 actual_player_action = stored_player[i].programmed_action;
9639 PlayerActions(&stored_player[i], actual_player_action);
9641 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9643 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9644 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9647 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9651 network_player_action_received = FALSE;
9654 ScrollScreen(NULL, SCROLL_GO_ON);
9656 /* for backwards compatibility, the following code emulates a fixed bug that
9657 occured when pushing elements (causing elements that just made their last
9658 pushing step to already (if possible) make their first falling step in the
9659 same game frame, which is bad); this code is also needed to use the famous
9660 "spring push bug" which is used in older levels and might be wanted to be
9661 used also in newer levels, but in this case the buggy pushing code is only
9662 affecting the "spring" element and no other elements */
9664 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9666 for (i = 0; i < MAX_PLAYERS; i++)
9668 struct PlayerInfo *player = &stored_player[i];
9672 if (player->active && player->is_pushing && player->is_moving &&
9674 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9675 Feld[x][y] == EL_SPRING))
9677 ContinueMoving(x, y);
9679 /* continue moving after pushing (this is actually a bug) */
9680 if (!IS_MOVING(x, y))
9689 SCAN_PLAYFIELD(x, y)
9691 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9694 ChangeCount[x][y] = 0;
9695 ChangeEvent[x][y] = -1;
9697 /* this must be handled before main playfield loop */
9698 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9701 if (MovDelay[x][y] <= 0)
9705 #if USE_NEW_SNAP_DELAY
9706 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9709 if (MovDelay[x][y] <= 0)
9712 DrawLevelField(x, y);
9714 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9720 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9722 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9723 printf("GameActions(): This should never happen!\n");
9725 ChangePage[x][y] = -1;
9730 if (WasJustMoving[x][y] > 0)
9731 WasJustMoving[x][y]--;
9732 if (WasJustFalling[x][y] > 0)
9733 WasJustFalling[x][y]--;
9734 if (CheckCollision[x][y] > 0)
9735 CheckCollision[x][y]--;
9739 /* reset finished pushing action (not done in ContinueMoving() to allow
9740 continuous pushing animation for elements with zero push delay) */
9741 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9743 ResetGfxAnimation(x, y);
9744 DrawLevelField(x, y);
9748 if (IS_BLOCKED(x, y))
9752 Blocked2Moving(x, y, &oldx, &oldy);
9753 if (!IS_MOVING(oldx, oldy))
9755 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9756 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9757 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9758 printf("GameActions(): This should never happen!\n");
9765 SCAN_PLAYFIELD(x, y)
9767 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9770 element = Feld[x][y];
9771 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9774 printf("::: %d,%d\n", x, y);
9776 if (element == EL_ROCK)
9777 printf("::: Yo man! Rocks can fall!\n");
9781 ResetGfxFrame(x, y, TRUE);
9783 if (graphic_info[graphic].anim_global_sync)
9784 GfxFrame[x][y] = FrameCounter;
9785 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9787 int old_gfx_frame = GfxFrame[x][y];
9789 GfxFrame[x][y] = CustomValue[x][y];
9792 if (GfxFrame[x][y] != old_gfx_frame)
9794 DrawLevelGraphicAnimation(x, y, graphic);
9796 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9798 int old_gfx_frame = GfxFrame[x][y];
9800 GfxFrame[x][y] = element_info[element].collect_score;
9803 if (GfxFrame[x][y] != old_gfx_frame)
9805 DrawLevelGraphicAnimation(x, y, graphic);
9809 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9810 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9811 ResetRandomAnimationValue(x, y);
9813 SetRandomAnimationValue(x, y);
9815 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9817 if (IS_INACTIVE(element))
9819 if (IS_ANIMATED(graphic))
9820 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9825 /* this may take place after moving, so 'element' may have changed */
9826 if (IS_CHANGING(x, y) &&
9827 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9829 int page = element_info[element].event_page_nr[CE_DELAY];
9831 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9835 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9839 if (element == EL_CUSTOM_255)
9840 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9844 HandleElementChange(x, y, page);
9846 if (CAN_CHANGE(element))
9847 HandleElementChange(x, y, page);
9849 if (HAS_ACTION(element))
9850 ExecuteCustomElementAction(x, y, element, page);
9855 element = Feld[x][y];
9856 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9859 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9863 element = Feld[x][y];
9864 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9866 if (IS_ANIMATED(graphic) &&
9869 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9871 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9872 EdelsteinFunkeln(x, y);
9874 else if ((element == EL_ACID ||
9875 element == EL_EXIT_OPEN ||
9876 element == EL_SP_EXIT_OPEN ||
9877 element == EL_SP_TERMINAL ||
9878 element == EL_SP_TERMINAL_ACTIVE ||
9879 element == EL_EXTRA_TIME ||
9880 element == EL_SHIELD_NORMAL ||
9881 element == EL_SHIELD_DEADLY) &&
9882 IS_ANIMATED(graphic))
9883 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9884 else if (IS_MOVING(x, y))
9885 ContinueMoving(x, y);
9886 else if (IS_ACTIVE_BOMB(element))
9887 CheckDynamite(x, y);
9888 else if (element == EL_AMOEBA_GROWING)
9889 AmoebeWaechst(x, y);
9890 else if (element == EL_AMOEBA_SHRINKING)
9891 AmoebaDisappearing(x, y);
9893 #if !USE_NEW_AMOEBA_CODE
9894 else if (IS_AMOEBALIVE(element))
9895 AmoebeAbleger(x, y);
9898 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9900 else if (element == EL_EXIT_CLOSED)
9902 else if (element == EL_SP_EXIT_CLOSED)
9904 else if (element == EL_EXPANDABLE_WALL_GROWING)
9906 else if (element == EL_EXPANDABLE_WALL ||
9907 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9908 element == EL_EXPANDABLE_WALL_VERTICAL ||
9909 element == EL_EXPANDABLE_WALL_ANY)
9911 else if (element == EL_FLAMES)
9912 CheckForDragon(x, y);
9913 else if (element == EL_EXPLOSION)
9914 ; /* drawing of correct explosion animation is handled separately */
9915 else if (element == EL_ELEMENT_SNAPPING ||
9916 element == EL_DIAGONAL_SHRINKING ||
9917 element == EL_DIAGONAL_GROWING)
9920 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9922 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9925 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9926 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9929 if (element == EL_CUSTOM_255 ||
9930 element == EL_CUSTOM_256)
9931 DrawLevelGraphicAnimation(x, y, graphic);
9934 if (IS_BELT_ACTIVE(element))
9935 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9937 if (game.magic_wall_active)
9939 int jx = local_player->jx, jy = local_player->jy;
9941 /* play the element sound at the position nearest to the player */
9942 if ((element == EL_MAGIC_WALL_FULL ||
9943 element == EL_MAGIC_WALL_ACTIVE ||
9944 element == EL_MAGIC_WALL_EMPTYING ||
9945 element == EL_BD_MAGIC_WALL_FULL ||
9946 element == EL_BD_MAGIC_WALL_ACTIVE ||
9947 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9948 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9956 #if USE_NEW_AMOEBA_CODE
9957 /* new experimental amoeba growth stuff */
9958 if (!(FrameCounter % 8))
9960 static unsigned long random = 1684108901;
9962 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9964 x = RND(lev_fieldx);
9965 y = RND(lev_fieldy);
9966 element = Feld[x][y];
9968 if (!IS_PLAYER(x,y) &&
9969 (element == EL_EMPTY ||
9970 CAN_GROW_INTO(element) ||
9971 element == EL_QUICKSAND_EMPTY ||
9972 element == EL_ACID_SPLASH_LEFT ||
9973 element == EL_ACID_SPLASH_RIGHT))
9975 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9976 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9977 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9978 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9979 Feld[x][y] = EL_AMOEBA_DROP;
9982 random = random * 129 + 1;
9988 if (game.explosions_delayed)
9991 game.explosions_delayed = FALSE;
9994 SCAN_PLAYFIELD(x, y)
9996 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9999 element = Feld[x][y];
10001 if (ExplodeField[x][y])
10002 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10003 else if (element == EL_EXPLOSION)
10004 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10006 ExplodeField[x][y] = EX_TYPE_NONE;
10009 game.explosions_delayed = TRUE;
10012 if (game.magic_wall_active)
10014 if (!(game.magic_wall_time_left % 4))
10016 int element = Feld[magic_wall_x][magic_wall_y];
10018 if (element == EL_BD_MAGIC_WALL_FULL ||
10019 element == EL_BD_MAGIC_WALL_ACTIVE ||
10020 element == EL_BD_MAGIC_WALL_EMPTYING)
10021 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10023 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10026 if (game.magic_wall_time_left > 0)
10028 game.magic_wall_time_left--;
10029 if (!game.magic_wall_time_left)
10032 SCAN_PLAYFIELD(x, y)
10034 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10037 element = Feld[x][y];
10039 if (element == EL_MAGIC_WALL_ACTIVE ||
10040 element == EL_MAGIC_WALL_FULL)
10042 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10043 DrawLevelField(x, y);
10045 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10046 element == EL_BD_MAGIC_WALL_FULL)
10048 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10049 DrawLevelField(x, y);
10053 game.magic_wall_active = FALSE;
10058 if (game.light_time_left > 0)
10060 game.light_time_left--;
10062 if (game.light_time_left == 0)
10063 RedrawAllLightSwitchesAndInvisibleElements();
10066 if (game.timegate_time_left > 0)
10068 game.timegate_time_left--;
10070 if (game.timegate_time_left == 0)
10071 CloseAllOpenTimegates();
10074 if (game.lenses_time_left > 0)
10076 game.lenses_time_left--;
10078 if (game.lenses_time_left == 0)
10079 RedrawAllInvisibleElementsForLenses();
10082 if (game.magnify_time_left > 0)
10084 game.magnify_time_left--;
10086 if (game.magnify_time_left == 0)
10087 RedrawAllInvisibleElementsForMagnifier();
10090 for (i = 0; i < MAX_PLAYERS; i++)
10092 struct PlayerInfo *player = &stored_player[i];
10094 if (SHIELD_ON(player))
10096 if (player->shield_deadly_time_left)
10097 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10098 else if (player->shield_normal_time_left)
10099 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10106 PlayAllPlayersSound();
10108 if (options.debug) /* calculate frames per second */
10110 static unsigned long fps_counter = 0;
10111 static int fps_frames = 0;
10112 unsigned long fps_delay_ms = Counter() - fps_counter;
10116 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10118 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10121 fps_counter = Counter();
10124 redraw_mask |= REDRAW_FPS;
10127 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10129 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10131 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10133 local_player->show_envelope = 0;
10136 /* use random number generator in every frame to make it less predictable */
10137 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10141 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10143 int min_x = x, min_y = y, max_x = x, max_y = y;
10146 for (i = 0; i < MAX_PLAYERS; i++)
10148 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10150 if (!stored_player[i].active || &stored_player[i] == player)
10153 min_x = MIN(min_x, jx);
10154 min_y = MIN(min_y, jy);
10155 max_x = MAX(max_x, jx);
10156 max_y = MAX(max_y, jy);
10159 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10162 static boolean AllPlayersInVisibleScreen()
10166 for (i = 0; i < MAX_PLAYERS; i++)
10168 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10170 if (!stored_player[i].active)
10173 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10180 void ScrollLevel(int dx, int dy)
10182 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10185 BlitBitmap(drawto_field, drawto_field,
10186 FX + TILEX * (dx == -1) - softscroll_offset,
10187 FY + TILEY * (dy == -1) - softscroll_offset,
10188 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10189 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10190 FX + TILEX * (dx == 1) - softscroll_offset,
10191 FY + TILEY * (dy == 1) - softscroll_offset);
10195 x = (dx == 1 ? BX1 : BX2);
10196 for (y = BY1; y <= BY2; y++)
10197 DrawScreenField(x, y);
10202 y = (dy == 1 ? BY1 : BY2);
10203 for (x = BX1; x <= BX2; x++)
10204 DrawScreenField(x, y);
10207 redraw_mask |= REDRAW_FIELD;
10210 static boolean canFallDown(struct PlayerInfo *player)
10212 int jx = player->jx, jy = player->jy;
10214 return (IN_LEV_FIELD(jx, jy + 1) &&
10215 (IS_FREE(jx, jy + 1) ||
10216 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10217 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10218 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10221 static boolean canPassField(int x, int y, int move_dir)
10223 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10224 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10225 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10226 int nextx = x + dx;
10227 int nexty = y + dy;
10228 int element = Feld[x][y];
10230 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10231 !CAN_MOVE(element) &&
10232 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10233 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10234 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10237 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10239 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10240 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10241 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10245 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10246 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10247 (IS_DIGGABLE(Feld[newx][newy]) ||
10248 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10249 canPassField(newx, newy, move_dir)));
10252 static void CheckGravityMovement(struct PlayerInfo *player)
10254 if (game.gravity && !player->programmed_action)
10256 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10257 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10258 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10259 int jx = player->jx, jy = player->jy;
10260 boolean player_is_moving_to_valid_field =
10261 (!player_is_snapping &&
10262 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10263 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10264 boolean player_can_fall_down = canFallDown(player);
10266 if (player_can_fall_down &&
10267 !player_is_moving_to_valid_field)
10268 player->programmed_action = MV_DOWN;
10272 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10274 return CheckGravityMovement(player);
10276 if (game.gravity && !player->programmed_action)
10278 int jx = player->jx, jy = player->jy;
10279 boolean field_under_player_is_free =
10280 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10281 boolean player_is_standing_on_valid_field =
10282 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10283 (IS_WALKABLE(Feld[jx][jy]) &&
10284 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10286 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10287 player->programmed_action = MV_DOWN;
10292 MovePlayerOneStep()
10293 -----------------------------------------------------------------------------
10294 dx, dy: direction (non-diagonal) to try to move the player to
10295 real_dx, real_dy: direction as read from input device (can be diagonal)
10298 boolean MovePlayerOneStep(struct PlayerInfo *player,
10299 int dx, int dy, int real_dx, int real_dy)
10301 int jx = player->jx, jy = player->jy;
10302 int new_jx = jx + dx, new_jy = jy + dy;
10303 #if !USE_FIXED_DONT_RUN_INTO
10307 boolean player_can_move = !player->cannot_move;
10309 if (!player->active || (!dx && !dy))
10310 return MP_NO_ACTION;
10312 player->MovDir = (dx < 0 ? MV_LEFT :
10313 dx > 0 ? MV_RIGHT :
10315 dy > 0 ? MV_DOWN : MV_NONE);
10317 if (!IN_LEV_FIELD(new_jx, new_jy))
10318 return MP_NO_ACTION;
10320 if (!player_can_move)
10323 if (player->MovPos == 0)
10325 player->is_moving = FALSE;
10326 player->is_digging = FALSE;
10327 player->is_collecting = FALSE;
10328 player->is_snapping = FALSE;
10329 player->is_pushing = FALSE;
10332 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10333 SnapField(player, 0, 0);
10337 return MP_NO_ACTION;
10342 if (!options.network && game.centered_player_nr == -1 &&
10343 !AllPlayersInSight(player, new_jx, new_jy))
10344 return MP_NO_ACTION;
10346 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10347 return MP_NO_ACTION;
10350 #if !USE_FIXED_DONT_RUN_INTO
10351 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10353 /* (moved to DigField()) */
10354 if (player_can_move && DONT_RUN_INTO(element))
10356 if (element == EL_ACID && dx == 0 && dy == 1)
10358 SplashAcid(new_jx, new_jy);
10359 Feld[jx][jy] = EL_PLAYER_1;
10360 InitMovingField(jx, jy, MV_DOWN);
10361 Store[jx][jy] = EL_ACID;
10362 ContinueMoving(jx, jy);
10363 BuryPlayer(player);
10366 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10372 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10374 #if USE_FIXED_DONT_RUN_INTO
10375 if (can_move == MP_DONT_RUN_INTO)
10379 if (can_move != MP_MOVING)
10382 #if USE_FIXED_DONT_RUN_INTO
10385 /* check if DigField() has caused relocation of the player */
10386 if (player->jx != jx || player->jy != jy)
10387 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10389 StorePlayer[jx][jy] = 0;
10390 player->last_jx = jx;
10391 player->last_jy = jy;
10392 player->jx = new_jx;
10393 player->jy = new_jy;
10394 StorePlayer[new_jx][new_jy] = player->element_nr;
10396 if (player->move_delay_value_next != -1)
10398 player->move_delay_value = player->move_delay_value_next;
10399 player->move_delay_value_next = -1;
10403 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10405 player->step_counter++;
10407 PlayerVisit[jx][jy] = FrameCounter;
10409 ScrollPlayer(player, SCROLL_INIT);
10414 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10416 int jx = player->jx, jy = player->jy;
10417 int old_jx = jx, old_jy = jy;
10418 int moved = MP_NO_ACTION;
10420 if (!player->active)
10425 if (player->MovPos == 0)
10427 player->is_moving = FALSE;
10428 player->is_digging = FALSE;
10429 player->is_collecting = FALSE;
10430 player->is_snapping = FALSE;
10431 player->is_pushing = FALSE;
10437 if (player->move_delay > 0)
10440 player->move_delay = -1; /* set to "uninitialized" value */
10442 /* store if player is automatically moved to next field */
10443 player->is_auto_moving = (player->programmed_action != MV_NONE);
10445 /* remove the last programmed player action */
10446 player->programmed_action = 0;
10448 if (player->MovPos)
10450 /* should only happen if pre-1.2 tape recordings are played */
10451 /* this is only for backward compatibility */
10453 int original_move_delay_value = player->move_delay_value;
10456 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10460 /* scroll remaining steps with finest movement resolution */
10461 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10463 while (player->MovPos)
10465 ScrollPlayer(player, SCROLL_GO_ON);
10466 ScrollScreen(NULL, SCROLL_GO_ON);
10468 AdvanceFrameAndPlayerCounters(player->index_nr);
10474 player->move_delay_value = original_move_delay_value;
10477 if (player->last_move_dir & MV_HORIZONTAL)
10479 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10480 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10484 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10485 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10492 if (moved & MP_MOVING && !ScreenMovPos &&
10493 (player->index_nr == game.centered_player_nr ||
10494 game.centered_player_nr == -1))
10496 if (moved & MP_MOVING && !ScreenMovPos &&
10497 (player == local_player || !options.network))
10500 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10501 int offset = (setup.scroll_delay ? 3 : 0);
10503 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10505 /* actual player has left the screen -- scroll in that direction */
10506 if (jx != old_jx) /* player has moved horizontally */
10507 scroll_x += (jx - old_jx);
10508 else /* player has moved vertically */
10509 scroll_y += (jy - old_jy);
10513 if (jx != old_jx) /* player has moved horizontally */
10515 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10516 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10517 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10519 /* don't scroll over playfield boundaries */
10520 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10521 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10523 /* don't scroll more than one field at a time */
10524 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10526 /* don't scroll against the player's moving direction */
10527 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10528 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10529 scroll_x = old_scroll_x;
10531 else /* player has moved vertically */
10533 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10534 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10535 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10537 /* don't scroll over playfield boundaries */
10538 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10539 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10541 /* don't scroll more than one field at a time */
10542 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10544 /* don't scroll against the player's moving direction */
10545 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10546 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10547 scroll_y = old_scroll_y;
10551 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10554 if (!options.network && game.centered_player_nr == -1 &&
10555 !AllPlayersInVisibleScreen())
10557 scroll_x = old_scroll_x;
10558 scroll_y = old_scroll_y;
10562 if (!options.network && !AllPlayersInVisibleScreen())
10564 scroll_x = old_scroll_x;
10565 scroll_y = old_scroll_y;
10570 ScrollScreen(player, SCROLL_INIT);
10571 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10576 player->StepFrame = 0;
10578 if (moved & MP_MOVING)
10580 if (old_jx != jx && old_jy == jy)
10581 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10582 else if (old_jx == jx && old_jy != jy)
10583 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10585 DrawLevelField(jx, jy); /* for "crumbled sand" */
10587 player->last_move_dir = player->MovDir;
10588 player->is_moving = TRUE;
10589 player->is_snapping = FALSE;
10590 player->is_switching = FALSE;
10591 player->is_dropping = FALSE;
10592 player->is_dropping_pressed = FALSE;
10593 player->drop_pressed_delay = 0;
10597 CheckGravityMovementWhenNotMoving(player);
10599 player->is_moving = FALSE;
10601 /* at this point, the player is allowed to move, but cannot move right now
10602 (e.g. because of something blocking the way) -- ensure that the player
10603 is also allowed to move in the next frame (in old versions before 3.1.1,
10604 the player was forced to wait again for eight frames before next try) */
10606 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10607 player->move_delay = 0; /* allow direct movement in the next frame */
10610 if (player->move_delay == -1) /* not yet initialized by DigField() */
10611 player->move_delay = player->move_delay_value;
10613 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10615 TestIfPlayerTouchesBadThing(jx, jy);
10616 TestIfPlayerTouchesCustomElement(jx, jy);
10619 if (!player->active)
10620 RemovePlayer(player);
10625 void ScrollPlayer(struct PlayerInfo *player, int mode)
10627 int jx = player->jx, jy = player->jy;
10628 int last_jx = player->last_jx, last_jy = player->last_jy;
10629 int move_stepsize = TILEX / player->move_delay_value;
10631 #if USE_NEW_PLAYER_SPEED
10632 if (!player->active)
10635 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10638 if (!player->active || player->MovPos == 0)
10642 if (mode == SCROLL_INIT)
10644 player->actual_frame_counter = FrameCounter;
10645 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10647 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10648 Feld[last_jx][last_jy] == EL_EMPTY)
10650 int last_field_block_delay = 0; /* start with no blocking at all */
10651 int block_delay_adjustment = player->block_delay_adjustment;
10653 /* if player blocks last field, add delay for exactly one move */
10654 if (player->block_last_field)
10656 last_field_block_delay += player->move_delay_value;
10658 /* when blocking enabled, prevent moving up despite gravity */
10659 if (game.gravity && player->MovDir == MV_UP)
10660 block_delay_adjustment = -1;
10663 /* add block delay adjustment (also possible when not blocking) */
10664 last_field_block_delay += block_delay_adjustment;
10666 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10667 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10670 #if USE_NEW_PLAYER_SPEED
10671 if (player->MovPos != 0) /* player has not yet reached destination */
10677 else if (!FrameReached(&player->actual_frame_counter, 1))
10681 printf("::: player->MovPos: %d -> %d\n",
10683 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10686 #if USE_NEW_PLAYER_SPEED
10687 if (player->MovPos != 0)
10689 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10690 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10692 /* before DrawPlayer() to draw correct player graphic for this case */
10693 if (player->MovPos == 0)
10694 CheckGravityMovement(player);
10697 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10698 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10700 /* before DrawPlayer() to draw correct player graphic for this case */
10701 if (player->MovPos == 0)
10702 CheckGravityMovement(player);
10705 if (player->MovPos == 0) /* player reached destination field */
10708 printf("::: player reached destination field\n");
10711 if (player->move_delay_reset_counter > 0)
10713 player->move_delay_reset_counter--;
10715 if (player->move_delay_reset_counter == 0)
10717 /* continue with normal speed after quickly moving through gate */
10718 HALVE_PLAYER_SPEED(player);
10720 /* be able to make the next move without delay */
10721 player->move_delay = 0;
10725 player->last_jx = jx;
10726 player->last_jy = jy;
10728 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10729 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10730 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10732 DrawPlayer(player); /* needed here only to cleanup last field */
10733 RemovePlayer(player);
10735 if (local_player->friends_still_needed == 0 ||
10736 IS_SP_ELEMENT(Feld[jx][jy]))
10737 player->LevelSolved = player->GameOver = TRUE;
10740 /* this breaks one level: "machine", level 000 */
10742 int move_direction = player->MovDir;
10743 int enter_side = MV_DIR_OPPOSITE(move_direction);
10744 int leave_side = move_direction;
10745 int old_jx = last_jx;
10746 int old_jy = last_jy;
10747 int old_element = Feld[old_jx][old_jy];
10748 int new_element = Feld[jx][jy];
10750 if (IS_CUSTOM_ELEMENT(old_element))
10751 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10753 player->index_bit, leave_side);
10755 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10756 CE_PLAYER_LEAVES_X,
10757 player->index_bit, leave_side);
10759 if (IS_CUSTOM_ELEMENT(new_element))
10760 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10761 player->index_bit, enter_side);
10763 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10764 CE_PLAYER_ENTERS_X,
10765 player->index_bit, enter_side);
10767 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10768 CE_MOVE_OF_X, move_direction);
10771 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10773 TestIfPlayerTouchesBadThing(jx, jy);
10774 TestIfPlayerTouchesCustomElement(jx, jy);
10776 /* needed because pushed element has not yet reached its destination,
10777 so it would trigger a change event at its previous field location */
10778 if (!player->is_pushing)
10779 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10781 if (!player->active)
10782 RemovePlayer(player);
10785 if (level.use_step_counter)
10795 if (TimeLeft <= 10 && setup.time_limit)
10796 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10798 DrawGameValue_Time(TimeLeft);
10800 if (!TimeLeft && setup.time_limit)
10801 for (i = 0; i < MAX_PLAYERS; i++)
10802 KillPlayer(&stored_player[i]);
10804 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10805 DrawGameValue_Time(TimePlayed);
10808 if (tape.single_step && tape.recording && !tape.pausing &&
10809 !player->programmed_action)
10810 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10814 void ScrollScreen(struct PlayerInfo *player, int mode)
10816 static unsigned long screen_frame_counter = 0;
10818 if (mode == SCROLL_INIT)
10820 /* set scrolling step size according to actual player's moving speed */
10821 ScrollStepSize = TILEX / player->move_delay_value;
10823 screen_frame_counter = FrameCounter;
10824 ScreenMovDir = player->MovDir;
10825 ScreenMovPos = player->MovPos;
10826 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10829 else if (!FrameReached(&screen_frame_counter, 1))
10834 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10835 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10836 redraw_mask |= REDRAW_FIELD;
10839 ScreenMovDir = MV_NONE;
10842 void TestIfPlayerTouchesCustomElement(int x, int y)
10844 static int xy[4][2] =
10851 static int trigger_sides[4][2] =
10853 /* center side border side */
10854 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10855 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10856 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10857 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10859 static int touch_dir[4] =
10861 MV_LEFT | MV_RIGHT,
10866 int center_element = Feld[x][y]; /* should always be non-moving! */
10869 for (i = 0; i < NUM_DIRECTIONS; i++)
10871 int xx = x + xy[i][0];
10872 int yy = y + xy[i][1];
10873 int center_side = trigger_sides[i][0];
10874 int border_side = trigger_sides[i][1];
10875 int border_element;
10877 if (!IN_LEV_FIELD(xx, yy))
10880 if (IS_PLAYER(x, y))
10882 struct PlayerInfo *player = PLAYERINFO(x, y);
10884 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10885 border_element = Feld[xx][yy]; /* may be moving! */
10886 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10887 border_element = Feld[xx][yy];
10888 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10889 border_element = MovingOrBlocked2Element(xx, yy);
10891 continue; /* center and border element do not touch */
10893 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10894 player->index_bit, border_side);
10895 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10896 CE_PLAYER_TOUCHES_X,
10897 player->index_bit, border_side);
10899 else if (IS_PLAYER(xx, yy))
10901 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10903 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10905 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10906 continue; /* center and border element do not touch */
10909 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10910 player->index_bit, center_side);
10911 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10912 CE_PLAYER_TOUCHES_X,
10913 player->index_bit, center_side);
10919 #if USE_ELEMENT_TOUCHING_BUGFIX
10921 void TestIfElementTouchesCustomElement(int x, int y)
10923 static int xy[4][2] =
10930 static int trigger_sides[4][2] =
10932 /* center side border side */
10933 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10934 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10935 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10936 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10938 static int touch_dir[4] =
10940 MV_LEFT | MV_RIGHT,
10945 boolean change_center_element = FALSE;
10946 int center_element = Feld[x][y]; /* should always be non-moving! */
10947 int border_element_old[NUM_DIRECTIONS];
10950 for (i = 0; i < NUM_DIRECTIONS; i++)
10952 int xx = x + xy[i][0];
10953 int yy = y + xy[i][1];
10954 int border_element;
10956 border_element_old[i] = -1;
10958 if (!IN_LEV_FIELD(xx, yy))
10961 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10962 border_element = Feld[xx][yy]; /* may be moving! */
10963 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10964 border_element = Feld[xx][yy];
10965 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10966 border_element = MovingOrBlocked2Element(xx, yy);
10968 continue; /* center and border element do not touch */
10970 border_element_old[i] = border_element;
10973 for (i = 0; i < NUM_DIRECTIONS; i++)
10975 int xx = x + xy[i][0];
10976 int yy = y + xy[i][1];
10977 int center_side = trigger_sides[i][0];
10978 int border_element = border_element_old[i];
10980 if (border_element == -1)
10983 /* check for change of border element */
10984 CheckElementChangeBySide(xx, yy, border_element, center_element,
10985 CE_TOUCHING_X, center_side);
10988 for (i = 0; i < NUM_DIRECTIONS; i++)
10990 int border_side = trigger_sides[i][1];
10991 int border_element = border_element_old[i];
10993 if (border_element == -1)
10996 /* check for change of center element (but change it only once) */
10997 if (!change_center_element)
10998 change_center_element =
10999 CheckElementChangeBySide(x, y, center_element, border_element,
11000 CE_TOUCHING_X, border_side);
11006 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11008 static int xy[4][2] =
11015 static int trigger_sides[4][2] =
11017 /* center side border side */
11018 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11019 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11020 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11021 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11023 static int touch_dir[4] =
11025 MV_LEFT | MV_RIGHT,
11030 boolean change_center_element = FALSE;
11031 int center_element = Feld[x][y]; /* should always be non-moving! */
11034 for (i = 0; i < NUM_DIRECTIONS; i++)
11036 int xx = x + xy[i][0];
11037 int yy = y + xy[i][1];
11038 int center_side = trigger_sides[i][0];
11039 int border_side = trigger_sides[i][1];
11040 int border_element;
11042 if (!IN_LEV_FIELD(xx, yy))
11045 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11046 border_element = Feld[xx][yy]; /* may be moving! */
11047 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11048 border_element = Feld[xx][yy];
11049 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11050 border_element = MovingOrBlocked2Element(xx, yy);
11052 continue; /* center and border element do not touch */
11054 /* check for change of center element (but change it only once) */
11055 if (!change_center_element)
11056 change_center_element =
11057 CheckElementChangeBySide(x, y, center_element, border_element,
11058 CE_TOUCHING_X, border_side);
11060 /* check for change of border element */
11061 CheckElementChangeBySide(xx, yy, border_element, center_element,
11062 CE_TOUCHING_X, center_side);
11068 void TestIfElementHitsCustomElement(int x, int y, int direction)
11070 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11071 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11072 int hitx = x + dx, hity = y + dy;
11073 int hitting_element = Feld[x][y];
11074 int touched_element;
11076 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11079 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11080 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11082 if (IN_LEV_FIELD(hitx, hity))
11084 int opposite_direction = MV_DIR_OPPOSITE(direction);
11085 int hitting_side = direction;
11086 int touched_side = opposite_direction;
11087 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11088 MovDir[hitx][hity] != direction ||
11089 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11095 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11096 CE_HITTING_X, touched_side);
11098 CheckElementChangeBySide(hitx, hity, touched_element,
11099 hitting_element, CE_HIT_BY_X, hitting_side);
11101 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11102 CE_HIT_BY_SOMETHING, opposite_direction);
11106 /* "hitting something" is also true when hitting the playfield border */
11107 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11108 CE_HITTING_SOMETHING, direction);
11112 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11114 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11115 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11116 int hitx = x + dx, hity = y + dy;
11117 int hitting_element = Feld[x][y];
11118 int touched_element;
11120 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11121 !IS_FREE(hitx, hity) &&
11122 (!IS_MOVING(hitx, hity) ||
11123 MovDir[hitx][hity] != direction ||
11124 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11127 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11131 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11135 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11136 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11138 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11139 EP_CAN_SMASH_EVERYTHING, direction);
11141 if (IN_LEV_FIELD(hitx, hity))
11143 int opposite_direction = MV_DIR_OPPOSITE(direction);
11144 int hitting_side = direction;
11145 int touched_side = opposite_direction;
11147 int touched_element = MovingOrBlocked2Element(hitx, hity);
11150 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11151 MovDir[hitx][hity] != direction ||
11152 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11161 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11162 CE_SMASHED_BY_SOMETHING, opposite_direction);
11164 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11165 CE_OTHER_IS_SMASHING, touched_side);
11167 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11168 CE_OTHER_GETS_SMASHED, hitting_side);
11174 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11176 int i, kill_x = -1, kill_y = -1;
11178 int bad_element = -1;
11179 static int test_xy[4][2] =
11186 static int test_dir[4] =
11194 for (i = 0; i < NUM_DIRECTIONS; i++)
11196 int test_x, test_y, test_move_dir, test_element;
11198 test_x = good_x + test_xy[i][0];
11199 test_y = good_y + test_xy[i][1];
11201 if (!IN_LEV_FIELD(test_x, test_y))
11205 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11207 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11209 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11210 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11212 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11213 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11217 bad_element = test_element;
11223 if (kill_x != -1 || kill_y != -1)
11225 if (IS_PLAYER(good_x, good_y))
11227 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11229 if (player->shield_deadly_time_left > 0 &&
11230 !IS_INDESTRUCTIBLE(bad_element))
11231 Bang(kill_x, kill_y);
11232 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11233 KillPlayer(player);
11236 Bang(good_x, good_y);
11240 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11242 int i, kill_x = -1, kill_y = -1;
11243 int bad_element = Feld[bad_x][bad_y];
11244 static int test_xy[4][2] =
11251 static int touch_dir[4] =
11253 MV_LEFT | MV_RIGHT,
11258 static int test_dir[4] =
11266 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11269 for (i = 0; i < NUM_DIRECTIONS; i++)
11271 int test_x, test_y, test_move_dir, test_element;
11273 test_x = bad_x + test_xy[i][0];
11274 test_y = bad_y + test_xy[i][1];
11275 if (!IN_LEV_FIELD(test_x, test_y))
11279 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11281 test_element = Feld[test_x][test_y];
11283 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11284 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11286 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11287 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11289 /* good thing is player or penguin that does not move away */
11290 if (IS_PLAYER(test_x, test_y))
11292 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11294 if (bad_element == EL_ROBOT && player->is_moving)
11295 continue; /* robot does not kill player if he is moving */
11297 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11299 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11300 continue; /* center and border element do not touch */
11307 else if (test_element == EL_PENGUIN)
11316 if (kill_x != -1 || kill_y != -1)
11318 if (IS_PLAYER(kill_x, kill_y))
11320 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11322 if (player->shield_deadly_time_left > 0 &&
11323 !IS_INDESTRUCTIBLE(bad_element))
11324 Bang(bad_x, bad_y);
11325 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11326 KillPlayer(player);
11329 Bang(kill_x, kill_y);
11333 void TestIfPlayerTouchesBadThing(int x, int y)
11335 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11338 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11340 TestIfGoodThingHitsBadThing(x, y, move_dir);
11343 void TestIfBadThingTouchesPlayer(int x, int y)
11345 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11348 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11350 TestIfBadThingHitsGoodThing(x, y, move_dir);
11353 void TestIfFriendTouchesBadThing(int x, int y)
11355 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11358 void TestIfBadThingTouchesFriend(int x, int y)
11360 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11363 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11365 int i, kill_x = bad_x, kill_y = bad_y;
11366 static int xy[4][2] =
11374 for (i = 0; i < NUM_DIRECTIONS; i++)
11378 x = bad_x + xy[i][0];
11379 y = bad_y + xy[i][1];
11380 if (!IN_LEV_FIELD(x, y))
11383 element = Feld[x][y];
11384 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11385 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11393 if (kill_x != bad_x || kill_y != bad_y)
11394 Bang(bad_x, bad_y);
11397 void KillPlayer(struct PlayerInfo *player)
11399 int jx = player->jx, jy = player->jy;
11401 if (!player->active)
11404 /* remove accessible field at the player's position */
11405 Feld[jx][jy] = EL_EMPTY;
11407 /* deactivate shield (else Bang()/Explode() would not work right) */
11408 player->shield_normal_time_left = 0;
11409 player->shield_deadly_time_left = 0;
11412 BuryPlayer(player);
11415 static void KillPlayerUnlessEnemyProtected(int x, int y)
11417 if (!PLAYER_ENEMY_PROTECTED(x, y))
11418 KillPlayer(PLAYERINFO(x, y));
11421 static void KillPlayerUnlessExplosionProtected(int x, int y)
11423 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11424 KillPlayer(PLAYERINFO(x, y));
11427 void BuryPlayer(struct PlayerInfo *player)
11429 int jx = player->jx, jy = player->jy;
11431 if (!player->active)
11434 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11435 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11437 player->GameOver = TRUE;
11438 RemovePlayer(player);
11441 void RemovePlayer(struct PlayerInfo *player)
11443 int jx = player->jx, jy = player->jy;
11444 int i, found = FALSE;
11446 player->present = FALSE;
11447 player->active = FALSE;
11449 if (!ExplodeField[jx][jy])
11450 StorePlayer[jx][jy] = 0;
11452 if (player->is_moving)
11453 DrawLevelField(player->last_jx, player->last_jy);
11455 for (i = 0; i < MAX_PLAYERS; i++)
11456 if (stored_player[i].active)
11460 AllPlayersGone = TRUE;
11466 #if USE_NEW_SNAP_DELAY
11467 static void setFieldForSnapping(int x, int y, int element, int direction)
11469 struct ElementInfo *ei = &element_info[element];
11470 int direction_bit = MV_DIR_TO_BIT(direction);
11471 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11472 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11473 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11475 Feld[x][y] = EL_ELEMENT_SNAPPING;
11476 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11478 ResetGfxAnimation(x, y);
11480 GfxElement[x][y] = element;
11481 GfxAction[x][y] = action;
11482 GfxDir[x][y] = direction;
11483 GfxFrame[x][y] = -1;
11488 =============================================================================
11489 checkDiagonalPushing()
11490 -----------------------------------------------------------------------------
11491 check if diagonal input device direction results in pushing of object
11492 (by checking if the alternative direction is walkable, diggable, ...)
11493 =============================================================================
11496 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11497 int x, int y, int real_dx, int real_dy)
11499 int jx, jy, dx, dy, xx, yy;
11501 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11504 /* diagonal direction: check alternative direction */
11509 xx = jx + (dx == 0 ? real_dx : 0);
11510 yy = jy + (dy == 0 ? real_dy : 0);
11512 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11516 =============================================================================
11518 -----------------------------------------------------------------------------
11519 x, y: field next to player (non-diagonal) to try to dig to
11520 real_dx, real_dy: direction as read from input device (can be diagonal)
11521 =============================================================================
11524 int DigField(struct PlayerInfo *player,
11525 int oldx, int oldy, int x, int y,
11526 int real_dx, int real_dy, int mode)
11528 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11529 boolean player_was_pushing = player->is_pushing;
11530 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11531 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11532 int jx = oldx, jy = oldy;
11533 int dx = x - jx, dy = y - jy;
11534 int nextx = x + dx, nexty = y + dy;
11535 int move_direction = (dx == -1 ? MV_LEFT :
11536 dx == +1 ? MV_RIGHT :
11538 dy == +1 ? MV_DOWN : MV_NONE);
11539 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11540 int dig_side = MV_DIR_OPPOSITE(move_direction);
11541 int old_element = Feld[jx][jy];
11542 #if USE_FIXED_DONT_RUN_INTO
11543 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11549 if (is_player) /* function can also be called by EL_PENGUIN */
11551 if (player->MovPos == 0)
11553 player->is_digging = FALSE;
11554 player->is_collecting = FALSE;
11557 if (player->MovPos == 0) /* last pushing move finished */
11558 player->is_pushing = FALSE;
11560 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11562 player->is_switching = FALSE;
11563 player->push_delay = -1;
11565 return MP_NO_ACTION;
11569 #if !USE_FIXED_DONT_RUN_INTO
11570 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11571 return MP_NO_ACTION;
11574 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11575 old_element = Back[jx][jy];
11577 /* in case of element dropped at player position, check background */
11578 else if (Back[jx][jy] != EL_EMPTY &&
11579 game.engine_version >= VERSION_IDENT(2,2,0,0))
11580 old_element = Back[jx][jy];
11583 #if USE_FIXED_DONT_RUN_INTO
11584 if (player_can_move && DONT_RUN_INTO(element))
11586 if (element == EL_ACID && dx == 0 && dy == 1)
11589 Feld[jx][jy] = EL_PLAYER_1;
11590 InitMovingField(jx, jy, MV_DOWN);
11591 Store[jx][jy] = EL_ACID;
11592 ContinueMoving(jx, jy);
11593 BuryPlayer(player);
11596 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11598 return MP_DONT_RUN_INTO;
11604 #if USE_FIXED_DONT_RUN_INTO
11605 if (player_can_move && DONT_RUN_INTO(element))
11607 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11609 return MP_DONT_RUN_INTO;
11614 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11615 return MP_NO_ACTION; /* field has no opening in this direction */
11617 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11618 return MP_NO_ACTION; /* field has no opening in this direction */
11621 #if USE_FIXED_DONT_RUN_INTO
11622 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11625 Feld[jx][jy] = EL_PLAYER_1;
11626 InitMovingField(jx, jy, MV_DOWN);
11627 Store[jx][jy] = EL_ACID;
11628 ContinueMoving(jx, jy);
11629 BuryPlayer(player);
11631 return MP_DONT_RUN_INTO;
11637 #if USE_FIXED_DONT_RUN_INTO
11638 if (player_can_move && DONT_RUN_INTO(element))
11640 if (element == EL_ACID && dx == 0 && dy == 1)
11643 Feld[jx][jy] = EL_PLAYER_1;
11644 InitMovingField(jx, jy, MV_DOWN);
11645 Store[jx][jy] = EL_ACID;
11646 ContinueMoving(jx, jy);
11647 BuryPlayer(player);
11650 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11652 return MP_DONT_RUN_INTO;
11657 #if USE_FIXED_DONT_RUN_INTO
11658 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11659 return MP_NO_ACTION;
11662 #if !USE_FIXED_DONT_RUN_INTO
11663 element = Feld[x][y];
11666 collect_count = element_info[element].collect_count_initial;
11668 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11669 return MP_NO_ACTION;
11671 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11672 player_can_move = player_can_move_or_snap;
11674 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11675 game.engine_version >= VERSION_IDENT(2,2,0,0))
11677 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11678 player->index_bit, dig_side);
11679 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11680 player->index_bit, dig_side);
11682 if (Feld[x][y] != element) /* field changed by snapping */
11685 return MP_NO_ACTION;
11688 if (game.gravity && is_player && !player->is_auto_moving &&
11689 canFallDown(player) && move_direction != MV_DOWN &&
11690 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11691 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11693 if (player_can_move &&
11694 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11696 int sound_element = SND_ELEMENT(element);
11697 int sound_action = ACTION_WALKING;
11699 if (IS_RND_GATE(element))
11701 if (!player->key[RND_GATE_NR(element)])
11702 return MP_NO_ACTION;
11704 else if (IS_RND_GATE_GRAY(element))
11706 if (!player->key[RND_GATE_GRAY_NR(element)])
11707 return MP_NO_ACTION;
11709 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11711 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11712 return MP_NO_ACTION;
11714 else if (element == EL_EXIT_OPEN ||
11715 element == EL_SP_EXIT_OPEN ||
11716 element == EL_SP_EXIT_OPENING)
11718 sound_action = ACTION_PASSING; /* player is passing exit */
11720 else if (element == EL_EMPTY)
11722 sound_action = ACTION_MOVING; /* nothing to walk on */
11725 /* play sound from background or player, whatever is available */
11726 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11727 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11729 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11731 else if (player_can_move &&
11732 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11734 if (!ACCESS_FROM(element, opposite_direction))
11735 return MP_NO_ACTION; /* field not accessible from this direction */
11737 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11738 return MP_NO_ACTION;
11740 if (IS_EM_GATE(element))
11742 if (!player->key[EM_GATE_NR(element)])
11743 return MP_NO_ACTION;
11745 else if (IS_EM_GATE_GRAY(element))
11747 if (!player->key[EM_GATE_GRAY_NR(element)])
11748 return MP_NO_ACTION;
11750 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11752 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11753 return MP_NO_ACTION;
11755 else if (IS_SP_PORT(element))
11757 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11758 element == EL_SP_GRAVITY_PORT_RIGHT ||
11759 element == EL_SP_GRAVITY_PORT_UP ||
11760 element == EL_SP_GRAVITY_PORT_DOWN)
11761 game.gravity = !game.gravity;
11762 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11763 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11764 element == EL_SP_GRAVITY_ON_PORT_UP ||
11765 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11766 game.gravity = TRUE;
11767 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11768 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11769 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11770 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11771 game.gravity = FALSE;
11774 /* automatically move to the next field with double speed */
11775 player->programmed_action = move_direction;
11777 if (player->move_delay_reset_counter == 0)
11779 player->move_delay_reset_counter = 2; /* two double speed steps */
11781 DOUBLE_PLAYER_SPEED(player);
11784 PlayLevelSoundAction(x, y, ACTION_PASSING);
11786 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11790 if (mode != DF_SNAP)
11792 GfxElement[x][y] = GFX_ELEMENT(element);
11793 player->is_digging = TRUE;
11796 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11798 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11799 player->index_bit, dig_side);
11801 if (mode == DF_SNAP)
11803 #if USE_NEW_SNAP_DELAY
11804 if (level.block_snap_field)
11805 setFieldForSnapping(x, y, element, move_direction);
11807 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11809 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11812 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11813 player->index_bit, dig_side);
11816 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11820 if (is_player && mode != DF_SNAP)
11822 GfxElement[x][y] = element;
11823 player->is_collecting = TRUE;
11826 if (element == EL_SPEED_PILL)
11828 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11830 else if (element == EL_EXTRA_TIME && level.time > 0)
11832 TimeLeft += level.extra_time;
11833 DrawGameValue_Time(TimeLeft);
11835 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11837 player->shield_normal_time_left += level.shield_normal_time;
11838 if (element == EL_SHIELD_DEADLY)
11839 player->shield_deadly_time_left += level.shield_deadly_time;
11841 else if (element == EL_DYNAMITE ||
11842 element == EL_EM_DYNAMITE ||
11843 element == EL_SP_DISK_RED)
11845 if (player->inventory_size < MAX_INVENTORY_SIZE)
11846 player->inventory_element[player->inventory_size++] = element;
11849 DrawGameDoorValues();
11851 DrawGameValue_Dynamite(local_player->inventory_size);
11854 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11856 player->dynabomb_count++;
11857 player->dynabombs_left++;
11859 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11861 player->dynabomb_size++;
11863 else if (element == EL_DYNABOMB_INCREASE_POWER)
11865 player->dynabomb_xl = TRUE;
11867 else if (IS_KEY(element))
11869 player->key[KEY_NR(element)] = TRUE;
11872 DrawGameDoorValues();
11874 DrawGameValue_Keys(player->key);
11877 redraw_mask |= REDRAW_DOOR_1;
11879 else if (IS_ENVELOPE(element))
11881 player->show_envelope = element;
11883 else if (element == EL_EMC_LENSES)
11885 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11887 RedrawAllInvisibleElementsForLenses();
11889 else if (element == EL_EMC_MAGNIFIER)
11891 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11893 RedrawAllInvisibleElementsForMagnifier();
11895 else if (IS_DROPPABLE(element) ||
11896 IS_THROWABLE(element)) /* can be collected and dropped */
11900 if (collect_count == 0)
11901 player->inventory_infinite_element = element;
11903 for (i = 0; i < collect_count; i++)
11904 if (player->inventory_size < MAX_INVENTORY_SIZE)
11905 player->inventory_element[player->inventory_size++] = element;
11908 DrawGameDoorValues();
11910 DrawGameValue_Dynamite(local_player->inventory_size);
11913 else if (collect_count > 0)
11915 local_player->gems_still_needed -= collect_count;
11916 if (local_player->gems_still_needed < 0)
11917 local_player->gems_still_needed = 0;
11919 DrawGameValue_Emeralds(local_player->gems_still_needed);
11922 RaiseScoreElement(element);
11923 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11926 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11927 player->index_bit, dig_side);
11929 if (mode == DF_SNAP)
11931 #if USE_NEW_SNAP_DELAY
11932 if (level.block_snap_field)
11933 setFieldForSnapping(x, y, element, move_direction);
11935 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11937 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11940 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11941 player->index_bit, dig_side);
11944 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11946 if (mode == DF_SNAP && element != EL_BD_ROCK)
11947 return MP_NO_ACTION;
11949 if (CAN_FALL(element) && dy)
11950 return MP_NO_ACTION;
11952 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11953 !(element == EL_SPRING && level.use_spring_bug))
11954 return MP_NO_ACTION;
11956 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11957 ((move_direction & MV_VERTICAL &&
11958 ((element_info[element].move_pattern & MV_LEFT &&
11959 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11960 (element_info[element].move_pattern & MV_RIGHT &&
11961 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11962 (move_direction & MV_HORIZONTAL &&
11963 ((element_info[element].move_pattern & MV_UP &&
11964 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11965 (element_info[element].move_pattern & MV_DOWN &&
11966 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11967 return MP_NO_ACTION;
11969 /* do not push elements already moving away faster than player */
11970 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11971 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11972 return MP_NO_ACTION;
11974 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11976 if (player->push_delay_value == -1 || !player_was_pushing)
11977 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11979 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11981 if (player->push_delay_value == -1)
11982 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11984 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11986 if (!player->is_pushing)
11987 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11990 player->is_pushing = TRUE;
11992 if (!(IN_LEV_FIELD(nextx, nexty) &&
11993 (IS_FREE(nextx, nexty) ||
11994 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11995 IS_SB_ELEMENT(element)))))
11996 return MP_NO_ACTION;
11998 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11999 return MP_NO_ACTION;
12001 if (player->push_delay == -1) /* new pushing; restart delay */
12002 player->push_delay = 0;
12004 if (player->push_delay < player->push_delay_value &&
12005 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12006 element != EL_SPRING && element != EL_BALLOON)
12008 /* make sure that there is no move delay before next try to push */
12009 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12010 player->move_delay = 0;
12012 return MP_NO_ACTION;
12015 if (IS_SB_ELEMENT(element))
12017 if (element == EL_SOKOBAN_FIELD_FULL)
12019 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12020 local_player->sokobanfields_still_needed++;
12023 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12025 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12026 local_player->sokobanfields_still_needed--;
12029 Feld[x][y] = EL_SOKOBAN_OBJECT;
12031 if (Back[x][y] == Back[nextx][nexty])
12032 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12033 else if (Back[x][y] != 0)
12034 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12037 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12040 if (local_player->sokobanfields_still_needed == 0 &&
12041 game.emulation == EMU_SOKOBAN)
12043 player->LevelSolved = player->GameOver = TRUE;
12044 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12048 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12050 InitMovingField(x, y, move_direction);
12051 GfxAction[x][y] = ACTION_PUSHING;
12053 if (mode == DF_SNAP)
12054 ContinueMoving(x, y);
12056 MovPos[x][y] = (dx != 0 ? dx : dy);
12058 Pushed[x][y] = TRUE;
12059 Pushed[nextx][nexty] = TRUE;
12061 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12062 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12064 player->push_delay_value = -1; /* get new value later */
12066 /* check for element change _after_ element has been pushed */
12067 if (game.use_change_when_pushing_bug)
12069 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12070 player->index_bit, dig_side);
12071 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12072 player->index_bit, dig_side);
12075 else if (IS_SWITCHABLE(element))
12077 if (PLAYER_SWITCHING(player, x, y))
12079 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12080 player->index_bit, dig_side);
12085 player->is_switching = TRUE;
12086 player->switch_x = x;
12087 player->switch_y = y;
12089 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12091 if (element == EL_ROBOT_WHEEL)
12093 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12097 DrawLevelField(x, y);
12099 else if (element == EL_SP_TERMINAL)
12104 SCAN_PLAYFIELD(xx, yy)
12106 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12109 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12111 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12112 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12115 else if (IS_BELT_SWITCH(element))
12117 ToggleBeltSwitch(x, y);
12119 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12120 element == EL_SWITCHGATE_SWITCH_DOWN)
12122 ToggleSwitchgateSwitch(x, y);
12124 else if (element == EL_LIGHT_SWITCH ||
12125 element == EL_LIGHT_SWITCH_ACTIVE)
12127 ToggleLightSwitch(x, y);
12129 else if (element == EL_TIMEGATE_SWITCH)
12131 ActivateTimegateSwitch(x, y);
12133 else if (element == EL_BALLOON_SWITCH_LEFT ||
12134 element == EL_BALLOON_SWITCH_RIGHT ||
12135 element == EL_BALLOON_SWITCH_UP ||
12136 element == EL_BALLOON_SWITCH_DOWN ||
12137 element == EL_BALLOON_SWITCH_NONE ||
12138 element == EL_BALLOON_SWITCH_ANY)
12140 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12141 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12142 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12143 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12144 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12147 else if (element == EL_LAMP)
12149 Feld[x][y] = EL_LAMP_ACTIVE;
12150 local_player->lights_still_needed--;
12152 ResetGfxAnimation(x, y);
12153 DrawLevelField(x, y);
12155 else if (element == EL_TIME_ORB_FULL)
12157 Feld[x][y] = EL_TIME_ORB_EMPTY;
12159 if (level.time > 0 || level.use_time_orb_bug)
12161 TimeLeft += level.time_orb_time;
12162 DrawGameValue_Time(TimeLeft);
12165 ResetGfxAnimation(x, y);
12166 DrawLevelField(x, y);
12168 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12169 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12173 game.ball_state = !game.ball_state;
12176 SCAN_PLAYFIELD(xx, yy)
12178 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12181 int e = Feld[xx][yy];
12183 if (game.ball_state)
12185 if (e == EL_EMC_MAGIC_BALL)
12186 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12187 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12188 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12192 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12193 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12194 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12195 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12200 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12201 player->index_bit, dig_side);
12203 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12204 player->index_bit, dig_side);
12206 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12207 player->index_bit, dig_side);
12213 if (!PLAYER_SWITCHING(player, x, y))
12215 player->is_switching = TRUE;
12216 player->switch_x = x;
12217 player->switch_y = y;
12219 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12220 player->index_bit, dig_side);
12221 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12222 player->index_bit, dig_side);
12224 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12225 player->index_bit, dig_side);
12226 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12227 player->index_bit, dig_side);
12230 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12231 player->index_bit, dig_side);
12232 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12233 player->index_bit, dig_side);
12235 return MP_NO_ACTION;
12238 player->push_delay = -1;
12240 if (is_player) /* function can also be called by EL_PENGUIN */
12242 if (Feld[x][y] != element) /* really digged/collected something */
12243 player->is_collecting = !player->is_digging;
12249 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12251 int jx = player->jx, jy = player->jy;
12252 int x = jx + dx, y = jy + dy;
12253 int snap_direction = (dx == -1 ? MV_LEFT :
12254 dx == +1 ? MV_RIGHT :
12256 dy == +1 ? MV_DOWN : MV_NONE);
12257 boolean can_continue_snapping = (level.continuous_snapping &&
12258 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12260 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12263 if (!player->active || !IN_LEV_FIELD(x, y))
12271 if (player->MovPos == 0)
12272 player->is_pushing = FALSE;
12274 player->is_snapping = FALSE;
12276 if (player->MovPos == 0)
12278 player->is_moving = FALSE;
12279 player->is_digging = FALSE;
12280 player->is_collecting = FALSE;
12286 #if USE_NEW_CONTINUOUS_SNAPPING
12287 /* prevent snapping with already pressed snap key when not allowed */
12288 if (player->is_snapping && !can_continue_snapping)
12291 if (player->is_snapping)
12295 player->MovDir = snap_direction;
12297 if (player->MovPos == 0)
12299 player->is_moving = FALSE;
12300 player->is_digging = FALSE;
12301 player->is_collecting = FALSE;
12304 player->is_dropping = FALSE;
12305 player->is_dropping_pressed = FALSE;
12306 player->drop_pressed_delay = 0;
12308 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12311 player->is_snapping = TRUE;
12313 if (player->MovPos == 0)
12315 player->is_moving = FALSE;
12316 player->is_digging = FALSE;
12317 player->is_collecting = FALSE;
12320 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12321 DrawLevelField(player->last_jx, player->last_jy);
12323 DrawLevelField(x, y);
12328 boolean DropElement(struct PlayerInfo *player)
12330 int old_element, new_element;
12331 int dropx = player->jx, dropy = player->jy;
12332 int drop_direction = player->MovDir;
12333 int drop_side = drop_direction;
12334 int drop_element = (player->inventory_size > 0 ?
12335 player->inventory_element[player->inventory_size - 1] :
12336 player->inventory_infinite_element != EL_UNDEFINED ?
12337 player->inventory_infinite_element :
12338 player->dynabombs_left > 0 ?
12339 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12342 player->is_dropping_pressed = TRUE;
12344 /* do not drop an element on top of another element; when holding drop key
12345 pressed without moving, dropped element must move away before the next
12346 element can be dropped (this is especially important if the next element
12347 is dynamite, which can be placed on background for historical reasons) */
12348 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12351 if (IS_THROWABLE(drop_element))
12353 dropx += GET_DX_FROM_DIR(drop_direction);
12354 dropy += GET_DY_FROM_DIR(drop_direction);
12356 if (!IN_LEV_FIELD(dropx, dropy))
12360 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12361 new_element = drop_element; /* default: no change when dropping */
12363 /* check if player is active, not moving and ready to drop */
12364 if (!player->active || player->MovPos || player->drop_delay > 0)
12367 /* check if player has anything that can be dropped */
12368 if (new_element == EL_UNDEFINED)
12371 /* check if drop key was pressed long enough for EM style dynamite */
12372 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12375 /* check if anything can be dropped at the current position */
12376 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12379 /* collected custom elements can only be dropped on empty fields */
12380 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12383 if (old_element != EL_EMPTY)
12384 Back[dropx][dropy] = old_element; /* store old element on this field */
12386 ResetGfxAnimation(dropx, dropy);
12387 ResetRandomAnimationValue(dropx, dropy);
12389 if (player->inventory_size > 0 ||
12390 player->inventory_infinite_element != EL_UNDEFINED)
12392 if (player->inventory_size > 0)
12394 player->inventory_size--;
12397 DrawGameDoorValues();
12399 DrawGameValue_Dynamite(local_player->inventory_size);
12402 if (new_element == EL_DYNAMITE)
12403 new_element = EL_DYNAMITE_ACTIVE;
12404 else if (new_element == EL_EM_DYNAMITE)
12405 new_element = EL_EM_DYNAMITE_ACTIVE;
12406 else if (new_element == EL_SP_DISK_RED)
12407 new_element = EL_SP_DISK_RED_ACTIVE;
12410 Feld[dropx][dropy] = new_element;
12412 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12413 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12414 el2img(Feld[dropx][dropy]), 0);
12416 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12418 /* needed if previous element just changed to "empty" in the last frame */
12419 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12421 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12422 player->index_bit, drop_side);
12423 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12425 player->index_bit, drop_side);
12427 TestIfElementTouchesCustomElement(dropx, dropy);
12429 else /* player is dropping a dyna bomb */
12431 player->dynabombs_left--;
12433 Feld[dropx][dropy] = new_element;
12435 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12436 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12437 el2img(Feld[dropx][dropy]), 0);
12439 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12442 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12443 InitField_WithBug1(dropx, dropy, FALSE);
12445 new_element = Feld[dropx][dropy]; /* element might have changed */
12447 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12448 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12450 int move_direction, nextx, nexty;
12452 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12453 MovDir[dropx][dropy] = drop_direction;
12455 move_direction = MovDir[dropx][dropy];
12456 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12457 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12459 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12460 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12463 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12464 player->is_dropping = TRUE;
12466 player->drop_pressed_delay = 0;
12467 player->is_dropping_pressed = FALSE;
12469 player->drop_x = dropx;
12470 player->drop_y = dropy;
12475 /* ------------------------------------------------------------------------- */
12476 /* game sound playing functions */
12477 /* ------------------------------------------------------------------------- */
12479 static int *loop_sound_frame = NULL;
12480 static int *loop_sound_volume = NULL;
12482 void InitPlayLevelSound()
12484 int num_sounds = getSoundListSize();
12486 checked_free(loop_sound_frame);
12487 checked_free(loop_sound_volume);
12489 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12490 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12493 static void PlayLevelSound(int x, int y, int nr)
12495 int sx = SCREENX(x), sy = SCREENY(y);
12496 int volume, stereo_position;
12497 int max_distance = 8;
12498 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12500 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12501 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12504 if (!IN_LEV_FIELD(x, y) ||
12505 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12506 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12509 volume = SOUND_MAX_VOLUME;
12511 if (!IN_SCR_FIELD(sx, sy))
12513 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12514 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12516 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12519 stereo_position = (SOUND_MAX_LEFT +
12520 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12521 (SCR_FIELDX + 2 * max_distance));
12523 if (IS_LOOP_SOUND(nr))
12525 /* This assures that quieter loop sounds do not overwrite louder ones,
12526 while restarting sound volume comparison with each new game frame. */
12528 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12531 loop_sound_volume[nr] = volume;
12532 loop_sound_frame[nr] = FrameCounter;
12535 PlaySoundExt(nr, volume, stereo_position, type);
12538 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12540 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12541 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12542 y < LEVELY(BY1) ? LEVELY(BY1) :
12543 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12547 static void PlayLevelSoundAction(int x, int y, int action)
12549 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12552 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12554 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12556 if (sound_effect != SND_UNDEFINED)
12557 PlayLevelSound(x, y, sound_effect);
12560 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12563 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12565 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12566 PlayLevelSound(x, y, sound_effect);
12569 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12571 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12573 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12574 PlayLevelSound(x, y, sound_effect);
12577 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12579 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12581 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12582 StopSound(sound_effect);
12585 static void PlayLevelMusic()
12587 if (levelset.music[level_nr] != MUS_UNDEFINED)
12588 PlayMusic(levelset.music[level_nr]); /* from config file */
12590 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12593 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12595 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12600 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12604 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12608 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12612 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12616 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12620 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12624 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12627 case SAMPLE_android_clone:
12628 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12631 case SAMPLE_android_move:
12632 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12635 case SAMPLE_spring:
12636 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12640 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12644 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12647 case SAMPLE_eater_eat:
12648 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12652 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12655 case SAMPLE_collect:
12656 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12659 case SAMPLE_diamond:
12660 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12663 case SAMPLE_squash:
12664 /* !!! CHECK THIS !!! */
12666 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12668 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12672 case SAMPLE_wonderfall:
12673 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12677 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12681 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12685 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12689 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12693 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12697 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12700 case SAMPLE_wonder:
12701 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12705 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12708 case SAMPLE_exit_open:
12709 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12712 case SAMPLE_exit_leave:
12713 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12716 case SAMPLE_dynamite:
12717 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12721 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12725 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12729 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12733 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12737 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12741 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12745 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12750 void RaiseScore(int value)
12752 local_player->score += value;
12754 DrawGameValue_Score(local_player->score);
12757 void RaiseScoreElement(int element)
12762 case EL_BD_DIAMOND:
12763 case EL_EMERALD_YELLOW:
12764 case EL_EMERALD_RED:
12765 case EL_EMERALD_PURPLE:
12766 case EL_SP_INFOTRON:
12767 RaiseScore(level.score[SC_EMERALD]);
12770 RaiseScore(level.score[SC_DIAMOND]);
12773 RaiseScore(level.score[SC_CRYSTAL]);
12776 RaiseScore(level.score[SC_PEARL]);
12779 case EL_BD_BUTTERFLY:
12780 case EL_SP_ELECTRON:
12781 RaiseScore(level.score[SC_BUG]);
12784 case EL_BD_FIREFLY:
12785 case EL_SP_SNIKSNAK:
12786 RaiseScore(level.score[SC_SPACESHIP]);
12789 case EL_DARK_YAMYAM:
12790 RaiseScore(level.score[SC_YAMYAM]);
12793 RaiseScore(level.score[SC_ROBOT]);
12796 RaiseScore(level.score[SC_PACMAN]);
12799 RaiseScore(level.score[SC_NUT]);
12802 case EL_EM_DYNAMITE:
12803 case EL_SP_DISK_RED:
12804 case EL_DYNABOMB_INCREASE_NUMBER:
12805 case EL_DYNABOMB_INCREASE_SIZE:
12806 case EL_DYNABOMB_INCREASE_POWER:
12807 RaiseScore(level.score[SC_DYNAMITE]);
12809 case EL_SHIELD_NORMAL:
12810 case EL_SHIELD_DEADLY:
12811 RaiseScore(level.score[SC_SHIELD]);
12813 case EL_EXTRA_TIME:
12814 RaiseScore(level.extra_time_score);
12828 RaiseScore(level.score[SC_KEY]);
12831 RaiseScore(element_info[element].collect_score);
12836 void RequestQuitGame(boolean ask_if_really_quit)
12838 if (AllPlayersGone ||
12839 !ask_if_really_quit ||
12840 level_editor_test_game ||
12841 Request("Do you really want to quit the game ?",
12842 REQ_ASK | REQ_STAY_CLOSED))
12844 #if defined(NETWORK_AVALIABLE)
12845 if (options.network)
12846 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12850 game_status = GAME_MODE_MAIN;
12856 if (tape.playing && tape.deactivate_display)
12857 TapeDeactivateDisplayOff(TRUE);
12859 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12861 if (tape.playing && tape.deactivate_display)
12862 TapeDeactivateDisplayOn();
12867 /* ---------- new game button stuff ---------------------------------------- */
12869 /* graphic position values for game buttons */
12870 #define GAME_BUTTON_XSIZE 30
12871 #define GAME_BUTTON_YSIZE 30
12872 #define GAME_BUTTON_XPOS 5
12873 #define GAME_BUTTON_YPOS 215
12874 #define SOUND_BUTTON_XPOS 5
12875 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12877 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12878 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12879 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12880 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12881 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12882 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12889 } gamebutton_info[NUM_GAME_BUTTONS] =
12892 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12897 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12898 GAME_CTRL_ID_PAUSE,
12902 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12907 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12908 SOUND_CTRL_ID_MUSIC,
12909 "background music on/off"
12912 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12913 SOUND_CTRL_ID_LOOPS,
12914 "sound loops on/off"
12917 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12918 SOUND_CTRL_ID_SIMPLE,
12919 "normal sounds on/off"
12923 void CreateGameButtons()
12927 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12929 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12930 struct GadgetInfo *gi;
12933 unsigned long event_mask;
12934 int gd_xoffset, gd_yoffset;
12935 int gd_x1, gd_x2, gd_y1, gd_y2;
12938 gd_xoffset = gamebutton_info[i].x;
12939 gd_yoffset = gamebutton_info[i].y;
12940 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12941 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12943 if (id == GAME_CTRL_ID_STOP ||
12944 id == GAME_CTRL_ID_PAUSE ||
12945 id == GAME_CTRL_ID_PLAY)
12947 button_type = GD_TYPE_NORMAL_BUTTON;
12949 event_mask = GD_EVENT_RELEASED;
12950 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12951 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12955 button_type = GD_TYPE_CHECK_BUTTON;
12957 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12958 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12959 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12960 event_mask = GD_EVENT_PRESSED;
12961 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12962 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12965 gi = CreateGadget(GDI_CUSTOM_ID, id,
12966 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12967 GDI_X, DX + gd_xoffset,
12968 GDI_Y, DY + gd_yoffset,
12969 GDI_WIDTH, GAME_BUTTON_XSIZE,
12970 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12971 GDI_TYPE, button_type,
12972 GDI_STATE, GD_BUTTON_UNPRESSED,
12973 GDI_CHECKED, checked,
12974 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12975 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12976 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12977 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12978 GDI_EVENT_MASK, event_mask,
12979 GDI_CALLBACK_ACTION, HandleGameButtons,
12983 Error(ERR_EXIT, "cannot create gadget");
12985 game_gadget[id] = gi;
12989 void FreeGameButtons()
12993 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12994 FreeGadget(game_gadget[i]);
12997 static void MapGameButtons()
13001 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13002 MapGadget(game_gadget[i]);
13005 void UnmapGameButtons()
13009 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13010 UnmapGadget(game_gadget[i]);
13013 static void HandleGameButtons(struct GadgetInfo *gi)
13015 int id = gi->custom_id;
13017 if (game_status != GAME_MODE_PLAYING)
13022 case GAME_CTRL_ID_STOP:
13026 RequestQuitGame(TRUE);
13029 case GAME_CTRL_ID_PAUSE:
13030 if (options.network)
13032 #if defined(NETWORK_AVALIABLE)
13034 SendToServer_ContinuePlaying();
13036 SendToServer_PausePlaying();
13040 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13043 case GAME_CTRL_ID_PLAY:
13046 #if defined(NETWORK_AVALIABLE)
13047 if (options.network)
13048 SendToServer_ContinuePlaying();
13052 tape.pausing = FALSE;
13053 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13058 case SOUND_CTRL_ID_MUSIC:
13059 if (setup.sound_music)
13061 setup.sound_music = FALSE;
13064 else if (audio.music_available)
13066 setup.sound = setup.sound_music = TRUE;
13068 SetAudioMode(setup.sound);
13074 case SOUND_CTRL_ID_LOOPS:
13075 if (setup.sound_loops)
13076 setup.sound_loops = FALSE;
13077 else if (audio.loops_available)
13079 setup.sound = setup.sound_loops = TRUE;
13080 SetAudioMode(setup.sound);
13084 case SOUND_CTRL_ID_SIMPLE:
13085 if (setup.sound_simple)
13086 setup.sound_simple = FALSE;
13087 else if (audio.sound_available)
13089 setup.sound = setup.sound_simple = TRUE;
13090 SetAudioMode(setup.sound);