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)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
55 /* for MovePlayer() */
56 #define MP_NO_ACTION 0
59 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
61 /* for ScrollPlayer() */
63 #define SCROLL_GO_ON 1
65 /* for Bang()/Explode() */
66 #define EX_PHASE_START 0
67 #define EX_TYPE_NONE 0
68 #define EX_TYPE_NORMAL (1 << 0)
69 #define EX_TYPE_CENTER (1 << 1)
70 #define EX_TYPE_BORDER (1 << 2)
71 #define EX_TYPE_CROSS (1 << 3)
72 #define EX_TYPE_DYNA (1 << 4)
73 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
75 /* special positions in the game control window (relative to control window) */
78 #define XX_EMERALDS 29
79 #define YY_EMERALDS 54
80 #define XX_DYNAMITE 29
81 #define YY_DYNAMITE 89
90 /* special positions in the game control window (relative to main window) */
91 #define DX_LEVEL (DX + XX_LEVEL)
92 #define DY_LEVEL (DY + YY_LEVEL)
93 #define DX_EMERALDS (DX + XX_EMERALDS)
94 #define DY_EMERALDS (DY + YY_EMERALDS)
95 #define DX_DYNAMITE (DX + XX_DYNAMITE)
96 #define DY_DYNAMITE (DY + YY_DYNAMITE)
97 #define DX_KEYS (DX + XX_KEYS)
98 #define DY_KEYS (DY + YY_KEYS)
99 #define DX_SCORE (DX + XX_SCORE)
100 #define DY_SCORE (DY + YY_SCORE)
101 #define DX_TIME1 (DX + XX_TIME1)
102 #define DX_TIME2 (DX + XX_TIME2)
103 #define DY_TIME (DY + YY_TIME)
105 /* values for delayed check of falling and moving elements and for collision */
106 #define CHECK_DELAY_MOVING 3
107 #define CHECK_DELAY_FALLING 3
108 #define CHECK_DELAY_COLLISION 2
110 /* values for initial player move delay (initial delay counter value) */
111 #define INITIAL_MOVE_DELAY_OFF -1
112 #define INITIAL_MOVE_DELAY_ON 0
114 /* values for player movement speed (which is in fact a delay value) */
115 #define MOVE_DELAY_MIN_SPEED 32
116 #define MOVE_DELAY_NORMAL_SPEED 8
117 #define MOVE_DELAY_HIGH_SPEED 4
118 #define MOVE_DELAY_MAX_SPEED 1
121 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
122 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
124 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
125 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
127 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
128 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
130 /* values for other actions */
131 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
132 #define MOVE_STEPSIZE_MIN (1)
133 #define MOVE_STEPSIZE_MAX (TILEX)
135 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
136 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
138 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
140 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
141 RND(element_info[e].push_delay_random))
142 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
143 RND(element_info[e].drop_delay_random))
144 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
145 RND(element_info[e].move_delay_random))
146 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
147 (element_info[e].move_delay_random))
148 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
149 RND(element_info[e].ce_value_random_initial))
150 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
151 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
152 RND((c)->delay_random * (c)->delay_frames))
153 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
154 RND((c)->delay_random))
158 #define GET_VALID_RUNTIME_ELEMENT(e) \
159 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
161 #define GET_VALID_FILE_ELEMENT(e) \
162 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
165 #define GET_TARGET_ELEMENT(e, ch, cv, cs) \
166 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
167 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
168 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
169 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
170 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
171 (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
173 #define CAN_GROW_INTO(e) \
174 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
176 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
177 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
180 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
181 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
182 (CAN_MOVE_INTO_ACID(e) && \
183 Feld[x][y] == EL_ACID) || \
186 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
187 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
188 (CAN_MOVE_INTO_ACID(e) && \
189 Feld[x][y] == EL_ACID) || \
192 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
193 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
195 (CAN_MOVE_INTO_ACID(e) && \
196 Feld[x][y] == EL_ACID) || \
197 (DONT_COLLIDE_WITH(e) && \
199 !PLAYER_ENEMY_PROTECTED(x, y))))
201 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
204 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
207 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
208 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
210 #define ANDROID_CAN_CLONE_FIELD(x, y) \
211 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
212 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
214 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
217 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
220 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
223 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
224 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
226 #define PIG_CAN_ENTER_FIELD(e, x, y) \
227 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
229 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
230 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
231 IS_FOOD_PENGUIN(Feld[x][y])))
232 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
233 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
235 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
236 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
238 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
239 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
241 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
242 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
243 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
246 #define GROUP_NR(e) ((e) - EL_GROUP_START)
247 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
248 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
250 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
251 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
254 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
256 #define CE_ENTER_FIELD_COND(e, x, y) \
257 (!IS_PLAYER(x, y) && \
258 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
260 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
261 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
263 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
264 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
266 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
267 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
268 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
269 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
271 /* game button identifiers */
272 #define GAME_CTRL_ID_STOP 0
273 #define GAME_CTRL_ID_PAUSE 1
274 #define GAME_CTRL_ID_PLAY 2
275 #define SOUND_CTRL_ID_MUSIC 3
276 #define SOUND_CTRL_ID_LOOPS 4
277 #define SOUND_CTRL_ID_SIMPLE 5
279 #define NUM_GAME_BUTTONS 6
282 /* forward declaration for internal use */
284 static void CreateField(int, int, int);
286 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
287 static void AdvanceFrameAndPlayerCounters(int);
289 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
290 static boolean MovePlayer(struct PlayerInfo *, int, int);
291 static void ScrollPlayer(struct PlayerInfo *, int);
292 static void ScrollScreen(struct PlayerInfo *, int);
294 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
296 static void InitBeltMovement(void);
297 static void CloseAllOpenTimegates(void);
298 static void CheckGravityMovement(struct PlayerInfo *);
299 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
300 static void KillPlayerUnlessEnemyProtected(int, int);
301 static void KillPlayerUnlessExplosionProtected(int, int);
303 static void TestIfPlayerTouchesCustomElement(int, int);
304 static void TestIfElementTouchesCustomElement(int, int);
305 static void TestIfElementHitsCustomElement(int, int, int);
307 static void TestIfElementSmashesCustomElement(int, int, int);
310 static void HandleElementChange(int, int, int);
311 static void ExecuteCustomElementAction(int, int, int, int);
312 static boolean ChangeElement(int, int, int, int);
314 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
315 #define CheckTriggeredElementChange(x, y, e, ev) \
316 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
317 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
318 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
319 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
321 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
322 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
324 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
325 #define CheckElementChange(x, y, e, te, ev) \
326 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
327 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
328 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
329 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
330 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
332 static void PlayLevelSound(int, int, int);
333 static void PlayLevelSoundNearest(int, int, int);
334 static void PlayLevelSoundAction(int, int, int);
335 static void PlayLevelSoundElementAction(int, int, int, int);
336 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
337 static void PlayLevelSoundActionIfLoop(int, int, int);
338 static void StopLevelSoundActionIfLoop(int, int, int);
339 static void PlayLevelMusic();
341 static void MapGameButtons();
342 static void HandleGameButtons(struct GadgetInfo *);
344 int AmoebeNachbarNr(int, int);
345 void AmoebeUmwandeln(int, int);
346 void ContinueMoving(int, int);
348 void InitMovDir(int, int);
349 void InitAmoebaNr(int, int);
350 int NewHiScore(void);
352 void TestIfGoodThingHitsBadThing(int, int, int);
353 void TestIfBadThingHitsGoodThing(int, int, int);
354 void TestIfPlayerTouchesBadThing(int, int);
355 void TestIfPlayerRunsIntoBadThing(int, int, int);
356 void TestIfBadThingTouchesPlayer(int, int);
357 void TestIfBadThingRunsIntoPlayer(int, int, int);
358 void TestIfFriendTouchesBadThing(int, int);
359 void TestIfBadThingTouchesFriend(int, int);
360 void TestIfBadThingTouchesOtherBadThing(int, int);
362 void KillPlayer(struct PlayerInfo *);
363 void BuryPlayer(struct PlayerInfo *);
364 void RemovePlayer(struct PlayerInfo *);
366 boolean SnapField(struct PlayerInfo *, int, int);
367 boolean DropElement(struct PlayerInfo *);
369 static int getInvisibleActiveFromInvisibleElement(int);
370 static int getInvisibleFromInvisibleActiveElement(int);
372 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
375 /* ------------------------------------------------------------------------- */
376 /* definition of elements that automatically change to other elements after */
377 /* a specified time, eventually calling a function when changing */
378 /* ------------------------------------------------------------------------- */
380 /* forward declaration for changer functions */
381 static void InitBuggyBase(int, int);
382 static void WarnBuggyBase(int, int);
384 static void InitTrap(int, int);
385 static void ActivateTrap(int, int);
386 static void ChangeActiveTrap(int, int);
388 static void InitRobotWheel(int, int);
389 static void RunRobotWheel(int, int);
390 static void StopRobotWheel(int, int);
392 static void InitTimegateWheel(int, int);
393 static void RunTimegateWheel(int, int);
395 static void InitMagicBallDelay(int, int);
396 static void ActivateMagicBall(int, int);
398 static void InitDiagonalMovingElement(int, int);
400 struct ChangingElementInfo
405 void (*pre_change_function)(int x, int y);
406 void (*change_function)(int x, int y);
407 void (*post_change_function)(int x, int y);
410 static struct ChangingElementInfo change_delay_list[] =
461 EL_SWITCHGATE_OPENING,
469 EL_SWITCHGATE_CLOSING,
470 EL_SWITCHGATE_CLOSED,
502 EL_ACID_SPLASH_RIGHT,
511 EL_SP_BUGGY_BASE_ACTIVATING,
518 EL_SP_BUGGY_BASE_ACTIVATING,
519 EL_SP_BUGGY_BASE_ACTIVE,
526 EL_SP_BUGGY_BASE_ACTIVE,
550 EL_ROBOT_WHEEL_ACTIVE,
558 EL_TIMEGATE_SWITCH_ACTIVE,
566 EL_EMC_MAGIC_BALL_ACTIVE,
567 EL_EMC_MAGIC_BALL_ACTIVE,
574 EL_EMC_SPRING_BUMPER_ACTIVE,
575 EL_EMC_SPRING_BUMPER,
582 EL_DIAGONAL_SHRINKING,
595 InitDiagonalMovingElement
611 int push_delay_fixed, push_delay_random;
616 { EL_BALLOON, 0, 0 },
618 { EL_SOKOBAN_OBJECT, 2, 0 },
619 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
620 { EL_SATELLITE, 2, 0 },
621 { EL_SP_DISK_YELLOW, 2, 0 },
623 { EL_UNDEFINED, 0, 0 },
631 move_stepsize_list[] =
633 { EL_AMOEBA_DROP, 2 },
634 { EL_AMOEBA_DROPPING, 2 },
635 { EL_QUICKSAND_FILLING, 1 },
636 { EL_QUICKSAND_EMPTYING, 1 },
637 { EL_MAGIC_WALL_FILLING, 2 },
638 { EL_BD_MAGIC_WALL_FILLING, 2 },
639 { EL_MAGIC_WALL_EMPTYING, 2 },
640 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
650 collect_count_list[] =
653 { EL_BD_DIAMOND, 1 },
654 { EL_EMERALD_YELLOW, 1 },
655 { EL_EMERALD_RED, 1 },
656 { EL_EMERALD_PURPLE, 1 },
658 { EL_SP_INFOTRON, 1 },
670 access_direction_list[] =
672 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
673 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
674 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
675 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
676 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
677 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
678 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
679 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
680 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
681 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
682 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
684 { EL_SP_PORT_LEFT, MV_RIGHT },
685 { EL_SP_PORT_RIGHT, MV_LEFT },
686 { EL_SP_PORT_UP, MV_DOWN },
687 { EL_SP_PORT_DOWN, MV_UP },
688 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
689 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
690 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
691 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
692 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
693 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
694 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
695 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
696 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
697 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
698 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
699 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
700 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
701 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
702 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
704 { EL_UNDEFINED, MV_NONE }
707 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
709 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
710 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
711 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
712 IS_JUST_CHANGING(x, y))
714 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
716 /* static variables for playfield scan mode (scanning forward or backward) */
717 static int playfield_scan_start_x = 0;
718 static int playfield_scan_start_y = 0;
719 static int playfield_scan_delta_x = 1;
720 static int playfield_scan_delta_y = 1;
722 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
723 (y) >= 0 && (y) <= lev_fieldy - 1; \
724 (y) += playfield_scan_delta_y) \
725 for ((x) = playfield_scan_start_x; \
726 (x) >= 0 && (x) <= lev_fieldx - 1; \
727 (x) += playfield_scan_delta_x) \
730 void DEBUG_SetMaximumDynamite()
734 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
735 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
736 local_player->inventory_element[local_player->inventory_size++] =
741 static void InitPlayfieldScanModeVars()
743 if (game.use_reverse_scan_direction)
745 playfield_scan_start_x = lev_fieldx - 1;
746 playfield_scan_start_y = lev_fieldy - 1;
748 playfield_scan_delta_x = -1;
749 playfield_scan_delta_y = -1;
753 playfield_scan_start_x = 0;
754 playfield_scan_start_y = 0;
756 playfield_scan_delta_x = 1;
757 playfield_scan_delta_y = 1;
761 static void InitPlayfieldScanMode(int mode)
763 game.use_reverse_scan_direction =
764 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
766 InitPlayfieldScanModeVars();
769 static int get_move_delay_from_stepsize(int move_stepsize)
772 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
774 /* make sure that stepsize value is always a power of 2 */
775 move_stepsize = (1 << log_2(move_stepsize));
777 return TILEX / move_stepsize;
780 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
783 int player_nr = player->index_nr;
784 int move_delay = get_move_delay_from_stepsize(move_stepsize);
785 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
787 /* do no immediately change move delay -- the player might just be moving */
788 player->move_delay_value_next = move_delay;
790 /* information if player can move must be set separately */
791 player->cannot_move = cannot_move;
795 player->move_delay = game.initial_move_delay[player_nr];
796 player->move_delay_value = game.initial_move_delay_value[player_nr];
798 player->move_delay_value_next = -1;
800 player->move_delay_reset_counter = 0;
804 void GetPlayerConfig()
806 if (!audio.sound_available)
807 setup.sound_simple = FALSE;
809 if (!audio.loops_available)
810 setup.sound_loops = FALSE;
812 if (!audio.music_available)
813 setup.sound_music = FALSE;
815 if (!video.fullscreen_available)
816 setup.fullscreen = FALSE;
818 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
820 SetAudioMode(setup.sound);
824 static int getBeltNrFromBeltElement(int element)
826 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
827 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
828 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
831 static int getBeltNrFromBeltActiveElement(int element)
833 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
834 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
835 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
838 static int getBeltNrFromBeltSwitchElement(int element)
840 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
841 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
842 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
845 static int getBeltDirNrFromBeltSwitchElement(int element)
847 static int belt_base_element[4] =
849 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
850 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
851 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
852 EL_CONVEYOR_BELT_4_SWITCH_LEFT
855 int belt_nr = getBeltNrFromBeltSwitchElement(element);
856 int belt_dir_nr = element - belt_base_element[belt_nr];
858 return (belt_dir_nr % 3);
861 static int getBeltDirFromBeltSwitchElement(int element)
863 static int belt_move_dir[3] =
870 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
872 return belt_move_dir[belt_dir_nr];
875 static int get_element_from_group_element(int element)
877 if (IS_GROUP_ELEMENT(element))
879 struct ElementGroupInfo *group = element_info[element].group;
880 int last_anim_random_frame = gfx.anim_random_frame;
883 if (group->choice_mode == ANIM_RANDOM)
884 gfx.anim_random_frame = RND(group->num_elements_resolved);
886 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
887 group->choice_mode, 0,
890 if (group->choice_mode == ANIM_RANDOM)
891 gfx.anim_random_frame = last_anim_random_frame;
895 element = group->element_resolved[element_pos];
901 static void InitPlayerField(int x, int y, int element, boolean init_game)
903 if (element == EL_SP_MURPHY)
907 if (stored_player[0].present)
909 Feld[x][y] = EL_SP_MURPHY_CLONE;
915 stored_player[0].use_murphy = TRUE;
917 if (!level.use_artwork_element[0])
918 stored_player[0].artwork_element = EL_SP_MURPHY;
921 Feld[x][y] = EL_PLAYER_1;
927 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
928 int jx = player->jx, jy = player->jy;
930 player->present = TRUE;
932 player->block_last_field = (element == EL_SP_MURPHY ?
933 level.sp_block_last_field :
934 level.block_last_field);
936 /* ---------- initialize player's last field block delay --------------- */
938 /* always start with reliable default value (no adjustment needed) */
939 player->block_delay_adjustment = 0;
941 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
942 if (player->block_last_field && element == EL_SP_MURPHY)
943 player->block_delay_adjustment = 1;
945 /* special case 2: in game engines before 3.1.1, blocking was different */
946 if (game.use_block_last_field_bug)
947 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
949 if (!options.network || player->connected)
951 player->active = TRUE;
953 /* remove potentially duplicate players */
954 if (StorePlayer[jx][jy] == Feld[x][y])
955 StorePlayer[jx][jy] = 0;
957 StorePlayer[x][y] = Feld[x][y];
961 printf("Player %d activated.\n", player->element_nr);
962 printf("[Local player is %d and currently %s.]\n",
963 local_player->element_nr,
964 local_player->active ? "active" : "not active");
968 Feld[x][y] = EL_EMPTY;
970 player->jx = player->last_jx = x;
971 player->jy = player->last_jy = y;
975 static void InitField(int x, int y, boolean init_game)
977 int element = Feld[x][y];
986 InitPlayerField(x, y, element, init_game);
989 case EL_SOKOBAN_FIELD_PLAYER:
990 element = Feld[x][y] = EL_PLAYER_1;
991 InitField(x, y, init_game);
993 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
994 InitField(x, y, init_game);
997 case EL_SOKOBAN_FIELD_EMPTY:
998 local_player->sokobanfields_still_needed++;
1002 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1003 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1004 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1005 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1006 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1007 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1008 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1009 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1010 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1011 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1020 case EL_SPACESHIP_RIGHT:
1021 case EL_SPACESHIP_UP:
1022 case EL_SPACESHIP_LEFT:
1023 case EL_SPACESHIP_DOWN:
1024 case EL_BD_BUTTERFLY:
1025 case EL_BD_BUTTERFLY_RIGHT:
1026 case EL_BD_BUTTERFLY_UP:
1027 case EL_BD_BUTTERFLY_LEFT:
1028 case EL_BD_BUTTERFLY_DOWN:
1030 case EL_BD_FIREFLY_RIGHT:
1031 case EL_BD_FIREFLY_UP:
1032 case EL_BD_FIREFLY_LEFT:
1033 case EL_BD_FIREFLY_DOWN:
1034 case EL_PACMAN_RIGHT:
1036 case EL_PACMAN_LEFT:
1037 case EL_PACMAN_DOWN:
1039 case EL_YAMYAM_LEFT:
1040 case EL_YAMYAM_RIGHT:
1042 case EL_YAMYAM_DOWN:
1043 case EL_DARK_YAMYAM:
1046 case EL_SP_SNIKSNAK:
1047 case EL_SP_ELECTRON:
1056 case EL_AMOEBA_FULL:
1061 case EL_AMOEBA_DROP:
1062 if (y == lev_fieldy - 1)
1064 Feld[x][y] = EL_AMOEBA_GROWING;
1065 Store[x][y] = EL_AMOEBA_WET;
1069 case EL_DYNAMITE_ACTIVE:
1070 case EL_SP_DISK_RED_ACTIVE:
1071 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1072 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1074 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1075 MovDelay[x][y] = 96;
1078 case EL_EM_DYNAMITE_ACTIVE:
1079 MovDelay[x][y] = 32;
1083 local_player->lights_still_needed++;
1087 local_player->friends_still_needed++;
1092 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1095 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1096 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1097 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1098 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1099 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1100 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1101 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1102 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1103 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1104 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1105 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1106 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1109 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1110 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1111 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1113 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1115 game.belt_dir[belt_nr] = belt_dir;
1116 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1118 else /* more than one switch -- set it like the first switch */
1120 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1125 #if !USE_BOTH_SWITCHGATE_SWITCHES
1126 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1128 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1132 case EL_LIGHT_SWITCH_ACTIVE:
1134 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1137 case EL_INVISIBLE_STEELWALL:
1138 case EL_INVISIBLE_WALL:
1139 case EL_INVISIBLE_SAND:
1140 if (game.light_time_left > 0 ||
1141 game.lenses_time_left > 0)
1142 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1145 case EL_EMC_MAGIC_BALL:
1146 if (game.ball_state)
1147 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1150 case EL_EMC_MAGIC_BALL_SWITCH:
1151 if (game.ball_state)
1152 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1157 if (IS_CUSTOM_ELEMENT(element))
1159 if (CAN_MOVE(element))
1162 #if USE_NEW_CUSTOM_VALUE
1163 if (!element_info[element].use_last_ce_value || init_game)
1164 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1168 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1171 else if (IS_GROUP_ELEMENT(element))
1174 Feld[x][y] = get_element_from_group_element(element);
1176 InitField(x, y, init_game);
1178 struct ElementGroupInfo *group = element_info[element].group;
1179 int last_anim_random_frame = gfx.anim_random_frame;
1182 if (group->choice_mode == ANIM_RANDOM)
1183 gfx.anim_random_frame = RND(group->num_elements_resolved);
1185 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1186 group->choice_mode, 0,
1189 if (group->choice_mode == ANIM_RANDOM)
1190 gfx.anim_random_frame = last_anim_random_frame;
1192 group->choice_pos++;
1194 Feld[x][y] = group->element_resolved[element_pos];
1196 InitField(x, y, init_game);
1204 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1209 #if USE_NEW_CUSTOM_VALUE
1212 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1214 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1222 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1224 InitField(x, y, init_game);
1226 /* not needed to call InitMovDir() -- already done by InitField()! */
1227 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1228 CAN_MOVE(Feld[x][y]))
1232 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1234 int old_element = Feld[x][y];
1236 InitField(x, y, init_game);
1238 /* not needed to call InitMovDir() -- already done by InitField()! */
1239 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1240 CAN_MOVE(old_element) &&
1241 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1244 /* this case is in fact a combination of not less than three bugs:
1245 first, it calls InitMovDir() for elements that can move, although this is
1246 already done by InitField(); then, it checks the element that was at this
1247 field _before_ the call to InitField() (which can change it); lastly, it
1248 was not called for "mole with direction" elements, which were treated as
1249 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1253 inline void DrawGameValue_Emeralds(int value)
1255 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1257 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1260 inline void DrawGameValue_Dynamite(int value)
1262 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1264 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1267 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1269 int base_key_graphic = EL_KEY_1;
1272 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1273 base_key_graphic = EL_EM_KEY_1;
1275 /* currently only 4 of 8 possible keys are displayed */
1276 for (i = 0; i < STD_NUM_KEYS; i++)
1279 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1280 el2edimg(base_key_graphic + i));
1282 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1283 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1284 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1288 inline void DrawGameValue_Score(int value)
1290 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1292 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1295 inline void DrawGameValue_Time(int value)
1297 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1298 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1300 /* clear background if value just changed its size */
1301 if (value == 999 || value == 1000)
1302 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1305 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1307 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1310 inline void DrawGameValue_Level(int value)
1313 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1316 /* misuse area for displaying emeralds to draw bigger level number */
1317 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1318 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1320 /* now copy it to the area for displaying level number */
1321 BlitBitmap(drawto, drawto,
1322 DX_EMERALDS, DY_EMERALDS + 1,
1323 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1324 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1325 DX_LEVEL - 1, DY_LEVEL + 1);
1327 /* restore the area for displaying emeralds */
1328 DrawGameValue_Emeralds(local_player->gems_still_needed);
1330 /* yes, this is all really ugly :-) */
1334 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1337 int key[MAX_NUM_KEYS];
1340 for (i = 0; i < MAX_NUM_KEYS; i++)
1341 key[i] = key_bits & (1 << i);
1343 DrawGameValue_Level(level_nr);
1345 DrawGameValue_Emeralds(emeralds);
1346 DrawGameValue_Dynamite(dynamite);
1347 DrawGameValue_Score(score);
1348 DrawGameValue_Time(time);
1350 DrawGameValue_Keys(key);
1353 void DrawGameDoorValues()
1355 int dynamite_state = 0;
1359 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1361 DrawGameDoorValues_EM();
1367 DrawGameValue_Level(level_nr);
1369 DrawGameValue_Emeralds(local_player->gems_still_needed);
1370 DrawGameValue_Dynamite(local_player->inventory_size);
1371 DrawGameValue_Score(local_player->score);
1372 DrawGameValue_Time(TimeLeft);
1376 if (game.centered_player_nr == -1)
1378 for (i = 0; i < MAX_PLAYERS; i++)
1380 for (j = 0; j < MAX_NUM_KEYS; j++)
1381 if (stored_player[i].key[j])
1382 key_bits |= (1 << j);
1384 dynamite_state += stored_player[i].inventory_size;
1388 DrawGameValue_Keys(stored_player[i].key);
1393 int player_nr = game.centered_player_nr;
1395 for (i = 0; i < MAX_NUM_KEYS; i++)
1396 if (stored_player[player_nr].key[i])
1397 key_bits |= (1 << i);
1399 dynamite_state = stored_player[player_nr].inventory_size;
1402 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1403 local_player->score, TimeLeft, key_bits);
1408 static void resolve_group_element(int group_element, int recursion_depth)
1410 static int group_nr;
1411 static struct ElementGroupInfo *group;
1412 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1415 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1417 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1418 group_element - EL_GROUP_START + 1);
1420 /* replace element which caused too deep recursion by question mark */
1421 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1426 if (recursion_depth == 0) /* initialization */
1428 group = element_info[group_element].group;
1429 group_nr = group_element - EL_GROUP_START;
1431 group->num_elements_resolved = 0;
1432 group->choice_pos = 0;
1435 for (i = 0; i < actual_group->num_elements; i++)
1437 int element = actual_group->element[i];
1439 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1442 if (IS_GROUP_ELEMENT(element))
1443 resolve_group_element(element, recursion_depth + 1);
1446 group->element_resolved[group->num_elements_resolved++] = element;
1447 element_info[element].in_group[group_nr] = TRUE;
1454 =============================================================================
1456 -----------------------------------------------------------------------------
1457 initialize game engine due to level / tape version number
1458 =============================================================================
1461 static void InitGameEngine()
1463 int i, j, k, l, x, y;
1465 /* set game engine from tape file when re-playing, else from level file */
1466 game.engine_version = (tape.playing ? tape.engine_version :
1467 level.game_version);
1469 /* ---------------------------------------------------------------------- */
1470 /* set flags for bugs and changes according to active game engine version */
1471 /* ---------------------------------------------------------------------- */
1474 Summary of bugfix/change:
1475 Fixed handling for custom elements that change when pushed by the player.
1477 Fixed/changed in version:
1481 Before 3.1.0, custom elements that "change when pushing" changed directly
1482 after the player started pushing them (until then handled in "DigField()").
1483 Since 3.1.0, these custom elements are not changed until the "pushing"
1484 move of the element is finished (now handled in "ContinueMoving()").
1486 Affected levels/tapes:
1487 The first condition is generally needed for all levels/tapes before version
1488 3.1.0, which might use the old behaviour before it was changed; known tapes
1489 that are affected are some tapes from the level set "Walpurgis Gardens" by
1491 The second condition is an exception from the above case and is needed for
1492 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1493 above (including some development versions of 3.1.0), but before it was
1494 known that this change would break tapes like the above and was fixed in
1495 3.1.1, so that the changed behaviour was active although the engine version
1496 while recording maybe was before 3.1.0. There is at least one tape that is
1497 affected by this exception, which is the tape for the one-level set "Bug
1498 Machine" by Juergen Bonhagen.
1501 game.use_change_when_pushing_bug =
1502 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1504 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1505 tape.game_version < VERSION_IDENT(3,1,1,0)));
1508 Summary of bugfix/change:
1509 Fixed handling for blocking the field the player leaves when moving.
1511 Fixed/changed in version:
1515 Before 3.1.1, when "block last field when moving" was enabled, the field
1516 the player is leaving when moving was blocked for the time of the move,
1517 and was directly unblocked afterwards. This resulted in the last field
1518 being blocked for exactly one less than the number of frames of one player
1519 move. Additionally, even when blocking was disabled, the last field was
1520 blocked for exactly one frame.
1521 Since 3.1.1, due to changes in player movement handling, the last field
1522 is not blocked at all when blocking is disabled. When blocking is enabled,
1523 the last field is blocked for exactly the number of frames of one player
1524 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1525 last field is blocked for exactly one more than the number of frames of
1528 Affected levels/tapes:
1529 (!!! yet to be determined -- probably many !!!)
1532 game.use_block_last_field_bug =
1533 (game.engine_version < VERSION_IDENT(3,1,1,0));
1536 Summary of bugfix/change:
1537 Changed behaviour of CE changes with multiple changes per single frame.
1539 Fixed/changed in version:
1543 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1544 This resulted in race conditions where CEs seem to behave strange in some
1545 situations (where triggered CE changes were just skipped because there was
1546 already a CE change on that tile in the playfield in that engine frame).
1547 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1548 (The number of changes per frame must be limited in any case, because else
1549 it is easily possible to define CE changes that would result in an infinite
1550 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1551 should be set large enough so that it would only be reached in cases where
1552 the corresponding CE change conditions run into a loop. Therefore, it seems
1553 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1554 maximal number of change pages for custom elements.)
1556 Affected levels/tapes:
1560 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1561 game.max_num_changes_per_frame = 1;
1563 game.max_num_changes_per_frame =
1564 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1567 /* ---------------------------------------------------------------------- */
1569 /* default scan direction: scan playfield from top/left to bottom/right */
1570 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1572 /* dynamically adjust element properties according to game engine version */
1573 InitElementPropertiesEngine(game.engine_version);
1576 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1577 printf(" tape version == %06d [%s] [file: %06d]\n",
1578 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1580 printf(" => game.engine_version == %06d\n", game.engine_version);
1584 /* ---------- recursively resolve group elements ------------------------- */
1586 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1587 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1588 element_info[i].in_group[j] = FALSE;
1590 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1591 resolve_group_element(EL_GROUP_START + i, 0);
1594 /* ---------- initialize player's initial move delay --------------------- */
1597 /* dynamically adjust player properties according to level information */
1598 for (i = 0; i < MAX_PLAYERS; i++)
1599 game.initial_move_delay_value[i] =
1600 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1602 /* dynamically adjust player properties according to level information */
1603 game.initial_move_delay_value =
1604 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1607 /* dynamically adjust player properties according to game engine version */
1608 for (i = 0; i < MAX_PLAYERS; i++)
1609 game.initial_move_delay[i] =
1610 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1611 game.initial_move_delay_value[i] : 0);
1613 /* ---------- initialize player's initial push delay --------------------- */
1615 /* dynamically adjust player properties according to game engine version */
1616 game.initial_push_delay_value =
1617 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1619 /* ---------- initialize changing elements ------------------------------- */
1621 /* initialize changing elements information */
1622 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1624 struct ElementInfo *ei = &element_info[i];
1626 /* this pointer might have been changed in the level editor */
1627 ei->change = &ei->change_page[0];
1629 if (!IS_CUSTOM_ELEMENT(i))
1631 ei->change->target_element = EL_EMPTY_SPACE;
1632 ei->change->delay_fixed = 0;
1633 ei->change->delay_random = 0;
1634 ei->change->delay_frames = 1;
1637 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1639 ei->has_change_event[j] = FALSE;
1641 ei->event_page_nr[j] = 0;
1642 ei->event_page[j] = &ei->change_page[0];
1646 /* add changing elements from pre-defined list */
1647 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1649 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1650 struct ElementInfo *ei = &element_info[ch_delay->element];
1652 ei->change->target_element = ch_delay->target_element;
1653 ei->change->delay_fixed = ch_delay->change_delay;
1655 ei->change->pre_change_function = ch_delay->pre_change_function;
1656 ei->change->change_function = ch_delay->change_function;
1657 ei->change->post_change_function = ch_delay->post_change_function;
1659 ei->change->can_change = TRUE;
1660 ei->change->can_change_or_has_action = TRUE;
1662 ei->has_change_event[CE_DELAY] = TRUE;
1664 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1665 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1668 /* ---------- initialize internal run-time variables ------------- */
1670 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1672 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1674 for (j = 0; j < ei->num_change_pages; j++)
1676 ei->change_page[j].can_change_or_has_action =
1677 (ei->change_page[j].can_change |
1678 ei->change_page[j].has_action);
1682 /* add change events from custom element configuration */
1683 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1685 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1687 for (j = 0; j < ei->num_change_pages; j++)
1689 if (!ei->change_page[j].can_change_or_has_action)
1692 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1694 /* only add event page for the first page found with this event */
1695 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1697 ei->has_change_event[k] = TRUE;
1699 ei->event_page_nr[k] = j;
1700 ei->event_page[k] = &ei->change_page[j];
1706 /* ---------- initialize run-time trigger player and element ------------- */
1708 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1710 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1712 for (j = 0; j < ei->num_change_pages; j++)
1714 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1715 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1716 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1717 ei->change_page[j].actual_trigger_ce_value = 0;
1718 ei->change_page[j].actual_trigger_ce_score = 0;
1722 /* ---------- initialize trigger events ---------------------------------- */
1724 /* initialize trigger events information */
1725 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1726 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1727 trigger_events[i][j] = FALSE;
1729 /* add trigger events from element change event properties */
1730 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1732 struct ElementInfo *ei = &element_info[i];
1734 for (j = 0; j < ei->num_change_pages; j++)
1736 if (!ei->change_page[j].can_change_or_has_action)
1739 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1741 int trigger_element = ei->change_page[j].trigger_element;
1743 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1745 if (ei->change_page[j].has_event[k])
1747 if (IS_GROUP_ELEMENT(trigger_element))
1749 struct ElementGroupInfo *group =
1750 element_info[trigger_element].group;
1752 for (l = 0; l < group->num_elements_resolved; l++)
1753 trigger_events[group->element_resolved[l]][k] = TRUE;
1756 trigger_events[trigger_element][k] = TRUE;
1763 /* ---------- initialize push delay -------------------------------------- */
1765 /* initialize push delay values to default */
1766 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1768 if (!IS_CUSTOM_ELEMENT(i))
1771 /* set default push delay values (corrected since version 3.0.7-1) */
1772 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1774 element_info[i].push_delay_fixed = 2;
1775 element_info[i].push_delay_random = 8;
1779 element_info[i].push_delay_fixed = 8;
1780 element_info[i].push_delay_random = 8;
1783 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1784 element_info[i].push_delay_random = game.default_push_delay_random;
1789 /* set push delay value for certain elements from pre-defined list */
1790 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1792 int e = push_delay_list[i].element;
1794 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1795 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1798 /* set push delay value for Supaplex elements for newer engine versions */
1799 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1801 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1803 if (IS_SP_ELEMENT(i))
1805 /* set SP push delay to just enough to push under a falling zonk */
1806 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1808 element_info[i].push_delay_fixed = delay;
1809 element_info[i].push_delay_random = 0;
1814 /* ---------- initialize move stepsize ----------------------------------- */
1816 /* initialize move stepsize values to default */
1817 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1818 if (!IS_CUSTOM_ELEMENT(i))
1819 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1821 /* set move stepsize value for certain elements from pre-defined list */
1822 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1824 int e = move_stepsize_list[i].element;
1826 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1829 /* ---------- initialize collect score ----------------------------------- */
1831 /* initialize collect score values for custom elements from initial value */
1832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1833 if (IS_CUSTOM_ELEMENT(i))
1834 element_info[i].collect_score = element_info[i].collect_score_initial;
1836 /* ---------- initialize collect count ----------------------------------- */
1838 /* initialize collect count values for non-custom elements */
1839 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1840 if (!IS_CUSTOM_ELEMENT(i))
1841 element_info[i].collect_count_initial = 0;
1843 /* add collect count values for all elements from pre-defined list */
1844 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1845 element_info[collect_count_list[i].element].collect_count_initial =
1846 collect_count_list[i].count;
1848 /* ---------- initialize access direction -------------------------------- */
1850 /* initialize access direction values to default (access from every side) */
1851 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1852 if (!IS_CUSTOM_ELEMENT(i))
1853 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1855 /* set access direction value for certain elements from pre-defined list */
1856 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1857 element_info[access_direction_list[i].element].access_direction =
1858 access_direction_list[i].direction;
1860 /* ---------- initialize explosion content ------------------------------- */
1861 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1863 if (IS_CUSTOM_ELEMENT(i))
1866 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1868 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1870 element_info[i].content.e[x][y] =
1871 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1872 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1873 i == EL_PLAYER_3 ? EL_EMERALD :
1874 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1875 i == EL_MOLE ? EL_EMERALD_RED :
1876 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1877 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1878 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1879 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1880 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1881 i == EL_WALL_EMERALD ? EL_EMERALD :
1882 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1883 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1884 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1885 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1886 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1887 i == EL_WALL_PEARL ? EL_PEARL :
1888 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1894 int get_num_special_action(int element, int action_first, int action_last)
1896 int num_special_action = 0;
1899 for (i = action_first; i <= action_last; i++)
1901 boolean found = FALSE;
1903 for (j = 0; j < NUM_DIRECTIONS; j++)
1904 if (el_act_dir2img(element, i, j) !=
1905 el_act_dir2img(element, ACTION_DEFAULT, j))
1909 num_special_action++;
1915 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1918 return num_special_action;
1922 =============================================================================
1924 -----------------------------------------------------------------------------
1925 initialize and start new game
1926 =============================================================================
1931 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1932 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1933 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1938 /* don't play tapes over network */
1939 network_playing = (options.network && !tape.playing);
1941 for (i = 0; i < MAX_PLAYERS; i++)
1943 struct PlayerInfo *player = &stored_player[i];
1945 player->index_nr = i;
1946 player->index_bit = (1 << i);
1947 player->element_nr = EL_PLAYER_1 + i;
1949 player->present = FALSE;
1950 player->active = FALSE;
1953 player->effective_action = 0;
1954 player->programmed_action = 0;
1957 player->gems_still_needed = level.gems_needed;
1958 player->sokobanfields_still_needed = 0;
1959 player->lights_still_needed = 0;
1960 player->friends_still_needed = 0;
1962 for (j = 0; j < MAX_NUM_KEYS; j++)
1963 player->key[j] = FALSE;
1965 player->dynabomb_count = 0;
1966 player->dynabomb_size = 1;
1967 player->dynabombs_left = 0;
1968 player->dynabomb_xl = FALSE;
1970 player->MovDir = MV_NONE;
1973 player->GfxDir = MV_NONE;
1974 player->GfxAction = ACTION_DEFAULT;
1976 player->StepFrame = 0;
1978 player->use_murphy = FALSE;
1979 player->artwork_element =
1980 (level.use_artwork_element[i] ? level.artwork_element[i] :
1981 player->element_nr);
1983 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1984 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1986 player->gravity = level.initial_player_gravity[i];
1988 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1990 player->actual_frame_counter = 0;
1992 player->step_counter = 0;
1994 player->last_move_dir = MV_NONE;
1996 player->is_waiting = FALSE;
1997 player->is_moving = FALSE;
1998 player->is_auto_moving = FALSE;
1999 player->is_digging = FALSE;
2000 player->is_snapping = FALSE;
2001 player->is_collecting = FALSE;
2002 player->is_pushing = FALSE;
2003 player->is_switching = FALSE;
2004 player->is_dropping = FALSE;
2005 player->is_dropping_pressed = FALSE;
2007 player->is_bored = FALSE;
2008 player->is_sleeping = FALSE;
2010 player->frame_counter_bored = -1;
2011 player->frame_counter_sleeping = -1;
2013 player->anim_delay_counter = 0;
2014 player->post_delay_counter = 0;
2016 player->dir_waiting = MV_NONE;
2017 player->action_waiting = ACTION_DEFAULT;
2018 player->last_action_waiting = ACTION_DEFAULT;
2019 player->special_action_bored = ACTION_DEFAULT;
2020 player->special_action_sleeping = ACTION_DEFAULT;
2023 /* cannot be set here -- could be modified in Init[Player]Field() below */
2025 /* set number of special actions for bored and sleeping animation */
2026 player->num_special_action_bored =
2027 get_num_special_action(player->artwork_element,
2028 ACTION_BORING_1, ACTION_BORING_LAST);
2029 player->num_special_action_sleeping =
2030 get_num_special_action(player->artwork_element,
2031 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2034 player->switch_x = -1;
2035 player->switch_y = -1;
2037 player->drop_x = -1;
2038 player->drop_y = -1;
2040 player->show_envelope = 0;
2043 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2045 player->move_delay = game.initial_move_delay;
2046 player->move_delay_value = game.initial_move_delay_value;
2048 player->move_delay_value_next = -1;
2050 player->move_delay_reset_counter = 0;
2052 player->cannot_move = FALSE;
2055 player->push_delay = -1; /* initialized when pushing starts */
2056 player->push_delay_value = game.initial_push_delay_value;
2058 player->drop_delay = 0;
2059 player->drop_pressed_delay = 0;
2061 player->last_jx = player->last_jy = 0;
2062 player->jx = player->jy = 0;
2064 player->shield_normal_time_left = 0;
2065 player->shield_deadly_time_left = 0;
2067 player->inventory_infinite_element = EL_UNDEFINED;
2068 player->inventory_size = 0;
2070 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2071 SnapField(player, 0, 0);
2073 player->LevelSolved = FALSE;
2074 player->GameOver = FALSE;
2077 network_player_action_received = FALSE;
2079 #if defined(NETWORK_AVALIABLE)
2080 /* initial null action */
2081 if (network_playing)
2082 SendToServer_MovePlayer(MV_NONE);
2091 TimeLeft = level.time;
2094 ScreenMovDir = MV_NONE;
2098 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2100 AllPlayersGone = FALSE;
2102 game.yamyam_content_nr = 0;
2103 game.magic_wall_active = FALSE;
2104 game.magic_wall_time_left = 0;
2105 game.light_time_left = 0;
2106 game.timegate_time_left = 0;
2107 game.switchgate_pos = 0;
2108 game.wind_direction = level.wind_direction_initial;
2110 #if !USE_PLAYER_GRAVITY
2112 game.gravity = FALSE;
2114 game.gravity = level.initial_gravity;
2116 game.explosions_delayed = TRUE;
2119 game.lenses_time_left = 0;
2120 game.magnify_time_left = 0;
2122 game.ball_state = level.ball_state_initial;
2123 game.ball_content_nr = 0;
2125 game.envelope_active = FALSE;
2127 /* set focus to local player for network games, else to all players */
2128 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2129 game.centered_player_nr_next = game.centered_player_nr;
2130 game.set_centered_player = FALSE;
2132 if (network_playing && tape.recording)
2134 /* store client dependent player focus when recording network games */
2135 tape.centered_player_nr_next = game.centered_player_nr_next;
2136 tape.set_centered_player = TRUE;
2140 printf("::: focus set to player %d [%d]\n",
2141 game.centered_player_nr, local_player->index_nr);
2144 for (i = 0; i < NUM_BELTS; i++)
2146 game.belt_dir[i] = MV_NONE;
2147 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2150 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2151 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2154 SCAN_PLAYFIELD(x, y)
2156 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2159 Feld[x][y] = level.field[x][y];
2160 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2161 ChangeDelay[x][y] = 0;
2162 ChangePage[x][y] = -1;
2163 #if USE_NEW_CUSTOM_VALUE
2164 CustomValue[x][y] = 0; /* initialized in InitField() */
2166 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2168 WasJustMoving[x][y] = 0;
2169 WasJustFalling[x][y] = 0;
2170 CheckCollision[x][y] = 0;
2172 Pushed[x][y] = FALSE;
2174 ChangeCount[x][y] = 0;
2175 ChangeEvent[x][y] = -1;
2177 ExplodePhase[x][y] = 0;
2178 ExplodeDelay[x][y] = 0;
2179 ExplodeField[x][y] = EX_TYPE_NONE;
2181 RunnerVisit[x][y] = 0;
2182 PlayerVisit[x][y] = 0;
2185 GfxRandom[x][y] = INIT_GFX_RANDOM();
2186 GfxElement[x][y] = EL_UNDEFINED;
2187 GfxAction[x][y] = ACTION_DEFAULT;
2188 GfxDir[x][y] = MV_NONE;
2192 SCAN_PLAYFIELD(x, y)
2194 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2197 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2199 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2201 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2204 InitField(x, y, TRUE);
2209 for (i = 0; i < MAX_PLAYERS; i++)
2211 struct PlayerInfo *player = &stored_player[i];
2214 /* set number of special actions for bored and sleeping animation */
2215 player->num_special_action_bored =
2216 get_num_special_action(player->artwork_element,
2217 ACTION_BORING_1, ACTION_BORING_LAST);
2218 player->num_special_action_sleeping =
2219 get_num_special_action(player->artwork_element,
2220 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2225 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2226 emulate_sb ? EMU_SOKOBAN :
2227 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2229 #if USE_NEW_ALL_SLIPPERY
2230 /* initialize type of slippery elements */
2231 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2233 if (!IS_CUSTOM_ELEMENT(i))
2235 /* default: elements slip down either to the left or right randomly */
2236 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2238 /* SP style elements prefer to slip down on the left side */
2239 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2240 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2242 /* BD style elements prefer to slip down on the left side */
2243 if (game.emulation == EMU_BOULDERDASH)
2244 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2249 /* initialize explosion and ignition delay */
2250 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2252 if (!IS_CUSTOM_ELEMENT(i))
2255 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2256 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2257 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2258 int last_phase = (num_phase + 1) * delay;
2259 int half_phase = (num_phase / 2) * delay;
2261 element_info[i].explosion_delay = last_phase - 1;
2262 element_info[i].ignition_delay = half_phase;
2264 if (i == EL_BLACK_ORB)
2265 element_info[i].ignition_delay = 1;
2269 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2270 element_info[i].explosion_delay = 1;
2272 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2273 element_info[i].ignition_delay = 1;
2277 /* correct non-moving belts to start moving left */
2278 for (i = 0; i < NUM_BELTS; i++)
2279 if (game.belt_dir[i] == MV_NONE)
2280 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2282 /* check if any connected player was not found in playfield */
2283 for (i = 0; i < MAX_PLAYERS; i++)
2285 struct PlayerInfo *player = &stored_player[i];
2287 if (player->connected && !player->present)
2289 for (j = 0; j < MAX_PLAYERS; j++)
2291 struct PlayerInfo *some_player = &stored_player[j];
2292 int jx = some_player->jx, jy = some_player->jy;
2294 /* assign first free player found that is present in the playfield */
2295 if (some_player->present && !some_player->connected)
2297 player->present = TRUE;
2298 player->active = TRUE;
2300 some_player->present = FALSE;
2301 some_player->active = FALSE;
2304 player->element_nr = some_player->element_nr;
2307 player->artwork_element = some_player->artwork_element;
2309 player->block_last_field = some_player->block_last_field;
2310 player->block_delay_adjustment = some_player->block_delay_adjustment;
2312 StorePlayer[jx][jy] = player->element_nr;
2313 player->jx = player->last_jx = jx;
2314 player->jy = player->last_jy = jy;
2324 /* when playing a tape, eliminate all players who do not participate */
2326 for (i = 0; i < MAX_PLAYERS; i++)
2328 if (stored_player[i].active && !tape.player_participates[i])
2330 struct PlayerInfo *player = &stored_player[i];
2331 int jx = player->jx, jy = player->jy;
2333 player->active = FALSE;
2334 StorePlayer[jx][jy] = 0;
2335 Feld[jx][jy] = EL_EMPTY;
2339 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2341 /* when in single player mode, eliminate all but the first active player */
2343 for (i = 0; i < MAX_PLAYERS; i++)
2345 if (stored_player[i].active)
2347 for (j = i + 1; j < MAX_PLAYERS; j++)
2349 if (stored_player[j].active)
2351 struct PlayerInfo *player = &stored_player[j];
2352 int jx = player->jx, jy = player->jy;
2354 player->active = FALSE;
2355 player->present = FALSE;
2357 StorePlayer[jx][jy] = 0;
2358 Feld[jx][jy] = EL_EMPTY;
2365 /* when recording the game, store which players take part in the game */
2368 for (i = 0; i < MAX_PLAYERS; i++)
2369 if (stored_player[i].active)
2370 tape.player_participates[i] = TRUE;
2375 for (i = 0; i < MAX_PLAYERS; i++)
2377 struct PlayerInfo *player = &stored_player[i];
2379 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2384 if (local_player == player)
2385 printf("Player %d is local player.\n", i+1);
2389 if (BorderElement == EL_EMPTY)
2392 SBX_Right = lev_fieldx - SCR_FIELDX;
2394 SBY_Lower = lev_fieldy - SCR_FIELDY;
2399 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2401 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2404 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2405 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2407 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2408 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2410 /* if local player not found, look for custom element that might create
2411 the player (make some assumptions about the right custom element) */
2412 if (!local_player->present)
2414 int start_x = 0, start_y = 0;
2415 int found_rating = 0;
2416 int found_element = EL_UNDEFINED;
2417 int player_nr = local_player->index_nr;
2420 SCAN_PLAYFIELD(x, y)
2422 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2425 int element = Feld[x][y];
2430 if (level.use_start_element[player_nr] &&
2431 level.start_element[player_nr] == element &&
2438 found_element = element;
2441 if (!IS_CUSTOM_ELEMENT(element))
2444 if (CAN_CHANGE(element))
2446 for (i = 0; i < element_info[element].num_change_pages; i++)
2448 /* check for player created from custom element as single target */
2449 content = element_info[element].change_page[i].target_element;
2450 is_player = ELEM_IS_PLAYER(content);
2452 if (is_player && (found_rating < 3 || element < found_element))
2458 found_element = element;
2463 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2465 /* check for player created from custom element as explosion content */
2466 content = element_info[element].content.e[xx][yy];
2467 is_player = ELEM_IS_PLAYER(content);
2469 if (is_player && (found_rating < 2 || element < found_element))
2471 start_x = x + xx - 1;
2472 start_y = y + yy - 1;
2475 found_element = element;
2478 if (!CAN_CHANGE(element))
2481 for (i = 0; i < element_info[element].num_change_pages; i++)
2483 /* check for player created from custom element as extended target */
2485 element_info[element].change_page[i].target_content.e[xx][yy];
2487 is_player = ELEM_IS_PLAYER(content);
2489 if (is_player && (found_rating < 1 || element < found_element))
2491 start_x = x + xx - 1;
2492 start_y = y + yy - 1;
2495 found_element = element;
2501 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2502 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2505 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2506 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2511 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2512 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2513 local_player->jx - MIDPOSX);
2515 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2516 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2517 local_player->jy - MIDPOSY);
2520 if (!game.restart_level)
2521 CloseDoor(DOOR_CLOSE_1);
2523 /* !!! FIX THIS (START) !!! */
2524 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2526 InitGameEngine_EM();
2533 /* after drawing the level, correct some elements */
2534 if (game.timegate_time_left == 0)
2535 CloseAllOpenTimegates();
2537 if (setup.soft_scrolling)
2538 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2540 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2543 /* !!! FIX THIS (END) !!! */
2545 if (!game.restart_level)
2547 /* copy default game door content to main double buffer */
2548 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2552 DrawGameDoorValues();
2554 if (!game.restart_level)
2558 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2559 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2560 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2564 /* copy actual game door content to door double buffer for OpenDoor() */
2565 BlitBitmap(drawto, bitmap_db_door,
2566 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2568 OpenDoor(DOOR_OPEN_ALL);
2570 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2572 if (setup.sound_music)
2575 KeyboardAutoRepeatOffUnlessAutoplay();
2579 for (i = 0; i < MAX_PLAYERS; i++)
2580 printf("Player %d %sactive.\n",
2581 i + 1, (stored_player[i].active ? "" : "not "));
2585 game.restart_level = FALSE;
2588 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2590 /* this is used for non-R'n'D game engines to update certain engine values */
2592 /* needed to determine if sounds are played within the visible screen area */
2593 scroll_x = actual_scroll_x;
2594 scroll_y = actual_scroll_y;
2597 void InitMovDir(int x, int y)
2599 int i, element = Feld[x][y];
2600 static int xy[4][2] =
2607 static int direction[3][4] =
2609 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2610 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2611 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2620 Feld[x][y] = EL_BUG;
2621 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2624 case EL_SPACESHIP_RIGHT:
2625 case EL_SPACESHIP_UP:
2626 case EL_SPACESHIP_LEFT:
2627 case EL_SPACESHIP_DOWN:
2628 Feld[x][y] = EL_SPACESHIP;
2629 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2632 case EL_BD_BUTTERFLY_RIGHT:
2633 case EL_BD_BUTTERFLY_UP:
2634 case EL_BD_BUTTERFLY_LEFT:
2635 case EL_BD_BUTTERFLY_DOWN:
2636 Feld[x][y] = EL_BD_BUTTERFLY;
2637 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2640 case EL_BD_FIREFLY_RIGHT:
2641 case EL_BD_FIREFLY_UP:
2642 case EL_BD_FIREFLY_LEFT:
2643 case EL_BD_FIREFLY_DOWN:
2644 Feld[x][y] = EL_BD_FIREFLY;
2645 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2648 case EL_PACMAN_RIGHT:
2650 case EL_PACMAN_LEFT:
2651 case EL_PACMAN_DOWN:
2652 Feld[x][y] = EL_PACMAN;
2653 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2656 case EL_YAMYAM_LEFT:
2657 case EL_YAMYAM_RIGHT:
2659 case EL_YAMYAM_DOWN:
2660 Feld[x][y] = EL_YAMYAM;
2661 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2664 case EL_SP_SNIKSNAK:
2665 MovDir[x][y] = MV_UP;
2668 case EL_SP_ELECTRON:
2669 MovDir[x][y] = MV_LEFT;
2676 Feld[x][y] = EL_MOLE;
2677 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2681 if (IS_CUSTOM_ELEMENT(element))
2683 struct ElementInfo *ei = &element_info[element];
2684 int move_direction_initial = ei->move_direction_initial;
2685 int move_pattern = ei->move_pattern;
2687 if (move_direction_initial == MV_START_PREVIOUS)
2689 if (MovDir[x][y] != MV_NONE)
2692 move_direction_initial = MV_START_AUTOMATIC;
2695 if (move_direction_initial == MV_START_RANDOM)
2696 MovDir[x][y] = 1 << RND(4);
2697 else if (move_direction_initial & MV_ANY_DIRECTION)
2698 MovDir[x][y] = move_direction_initial;
2699 else if (move_pattern == MV_ALL_DIRECTIONS ||
2700 move_pattern == MV_TURNING_LEFT ||
2701 move_pattern == MV_TURNING_RIGHT ||
2702 move_pattern == MV_TURNING_LEFT_RIGHT ||
2703 move_pattern == MV_TURNING_RIGHT_LEFT ||
2704 move_pattern == MV_TURNING_RANDOM)
2705 MovDir[x][y] = 1 << RND(4);
2706 else if (move_pattern == MV_HORIZONTAL)
2707 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2708 else if (move_pattern == MV_VERTICAL)
2709 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2710 else if (move_pattern & MV_ANY_DIRECTION)
2711 MovDir[x][y] = element_info[element].move_pattern;
2712 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2713 move_pattern == MV_ALONG_RIGHT_SIDE)
2715 /* use random direction as default start direction */
2716 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2717 MovDir[x][y] = 1 << RND(4);
2719 for (i = 0; i < NUM_DIRECTIONS; i++)
2721 int x1 = x + xy[i][0];
2722 int y1 = y + xy[i][1];
2724 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2726 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2727 MovDir[x][y] = direction[0][i];
2729 MovDir[x][y] = direction[1][i];
2738 MovDir[x][y] = 1 << RND(4);
2740 if (element != EL_BUG &&
2741 element != EL_SPACESHIP &&
2742 element != EL_BD_BUTTERFLY &&
2743 element != EL_BD_FIREFLY)
2746 for (i = 0; i < NUM_DIRECTIONS; i++)
2748 int x1 = x + xy[i][0];
2749 int y1 = y + xy[i][1];
2751 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2753 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2755 MovDir[x][y] = direction[0][i];
2758 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2759 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2761 MovDir[x][y] = direction[1][i];
2770 GfxDir[x][y] = MovDir[x][y];
2773 void InitAmoebaNr(int x, int y)
2776 int group_nr = AmoebeNachbarNr(x, y);
2780 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2782 if (AmoebaCnt[i] == 0)
2790 AmoebaNr[x][y] = group_nr;
2791 AmoebaCnt[group_nr]++;
2792 AmoebaCnt2[group_nr]++;
2798 boolean raise_level = FALSE;
2800 if (local_player->MovPos)
2803 if (tape.auto_play) /* tape might already be stopped here */
2804 tape.auto_play_level_solved = TRUE;
2806 local_player->LevelSolved = FALSE;
2808 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2812 if (!tape.playing && setup.sound_loops)
2813 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2814 SND_CTRL_PLAY_LOOP);
2816 while (TimeLeft > 0)
2818 if (!tape.playing && !setup.sound_loops)
2819 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2821 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2824 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2829 RaiseScore(level.score[SC_TIME_BONUS]);
2832 DrawGameValue_Time(TimeLeft);
2840 if (!tape.playing && setup.sound_loops)
2841 StopSound(SND_GAME_LEVELTIME_BONUS);
2843 else if (level.time == 0) /* level without time limit */
2845 if (!tape.playing && setup.sound_loops)
2846 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2847 SND_CTRL_PLAY_LOOP);
2849 while (TimePlayed < 999)
2851 if (!tape.playing && !setup.sound_loops)
2852 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2854 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2857 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2862 RaiseScore(level.score[SC_TIME_BONUS]);
2865 DrawGameValue_Time(TimePlayed);
2873 if (!tape.playing && setup.sound_loops)
2874 StopSound(SND_GAME_LEVELTIME_BONUS);
2877 /* close exit door after last player */
2878 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2879 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2880 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2882 int element = Feld[ExitX][ExitY];
2884 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2885 EL_SP_EXIT_CLOSING);
2887 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2890 /* player disappears */
2891 if (ExitX >= 0 && ExitY >= 0)
2892 DrawLevelField(ExitX, ExitY);
2898 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2900 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2906 CloseDoor(DOOR_CLOSE_1);
2911 SaveTape(tape.level_nr); /* Ask to save tape */
2914 if (level_nr == leveldir_current->handicap_level)
2916 leveldir_current->handicap_level++;
2917 SaveLevelSetup_SeriesInfo();
2920 if (level_editor_test_game)
2921 local_player->score = -1; /* no highscore when playing from editor */
2922 else if (level_nr < leveldir_current->last_level)
2923 raise_level = TRUE; /* advance to next level */
2925 if ((hi_pos = NewHiScore()) >= 0)
2927 game_status = GAME_MODE_SCORES;
2928 DrawHallOfFame(hi_pos);
2937 game_status = GAME_MODE_MAIN;
2954 LoadScore(level_nr);
2956 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2957 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2960 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2962 if (local_player->score > highscore[k].Score)
2964 /* player has made it to the hall of fame */
2966 if (k < MAX_SCORE_ENTRIES - 1)
2968 int m = MAX_SCORE_ENTRIES - 1;
2971 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2972 if (strEqual(setup.player_name, highscore[l].Name))
2974 if (m == k) /* player's new highscore overwrites his old one */
2978 for (l = m; l > k; l--)
2980 strcpy(highscore[l].Name, highscore[l - 1].Name);
2981 highscore[l].Score = highscore[l - 1].Score;
2988 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2989 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2990 highscore[k].Score = local_player->score;
2996 else if (!strncmp(setup.player_name, highscore[k].Name,
2997 MAX_PLAYER_NAME_LEN))
2998 break; /* player already there with a higher score */
3004 SaveScore(level_nr);
3009 inline static int getElementMoveStepsize(int x, int y)
3011 int element = Feld[x][y];
3012 int direction = MovDir[x][y];
3013 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3014 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3015 int horiz_move = (dx != 0);
3016 int sign = (horiz_move ? dx : dy);
3017 int step = sign * element_info[element].move_stepsize;
3019 /* special values for move stepsize for spring and things on conveyor belt */
3023 if (element == EL_SPRING)
3024 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3025 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3026 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3027 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3029 if (CAN_FALL(element) &&
3030 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3031 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3032 else if (element == EL_SPRING)
3033 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3040 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3042 if (player->GfxAction != action || player->GfxDir != dir)
3045 printf("Player frame reset! (%d => %d, %d => %d)\n",
3046 player->GfxAction, action, player->GfxDir, dir);
3049 player->GfxAction = action;
3050 player->GfxDir = dir;
3052 player->StepFrame = 0;
3056 #if USE_GFX_RESET_GFX_ANIMATION
3057 static void ResetGfxFrame(int x, int y, boolean redraw)
3059 int element = Feld[x][y];
3060 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3061 int last_gfx_frame = GfxFrame[x][y];
3063 if (graphic_info[graphic].anim_global_sync)
3064 GfxFrame[x][y] = FrameCounter;
3065 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3066 GfxFrame[x][y] = CustomValue[x][y];
3067 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3068 GfxFrame[x][y] = element_info[element].collect_score;
3069 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3070 GfxFrame[x][y] = ChangeDelay[x][y];
3072 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3073 DrawLevelGraphicAnimation(x, y, graphic);
3077 static void ResetGfxAnimation(int x, int y)
3080 int element, graphic;
3083 GfxAction[x][y] = ACTION_DEFAULT;
3084 GfxDir[x][y] = MovDir[x][y];
3088 element = Feld[x][y];
3089 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3091 if (graphic_info[graphic].anim_global_sync)
3092 GfxFrame[x][y] = FrameCounter;
3093 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3094 GfxFrame[x][y] = CustomValue[x][y];
3095 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3096 GfxFrame[x][y] = element_info[element].collect_score;
3097 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3098 GfxFrame[x][y] = ChangeDelay[x][y];
3101 #if USE_GFX_RESET_GFX_ANIMATION
3102 ResetGfxFrame(x, y, FALSE);
3106 static void ResetRandomAnimationValue(int x, int y)
3108 GfxRandom[x][y] = INIT_GFX_RANDOM();
3111 void InitMovingField(int x, int y, int direction)
3113 int element = Feld[x][y];
3117 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3118 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3122 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3123 ResetGfxAnimation(x, y);
3125 MovDir[x][y] = direction;
3126 GfxDir[x][y] = direction;
3127 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3128 ACTION_FALLING : ACTION_MOVING);
3131 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3133 if (graphic_info[graphic].anim_global_sync)
3134 GfxFrame[x][y] = FrameCounter;
3135 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3136 GfxFrame[x][y] = CustomValue[x][y];
3137 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3138 GfxFrame[x][y] = element_info[element].collect_score;
3139 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3140 GfxFrame[x][y] = ChangeDelay[x][y];
3143 /* this is needed for CEs with property "can move" / "not moving" */
3145 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3147 if (Feld[newx][newy] == EL_EMPTY)
3148 Feld[newx][newy] = EL_BLOCKED;
3150 MovDir[newx][newy] = MovDir[x][y];
3152 #if USE_NEW_CUSTOM_VALUE
3153 CustomValue[newx][newy] = CustomValue[x][y];
3156 GfxFrame[newx][newy] = GfxFrame[x][y];
3157 GfxRandom[newx][newy] = GfxRandom[x][y];
3158 GfxAction[newx][newy] = GfxAction[x][y];
3159 GfxDir[newx][newy] = GfxDir[x][y];
3163 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3165 int direction = MovDir[x][y];
3167 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3168 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3170 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3171 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3178 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3180 int oldx = x, oldy = y;
3181 int direction = MovDir[x][y];
3183 if (direction == MV_LEFT)
3185 else if (direction == MV_RIGHT)
3187 else if (direction == MV_UP)
3189 else if (direction == MV_DOWN)
3192 *comes_from_x = oldx;
3193 *comes_from_y = oldy;
3196 int MovingOrBlocked2Element(int x, int y)
3198 int element = Feld[x][y];
3200 if (element == EL_BLOCKED)
3204 Blocked2Moving(x, y, &oldx, &oldy);
3205 return Feld[oldx][oldy];
3211 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3213 /* like MovingOrBlocked2Element(), but if element is moving
3214 and (x,y) is the field the moving element is just leaving,
3215 return EL_BLOCKED instead of the element value */
3216 int element = Feld[x][y];
3218 if (IS_MOVING(x, y))
3220 if (element == EL_BLOCKED)
3224 Blocked2Moving(x, y, &oldx, &oldy);
3225 return Feld[oldx][oldy];
3234 static void RemoveField(int x, int y)
3236 Feld[x][y] = EL_EMPTY;
3242 #if USE_NEW_CUSTOM_VALUE
3243 CustomValue[x][y] = 0;
3247 ChangeDelay[x][y] = 0;
3248 ChangePage[x][y] = -1;
3249 Pushed[x][y] = FALSE;
3252 ExplodeField[x][y] = EX_TYPE_NONE;
3255 GfxElement[x][y] = EL_UNDEFINED;
3256 GfxAction[x][y] = ACTION_DEFAULT;
3257 GfxDir[x][y] = MV_NONE;
3260 void RemoveMovingField(int x, int y)
3262 int oldx = x, oldy = y, newx = x, newy = y;
3263 int element = Feld[x][y];
3264 int next_element = EL_UNDEFINED;
3266 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3269 if (IS_MOVING(x, y))
3271 Moving2Blocked(x, y, &newx, &newy);
3273 if (Feld[newx][newy] != EL_BLOCKED)
3275 /* element is moving, but target field is not free (blocked), but
3276 already occupied by something different (example: acid pool);
3277 in this case, only remove the moving field, but not the target */
3279 RemoveField(oldx, oldy);
3281 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3283 DrawLevelField(oldx, oldy);
3288 else if (element == EL_BLOCKED)
3290 Blocked2Moving(x, y, &oldx, &oldy);
3291 if (!IS_MOVING(oldx, oldy))
3295 if (element == EL_BLOCKED &&
3296 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3297 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3298 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3299 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3300 next_element = get_next_element(Feld[oldx][oldy]);
3302 RemoveField(oldx, oldy);
3303 RemoveField(newx, newy);
3305 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3307 if (next_element != EL_UNDEFINED)
3308 Feld[oldx][oldy] = next_element;
3310 DrawLevelField(oldx, oldy);
3311 DrawLevelField(newx, newy);
3314 void DrawDynamite(int x, int y)
3316 int sx = SCREENX(x), sy = SCREENY(y);
3317 int graphic = el2img(Feld[x][y]);
3320 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3323 if (IS_WALKABLE_INSIDE(Back[x][y]))
3327 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3328 else if (Store[x][y])
3329 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3331 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3333 if (Back[x][y] || Store[x][y])
3334 DrawGraphicThruMask(sx, sy, graphic, frame);
3336 DrawGraphic(sx, sy, graphic, frame);
3339 void CheckDynamite(int x, int y)
3341 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3345 if (MovDelay[x][y] != 0)
3348 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3354 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3361 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3363 boolean num_checked_players = 0;
3366 for (i = 0; i < MAX_PLAYERS; i++)
3368 if (stored_player[i].active)
3370 int sx = stored_player[i].jx;
3371 int sy = stored_player[i].jy;
3373 if (num_checked_players == 0)
3380 *sx1 = MIN(*sx1, sx);
3381 *sy1 = MIN(*sy1, sy);
3382 *sx2 = MAX(*sx2, sx);
3383 *sy2 = MAX(*sy2, sy);
3386 num_checked_players++;
3391 static boolean checkIfAllPlayersFitToScreen_RND()
3393 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3395 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3397 return (sx2 - sx1 < SCR_FIELDX &&
3398 sy2 - sy1 < SCR_FIELDY);
3401 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3403 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3405 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3407 *sx = (sx1 + sx2) / 2;
3408 *sy = (sy1 + sy2) / 2;
3412 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3413 int center_x, int center_y)
3415 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3417 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3419 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3420 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3423 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3427 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3429 return (max_dx <= SCR_FIELDX / 2 &&
3430 max_dy <= SCR_FIELDY / 2);
3438 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3439 boolean quick_relocation)
3441 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3442 boolean no_delay = (tape.warp_forward);
3443 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3444 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3446 if (quick_relocation)
3448 int offset = (setup.scroll_delay ? 3 : 0);
3455 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3457 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3458 x > SBX_Right + MIDPOSX ? SBX_Right :
3461 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3462 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3467 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3468 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3469 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3471 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3472 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3473 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3475 /* don't scroll over playfield boundaries */
3476 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3477 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3479 /* don't scroll over playfield boundaries */
3480 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3481 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3484 RedrawPlayfield(TRUE, 0,0,0,0);
3488 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3489 x > SBX_Right + MIDPOSX ? SBX_Right :
3492 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3493 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3496 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3498 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3501 int fx = FX, fy = FY;
3503 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3504 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3506 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3512 fx += dx * TILEX / 2;
3513 fy += dy * TILEY / 2;
3515 ScrollLevel(dx, dy);
3518 /* scroll in two steps of half tile size to make things smoother */
3519 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3521 Delay(wait_delay_value);
3523 /* scroll second step to align at full tile size */
3525 Delay(wait_delay_value);
3530 Delay(wait_delay_value);
3536 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3538 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3539 boolean no_delay = (tape.warp_forward);
3540 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3541 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3542 int jx = player->jx;
3543 int jy = player->jy;
3545 if (quick_relocation)
3547 int offset = (setup.scroll_delay ? 3 : 0);
3549 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3551 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3552 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3553 player->jx - MIDPOSX);
3555 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3556 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3557 player->jy - MIDPOSY);
3561 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3562 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3563 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3565 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3566 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3567 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3569 /* don't scroll over playfield boundaries */
3570 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3571 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3573 /* don't scroll over playfield boundaries */
3574 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3575 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3578 RedrawPlayfield(TRUE, 0,0,0,0);
3582 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3583 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3584 player->jx - MIDPOSX);
3586 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3587 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3588 player->jy - MIDPOSY);
3590 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3592 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3595 int fx = FX, fy = FY;
3597 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3598 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3600 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3606 fx += dx * TILEX / 2;
3607 fy += dy * TILEY / 2;
3609 ScrollLevel(dx, dy);
3612 /* scroll in two steps of half tile size to make things smoother */
3613 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3615 Delay(wait_delay_value);
3617 /* scroll second step to align at full tile size */
3619 Delay(wait_delay_value);
3624 Delay(wait_delay_value);
3630 void RelocatePlayer(int jx, int jy, int el_player_raw)
3632 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3633 int player_nr = GET_PLAYER_NR(el_player);
3634 struct PlayerInfo *player = &stored_player[player_nr];
3635 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3636 boolean no_delay = (tape.warp_forward);
3637 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3638 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3639 int old_jx = player->jx;
3640 int old_jy = player->jy;
3641 int old_element = Feld[old_jx][old_jy];
3642 int element = Feld[jx][jy];
3643 boolean player_relocated = (old_jx != jx || old_jy != jy);
3645 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3646 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3647 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3648 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3649 int leave_side_horiz = move_dir_horiz;
3650 int leave_side_vert = move_dir_vert;
3651 int enter_side = enter_side_horiz | enter_side_vert;
3652 int leave_side = leave_side_horiz | leave_side_vert;
3654 if (player->GameOver) /* do not reanimate dead player */
3657 if (!player_relocated) /* no need to relocate the player */
3660 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3662 RemoveField(jx, jy); /* temporarily remove newly placed player */
3663 DrawLevelField(jx, jy);
3666 if (player->present)
3668 while (player->MovPos)
3670 ScrollPlayer(player, SCROLL_GO_ON);
3671 ScrollScreen(NULL, SCROLL_GO_ON);
3673 AdvanceFrameAndPlayerCounters(player->index_nr);
3678 Delay(wait_delay_value);
3681 DrawPlayer(player); /* needed here only to cleanup last field */
3682 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3684 player->is_moving = FALSE;
3687 if (IS_CUSTOM_ELEMENT(old_element))
3688 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3690 player->index_bit, leave_side);
3692 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3694 player->index_bit, leave_side);
3696 Feld[jx][jy] = el_player;
3697 InitPlayerField(jx, jy, el_player, TRUE);
3699 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3701 Feld[jx][jy] = element;
3702 InitField(jx, jy, FALSE);
3706 /* only visually relocate centered player */
3708 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3709 level.instant_relocation);
3711 if (player->index_nr == game.centered_player_nr)
3712 DrawRelocatePlayer(player, level.instant_relocation);
3715 if (player == local_player) /* only visually relocate local player */
3716 DrawRelocatePlayer(player, level.instant_relocation);
3719 TestIfPlayerTouchesBadThing(jx, jy);
3720 TestIfPlayerTouchesCustomElement(jx, jy);
3722 if (IS_CUSTOM_ELEMENT(element))
3723 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3724 player->index_bit, enter_side);
3726 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3727 player->index_bit, enter_side);
3730 void Explode(int ex, int ey, int phase, int mode)
3736 /* !!! eliminate this variable !!! */
3737 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3739 if (game.explosions_delayed)
3741 ExplodeField[ex][ey] = mode;
3745 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3747 int center_element = Feld[ex][ey];
3748 int artwork_element, explosion_element; /* set these values later */
3751 /* --- This is only really needed (and now handled) in "Impact()". --- */
3752 /* do not explode moving elements that left the explode field in time */
3753 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3754 center_element == EL_EMPTY &&
3755 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3760 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3761 if (mode == EX_TYPE_NORMAL ||
3762 mode == EX_TYPE_CENTER ||
3763 mode == EX_TYPE_CROSS)
3764 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3767 /* remove things displayed in background while burning dynamite */
3768 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3771 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3773 /* put moving element to center field (and let it explode there) */
3774 center_element = MovingOrBlocked2Element(ex, ey);
3775 RemoveMovingField(ex, ey);
3776 Feld[ex][ey] = center_element;
3779 /* now "center_element" is finally determined -- set related values now */
3780 artwork_element = center_element; /* for custom player artwork */
3781 explosion_element = center_element; /* for custom player artwork */
3783 if (IS_PLAYER(ex, ey))
3785 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3787 artwork_element = stored_player[player_nr].artwork_element;
3789 if (level.use_explosion_element[player_nr])
3791 explosion_element = level.explosion_element[player_nr];
3792 artwork_element = explosion_element;
3797 if (mode == EX_TYPE_NORMAL ||
3798 mode == EX_TYPE_CENTER ||
3799 mode == EX_TYPE_CROSS)
3800 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3804 last_phase = element_info[explosion_element].explosion_delay + 1;
3806 last_phase = element_info[center_element].explosion_delay + 1;
3809 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3811 int xx = x - ex + 1;
3812 int yy = y - ey + 1;
3815 if (!IN_LEV_FIELD(x, y) ||
3816 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3817 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3820 element = Feld[x][y];
3822 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3824 element = MovingOrBlocked2Element(x, y);
3826 if (!IS_EXPLOSION_PROOF(element))
3827 RemoveMovingField(x, y);
3830 /* indestructible elements can only explode in center (but not flames) */
3831 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3832 mode == EX_TYPE_BORDER)) ||
3833 element == EL_FLAMES)
3836 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3837 behaviour, for example when touching a yamyam that explodes to rocks
3838 with active deadly shield, a rock is created under the player !!! */
3839 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3841 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3842 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3843 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3845 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3848 if (IS_ACTIVE_BOMB(element))
3850 /* re-activate things under the bomb like gate or penguin */
3851 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3858 /* save walkable background elements while explosion on same tile */
3859 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3860 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3861 Back[x][y] = element;
3863 /* ignite explodable elements reached by other explosion */
3864 if (element == EL_EXPLOSION)
3865 element = Store2[x][y];
3867 if (AmoebaNr[x][y] &&
3868 (element == EL_AMOEBA_FULL ||
3869 element == EL_BD_AMOEBA ||
3870 element == EL_AMOEBA_GROWING))
3872 AmoebaCnt[AmoebaNr[x][y]]--;
3873 AmoebaCnt2[AmoebaNr[x][y]]--;
3878 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3881 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3883 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3885 switch(StorePlayer[ex][ey])
3888 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3891 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3894 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3898 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3903 if (PLAYERINFO(ex, ey)->use_murphy)
3904 Store[x][y] = EL_EMPTY;
3907 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3908 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3909 else if (ELEM_IS_PLAYER(center_element))
3910 Store[x][y] = EL_EMPTY;
3911 else if (center_element == EL_YAMYAM)
3912 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3913 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3914 Store[x][y] = element_info[center_element].content.e[xx][yy];
3916 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3917 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3918 otherwise) -- FIX THIS !!! */
3919 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3920 Store[x][y] = element_info[element].content.e[1][1];
3922 else if (!CAN_EXPLODE(element))
3923 Store[x][y] = element_info[element].content.e[1][1];
3926 Store[x][y] = EL_EMPTY;
3928 else if (center_element == EL_MOLE)
3929 Store[x][y] = EL_EMERALD_RED;
3930 else if (center_element == EL_PENGUIN)
3931 Store[x][y] = EL_EMERALD_PURPLE;
3932 else if (center_element == EL_BUG)
3933 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3934 else if (center_element == EL_BD_BUTTERFLY)
3935 Store[x][y] = EL_BD_DIAMOND;
3936 else if (center_element == EL_SP_ELECTRON)
3937 Store[x][y] = EL_SP_INFOTRON;
3938 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3939 Store[x][y] = level.amoeba_content;
3940 else if (center_element == EL_YAMYAM)
3941 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3942 else if (IS_CUSTOM_ELEMENT(center_element) &&
3943 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3944 Store[x][y] = element_info[center_element].content.e[xx][yy];
3945 else if (element == EL_WALL_EMERALD)
3946 Store[x][y] = EL_EMERALD;
3947 else if (element == EL_WALL_DIAMOND)
3948 Store[x][y] = EL_DIAMOND;
3949 else if (element == EL_WALL_BD_DIAMOND)
3950 Store[x][y] = EL_BD_DIAMOND;
3951 else if (element == EL_WALL_EMERALD_YELLOW)
3952 Store[x][y] = EL_EMERALD_YELLOW;
3953 else if (element == EL_WALL_EMERALD_RED)
3954 Store[x][y] = EL_EMERALD_RED;
3955 else if (element == EL_WALL_EMERALD_PURPLE)
3956 Store[x][y] = EL_EMERALD_PURPLE;
3957 else if (element == EL_WALL_PEARL)
3958 Store[x][y] = EL_PEARL;
3959 else if (element == EL_WALL_CRYSTAL)
3960 Store[x][y] = EL_CRYSTAL;
3961 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3962 Store[x][y] = element_info[element].content.e[1][1];
3964 Store[x][y] = EL_EMPTY;
3967 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3968 center_element == EL_AMOEBA_TO_DIAMOND)
3969 Store2[x][y] = element;
3971 Feld[x][y] = EL_EXPLOSION;
3972 GfxElement[x][y] = artwork_element;
3975 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3976 x, y, artwork_element, EL_NAME(artwork_element));
3979 ExplodePhase[x][y] = 1;
3980 ExplodeDelay[x][y] = last_phase;
3985 if (center_element == EL_YAMYAM)
3986 game.yamyam_content_nr =
3987 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3999 GfxFrame[x][y] = 0; /* restart explosion animation */
4001 last_phase = ExplodeDelay[x][y];
4003 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4007 /* activate this even in non-DEBUG version until cause for crash in
4008 getGraphicAnimationFrame() (see below) is found and eliminated */
4014 /* this can happen if the player leaves an explosion just in time */
4015 if (GfxElement[x][y] == EL_UNDEFINED)
4016 GfxElement[x][y] = EL_EMPTY;
4018 if (GfxElement[x][y] == EL_UNDEFINED)
4021 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4022 printf("Explode(): This should never happen!\n");
4025 GfxElement[x][y] = EL_EMPTY;
4031 border_element = Store2[x][y];
4032 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4033 border_element = StorePlayer[x][y];
4035 if (phase == element_info[border_element].ignition_delay ||
4036 phase == last_phase)
4038 boolean border_explosion = FALSE;
4040 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4041 !PLAYER_EXPLOSION_PROTECTED(x, y))
4043 KillPlayerUnlessExplosionProtected(x, y);
4044 border_explosion = TRUE;
4046 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4048 Feld[x][y] = Store2[x][y];
4051 border_explosion = TRUE;
4053 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4055 AmoebeUmwandeln(x, y);
4057 border_explosion = TRUE;
4060 /* if an element just explodes due to another explosion (chain-reaction),
4061 do not immediately end the new explosion when it was the last frame of
4062 the explosion (as it would be done in the following "if"-statement!) */
4063 if (border_explosion && phase == last_phase)
4067 if (phase == last_phase)
4071 element = Feld[x][y] = Store[x][y];
4072 Store[x][y] = Store2[x][y] = 0;
4073 GfxElement[x][y] = EL_UNDEFINED;
4075 /* player can escape from explosions and might therefore be still alive */
4076 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4077 element <= EL_PLAYER_IS_EXPLODING_4)
4079 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4080 int explosion_element = EL_PLAYER_1 + player_nr;
4081 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4082 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4084 if (level.use_explosion_element[player_nr])
4085 explosion_element = level.explosion_element[player_nr];
4087 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4088 element_info[explosion_element].content.e[xx][yy]);
4091 /* restore probably existing indestructible background element */
4092 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4093 element = Feld[x][y] = Back[x][y];
4096 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4097 GfxDir[x][y] = MV_NONE;
4098 ChangeDelay[x][y] = 0;
4099 ChangePage[x][y] = -1;
4101 #if USE_NEW_CUSTOM_VALUE
4102 CustomValue[x][y] = 0;
4105 InitField_WithBug2(x, y, FALSE);
4107 DrawLevelField(x, y);
4109 TestIfElementTouchesCustomElement(x, y);
4111 if (GFX_CRUMBLED(element))
4112 DrawLevelFieldCrumbledSandNeighbours(x, y);
4114 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4115 StorePlayer[x][y] = 0;
4117 if (ELEM_IS_PLAYER(element))
4118 RelocatePlayer(x, y, element);
4120 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4122 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4123 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4126 DrawLevelFieldCrumbledSand(x, y);
4128 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4130 DrawLevelElement(x, y, Back[x][y]);
4131 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4133 else if (IS_WALKABLE_UNDER(Back[x][y]))
4135 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4136 DrawLevelElementThruMask(x, y, Back[x][y]);
4138 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4139 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4143 void DynaExplode(int ex, int ey)
4146 int dynabomb_element = Feld[ex][ey];
4147 int dynabomb_size = 1;
4148 boolean dynabomb_xl = FALSE;
4149 struct PlayerInfo *player;
4150 static int xy[4][2] =
4158 if (IS_ACTIVE_BOMB(dynabomb_element))
4160 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4161 dynabomb_size = player->dynabomb_size;
4162 dynabomb_xl = player->dynabomb_xl;
4163 player->dynabombs_left++;
4166 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4168 for (i = 0; i < NUM_DIRECTIONS; i++)
4170 for (j = 1; j <= dynabomb_size; j++)
4172 int x = ex + j * xy[i][0];
4173 int y = ey + j * xy[i][1];
4176 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4179 element = Feld[x][y];
4181 /* do not restart explosions of fields with active bombs */
4182 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4185 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4187 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4188 !IS_DIGGABLE(element) && !dynabomb_xl)
4194 void Bang(int x, int y)
4196 int element = MovingOrBlocked2Element(x, y);
4197 int explosion_type = EX_TYPE_NORMAL;
4199 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4201 struct PlayerInfo *player = PLAYERINFO(x, y);
4203 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4204 player->element_nr);
4206 if (level.use_explosion_element[player->index_nr])
4208 int explosion_element = level.explosion_element[player->index_nr];
4210 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4211 explosion_type = EX_TYPE_CROSS;
4212 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4213 explosion_type = EX_TYPE_CENTER;
4221 case EL_BD_BUTTERFLY:
4224 case EL_DARK_YAMYAM:
4228 RaiseScoreElement(element);
4231 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4232 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4233 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4234 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4235 case EL_DYNABOMB_INCREASE_NUMBER:
4236 case EL_DYNABOMB_INCREASE_SIZE:
4237 case EL_DYNABOMB_INCREASE_POWER:
4238 explosion_type = EX_TYPE_DYNA;
4243 case EL_LAMP_ACTIVE:
4244 case EL_AMOEBA_TO_DIAMOND:
4245 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4246 explosion_type = EX_TYPE_CENTER;
4250 if (element_info[element].explosion_type == EXPLODES_CROSS)
4251 explosion_type = EX_TYPE_CROSS;
4252 else if (element_info[element].explosion_type == EXPLODES_1X1)
4253 explosion_type = EX_TYPE_CENTER;
4257 if (explosion_type == EX_TYPE_DYNA)
4260 Explode(x, y, EX_PHASE_START, explosion_type);
4262 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4265 void SplashAcid(int x, int y)
4267 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4268 (!IN_LEV_FIELD(x - 1, y - 2) ||
4269 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4270 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4272 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4273 (!IN_LEV_FIELD(x + 1, y - 2) ||
4274 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4275 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4277 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4280 static void InitBeltMovement()
4282 static int belt_base_element[4] =
4284 EL_CONVEYOR_BELT_1_LEFT,
4285 EL_CONVEYOR_BELT_2_LEFT,
4286 EL_CONVEYOR_BELT_3_LEFT,
4287 EL_CONVEYOR_BELT_4_LEFT
4289 static int belt_base_active_element[4] =
4291 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4292 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4293 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4294 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4299 /* set frame order for belt animation graphic according to belt direction */
4300 for (i = 0; i < NUM_BELTS; i++)
4304 for (j = 0; j < NUM_BELT_PARTS; j++)
4306 int element = belt_base_active_element[belt_nr] + j;
4307 int graphic = el2img(element);
4309 if (game.belt_dir[i] == MV_LEFT)
4310 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4312 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4317 SCAN_PLAYFIELD(x, y)
4319 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4322 int element = Feld[x][y];
4324 for (i = 0; i < NUM_BELTS; i++)
4326 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4328 int e_belt_nr = getBeltNrFromBeltElement(element);
4331 if (e_belt_nr == belt_nr)
4333 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4335 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4342 static void ToggleBeltSwitch(int x, int y)
4344 static int belt_base_element[4] =
4346 EL_CONVEYOR_BELT_1_LEFT,
4347 EL_CONVEYOR_BELT_2_LEFT,
4348 EL_CONVEYOR_BELT_3_LEFT,
4349 EL_CONVEYOR_BELT_4_LEFT
4351 static int belt_base_active_element[4] =
4353 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4354 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4355 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4356 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4358 static int belt_base_switch_element[4] =
4360 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4361 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4362 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4363 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4365 static int belt_move_dir[4] =
4373 int element = Feld[x][y];
4374 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4375 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4376 int belt_dir = belt_move_dir[belt_dir_nr];
4379 if (!IS_BELT_SWITCH(element))
4382 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4383 game.belt_dir[belt_nr] = belt_dir;
4385 if (belt_dir_nr == 3)
4388 /* set frame order for belt animation graphic according to belt direction */
4389 for (i = 0; i < NUM_BELT_PARTS; i++)
4391 int element = belt_base_active_element[belt_nr] + i;
4392 int graphic = el2img(element);
4394 if (belt_dir == MV_LEFT)
4395 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4397 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4401 SCAN_PLAYFIELD(xx, yy)
4403 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4406 int element = Feld[xx][yy];
4408 if (IS_BELT_SWITCH(element))
4410 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4412 if (e_belt_nr == belt_nr)
4414 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4415 DrawLevelField(xx, yy);
4418 else if (IS_BELT(element) && belt_dir != MV_NONE)
4420 int e_belt_nr = getBeltNrFromBeltElement(element);
4422 if (e_belt_nr == belt_nr)
4424 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4426 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4427 DrawLevelField(xx, yy);
4430 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4432 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4434 if (e_belt_nr == belt_nr)
4436 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4438 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4439 DrawLevelField(xx, yy);
4445 static void ToggleSwitchgateSwitch(int x, int y)
4449 game.switchgate_pos = !game.switchgate_pos;
4452 SCAN_PLAYFIELD(xx, yy)
4454 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4457 int element = Feld[xx][yy];
4459 #if !USE_BOTH_SWITCHGATE_SWITCHES
4460 if (element == EL_SWITCHGATE_SWITCH_UP ||
4461 element == EL_SWITCHGATE_SWITCH_DOWN)
4463 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4464 DrawLevelField(xx, yy);
4467 if (element == EL_SWITCHGATE_SWITCH_UP)
4469 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4470 DrawLevelField(xx, yy);
4472 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4474 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4475 DrawLevelField(xx, yy);
4478 else if (element == EL_SWITCHGATE_OPEN ||
4479 element == EL_SWITCHGATE_OPENING)
4481 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4483 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4485 else if (element == EL_SWITCHGATE_CLOSED ||
4486 element == EL_SWITCHGATE_CLOSING)
4488 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4490 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4495 static int getInvisibleActiveFromInvisibleElement(int element)
4497 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4498 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4499 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4503 static int getInvisibleFromInvisibleActiveElement(int element)
4505 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4506 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4507 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4511 static void RedrawAllLightSwitchesAndInvisibleElements()
4516 SCAN_PLAYFIELD(x, y)
4518 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4521 int element = Feld[x][y];
4523 if (element == EL_LIGHT_SWITCH &&
4524 game.light_time_left > 0)
4526 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4527 DrawLevelField(x, y);
4529 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4530 game.light_time_left == 0)
4532 Feld[x][y] = EL_LIGHT_SWITCH;
4533 DrawLevelField(x, y);
4535 else if (element == EL_EMC_DRIPPER &&
4536 game.light_time_left > 0)
4538 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4539 DrawLevelField(x, y);
4541 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4542 game.light_time_left == 0)
4544 Feld[x][y] = EL_EMC_DRIPPER;
4545 DrawLevelField(x, y);
4547 else if (element == EL_INVISIBLE_STEELWALL ||
4548 element == EL_INVISIBLE_WALL ||
4549 element == EL_INVISIBLE_SAND)
4551 if (game.light_time_left > 0)
4552 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4554 DrawLevelField(x, y);
4556 /* uncrumble neighbour fields, if needed */
4557 if (element == EL_INVISIBLE_SAND)
4558 DrawLevelFieldCrumbledSandNeighbours(x, y);
4560 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4561 element == EL_INVISIBLE_WALL_ACTIVE ||
4562 element == EL_INVISIBLE_SAND_ACTIVE)
4564 if (game.light_time_left == 0)
4565 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4567 DrawLevelField(x, y);
4569 /* re-crumble neighbour fields, if needed */
4570 if (element == EL_INVISIBLE_SAND)
4571 DrawLevelFieldCrumbledSandNeighbours(x, y);
4576 static void RedrawAllInvisibleElementsForLenses()
4581 SCAN_PLAYFIELD(x, y)
4583 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4586 int element = Feld[x][y];
4588 if (element == EL_EMC_DRIPPER &&
4589 game.lenses_time_left > 0)
4591 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4592 DrawLevelField(x, y);
4594 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4595 game.lenses_time_left == 0)
4597 Feld[x][y] = EL_EMC_DRIPPER;
4598 DrawLevelField(x, y);
4600 else if (element == EL_INVISIBLE_STEELWALL ||
4601 element == EL_INVISIBLE_WALL ||
4602 element == EL_INVISIBLE_SAND)
4604 if (game.lenses_time_left > 0)
4605 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4607 DrawLevelField(x, y);
4609 /* uncrumble neighbour fields, if needed */
4610 if (element == EL_INVISIBLE_SAND)
4611 DrawLevelFieldCrumbledSandNeighbours(x, y);
4613 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4614 element == EL_INVISIBLE_WALL_ACTIVE ||
4615 element == EL_INVISIBLE_SAND_ACTIVE)
4617 if (game.lenses_time_left == 0)
4618 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4620 DrawLevelField(x, y);
4622 /* re-crumble neighbour fields, if needed */
4623 if (element == EL_INVISIBLE_SAND)
4624 DrawLevelFieldCrumbledSandNeighbours(x, y);
4629 static void RedrawAllInvisibleElementsForMagnifier()
4634 SCAN_PLAYFIELD(x, y)
4636 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4639 int element = Feld[x][y];
4641 if (element == EL_EMC_FAKE_GRASS &&
4642 game.magnify_time_left > 0)
4644 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4645 DrawLevelField(x, y);
4647 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4648 game.magnify_time_left == 0)
4650 Feld[x][y] = EL_EMC_FAKE_GRASS;
4651 DrawLevelField(x, y);
4653 else if (IS_GATE_GRAY(element) &&
4654 game.magnify_time_left > 0)
4656 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4657 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4658 IS_EM_GATE_GRAY(element) ?
4659 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4660 IS_EMC_GATE_GRAY(element) ?
4661 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4663 DrawLevelField(x, y);
4665 else if (IS_GATE_GRAY_ACTIVE(element) &&
4666 game.magnify_time_left == 0)
4668 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4669 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4670 IS_EM_GATE_GRAY_ACTIVE(element) ?
4671 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4672 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4673 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4675 DrawLevelField(x, y);
4680 static void ToggleLightSwitch(int x, int y)
4682 int element = Feld[x][y];
4684 game.light_time_left =
4685 (element == EL_LIGHT_SWITCH ?
4686 level.time_light * FRAMES_PER_SECOND : 0);
4688 RedrawAllLightSwitchesAndInvisibleElements();
4691 static void ActivateTimegateSwitch(int x, int y)
4695 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4698 SCAN_PLAYFIELD(xx, yy)
4700 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4703 int element = Feld[xx][yy];
4705 if (element == EL_TIMEGATE_CLOSED ||
4706 element == EL_TIMEGATE_CLOSING)
4708 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4709 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4713 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4715 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4716 DrawLevelField(xx, yy);
4722 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4725 void Impact(int x, int y)
4727 boolean last_line = (y == lev_fieldy - 1);
4728 boolean object_hit = FALSE;
4729 boolean impact = (last_line || object_hit);
4730 int element = Feld[x][y];
4731 int smashed = EL_STEELWALL;
4733 if (!last_line) /* check if element below was hit */
4735 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4738 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4739 MovDir[x][y + 1] != MV_DOWN ||
4740 MovPos[x][y + 1] <= TILEY / 2));
4742 /* do not smash moving elements that left the smashed field in time */
4743 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4744 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4747 #if USE_QUICKSAND_IMPACT_BUGFIX
4748 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4750 RemoveMovingField(x, y + 1);
4751 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4752 Feld[x][y + 2] = EL_ROCK;
4753 DrawLevelField(x, y + 2);
4760 smashed = MovingOrBlocked2Element(x, y + 1);
4762 impact = (last_line || object_hit);
4765 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4767 SplashAcid(x, y + 1);
4771 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4772 /* only reset graphic animation if graphic really changes after impact */
4774 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4776 ResetGfxAnimation(x, y);
4777 DrawLevelField(x, y);
4780 if (impact && CAN_EXPLODE_IMPACT(element))
4785 else if (impact && element == EL_PEARL)
4787 ResetGfxAnimation(x, y);
4789 Feld[x][y] = EL_PEARL_BREAKING;
4790 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4793 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4795 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4800 if (impact && element == EL_AMOEBA_DROP)
4802 if (object_hit && IS_PLAYER(x, y + 1))
4803 KillPlayerUnlessEnemyProtected(x, y + 1);
4804 else if (object_hit && smashed == EL_PENGUIN)
4808 Feld[x][y] = EL_AMOEBA_GROWING;
4809 Store[x][y] = EL_AMOEBA_WET;
4811 ResetRandomAnimationValue(x, y);
4816 if (object_hit) /* check which object was hit */
4818 if (CAN_PASS_MAGIC_WALL(element) &&
4819 (smashed == EL_MAGIC_WALL ||
4820 smashed == EL_BD_MAGIC_WALL))
4823 int activated_magic_wall =
4824 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4825 EL_BD_MAGIC_WALL_ACTIVE);
4827 /* activate magic wall / mill */
4829 SCAN_PLAYFIELD(xx, yy)
4831 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4833 if (Feld[xx][yy] == smashed)
4834 Feld[xx][yy] = activated_magic_wall;
4836 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4837 game.magic_wall_active = TRUE;
4839 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4840 SND_MAGIC_WALL_ACTIVATING :
4841 SND_BD_MAGIC_WALL_ACTIVATING));
4844 if (IS_PLAYER(x, y + 1))
4846 if (CAN_SMASH_PLAYER(element))
4848 KillPlayerUnlessEnemyProtected(x, y + 1);
4852 else if (smashed == EL_PENGUIN)
4854 if (CAN_SMASH_PLAYER(element))
4860 else if (element == EL_BD_DIAMOND)
4862 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4868 else if (((element == EL_SP_INFOTRON ||
4869 element == EL_SP_ZONK) &&
4870 (smashed == EL_SP_SNIKSNAK ||
4871 smashed == EL_SP_ELECTRON ||
4872 smashed == EL_SP_DISK_ORANGE)) ||
4873 (element == EL_SP_INFOTRON &&
4874 smashed == EL_SP_DISK_YELLOW))
4879 else if (CAN_SMASH_EVERYTHING(element))
4881 if (IS_CLASSIC_ENEMY(smashed) ||
4882 CAN_EXPLODE_SMASHED(smashed))
4887 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4889 if (smashed == EL_LAMP ||
4890 smashed == EL_LAMP_ACTIVE)
4895 else if (smashed == EL_NUT)
4897 Feld[x][y + 1] = EL_NUT_BREAKING;
4898 PlayLevelSound(x, y, SND_NUT_BREAKING);
4899 RaiseScoreElement(EL_NUT);
4902 else if (smashed == EL_PEARL)
4904 ResetGfxAnimation(x, y);
4906 Feld[x][y + 1] = EL_PEARL_BREAKING;
4907 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4910 else if (smashed == EL_DIAMOND)
4912 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4913 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4916 else if (IS_BELT_SWITCH(smashed))
4918 ToggleBeltSwitch(x, y + 1);
4920 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4921 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4923 ToggleSwitchgateSwitch(x, y + 1);
4925 else if (smashed == EL_LIGHT_SWITCH ||
4926 smashed == EL_LIGHT_SWITCH_ACTIVE)
4928 ToggleLightSwitch(x, y + 1);
4933 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4936 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4938 CheckElementChangeBySide(x, y + 1, smashed, element,
4939 CE_SWITCHED, CH_SIDE_TOP);
4940 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4946 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4951 /* play sound of magic wall / mill */
4953 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4954 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4956 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4957 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4958 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4959 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4964 /* play sound of object that hits the ground */
4965 if (last_line || object_hit)
4966 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4969 inline static void TurnRoundExt(int x, int y)
4981 { 0, 0 }, { 0, 0 }, { 0, 0 },
4986 int left, right, back;
4990 { MV_DOWN, MV_UP, MV_RIGHT },
4991 { MV_UP, MV_DOWN, MV_LEFT },
4993 { MV_LEFT, MV_RIGHT, MV_DOWN },
4997 { MV_RIGHT, MV_LEFT, MV_UP }
5000 int element = Feld[x][y];
5001 int move_pattern = element_info[element].move_pattern;
5003 int old_move_dir = MovDir[x][y];
5004 int left_dir = turn[old_move_dir].left;
5005 int right_dir = turn[old_move_dir].right;
5006 int back_dir = turn[old_move_dir].back;
5008 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5009 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5010 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5011 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5013 int left_x = x + left_dx, left_y = y + left_dy;
5014 int right_x = x + right_dx, right_y = y + right_dy;
5015 int move_x = x + move_dx, move_y = y + move_dy;
5019 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5021 TestIfBadThingTouchesOtherBadThing(x, y);
5023 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5024 MovDir[x][y] = right_dir;
5025 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5026 MovDir[x][y] = left_dir;
5028 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5030 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5033 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5035 TestIfBadThingTouchesOtherBadThing(x, y);
5037 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5038 MovDir[x][y] = left_dir;
5039 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5040 MovDir[x][y] = right_dir;
5042 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5044 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5047 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5049 TestIfBadThingTouchesOtherBadThing(x, y);
5051 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5052 MovDir[x][y] = left_dir;
5053 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5054 MovDir[x][y] = right_dir;
5056 if (MovDir[x][y] != old_move_dir)
5059 else if (element == EL_YAMYAM)
5061 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5062 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5064 if (can_turn_left && can_turn_right)
5065 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5066 else if (can_turn_left)
5067 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5068 else if (can_turn_right)
5069 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5071 MovDir[x][y] = back_dir;
5073 MovDelay[x][y] = 16 + 16 * RND(3);
5075 else if (element == EL_DARK_YAMYAM)
5077 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5079 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5082 if (can_turn_left && can_turn_right)
5083 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5084 else if (can_turn_left)
5085 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5086 else if (can_turn_right)
5087 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5089 MovDir[x][y] = back_dir;
5091 MovDelay[x][y] = 16 + 16 * RND(3);
5093 else if (element == EL_PACMAN)
5095 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5096 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5098 if (can_turn_left && can_turn_right)
5099 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5100 else if (can_turn_left)
5101 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5102 else if (can_turn_right)
5103 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5105 MovDir[x][y] = back_dir;
5107 MovDelay[x][y] = 6 + RND(40);
5109 else if (element == EL_PIG)
5111 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5112 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5113 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5114 boolean should_turn_left, should_turn_right, should_move_on;
5116 int rnd = RND(rnd_value);
5118 should_turn_left = (can_turn_left &&
5120 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5121 y + back_dy + left_dy)));
5122 should_turn_right = (can_turn_right &&
5124 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5125 y + back_dy + right_dy)));
5126 should_move_on = (can_move_on &&
5129 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5130 y + move_dy + left_dy) ||
5131 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5132 y + move_dy + right_dy)));
5134 if (should_turn_left || should_turn_right || should_move_on)
5136 if (should_turn_left && should_turn_right && should_move_on)
5137 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5138 rnd < 2 * rnd_value / 3 ? right_dir :
5140 else if (should_turn_left && should_turn_right)
5141 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5142 else if (should_turn_left && should_move_on)
5143 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5144 else if (should_turn_right && should_move_on)
5145 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5146 else if (should_turn_left)
5147 MovDir[x][y] = left_dir;
5148 else if (should_turn_right)
5149 MovDir[x][y] = right_dir;
5150 else if (should_move_on)
5151 MovDir[x][y] = old_move_dir;
5153 else if (can_move_on && rnd > rnd_value / 8)
5154 MovDir[x][y] = old_move_dir;
5155 else if (can_turn_left && can_turn_right)
5156 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5157 else if (can_turn_left && rnd > rnd_value / 8)
5158 MovDir[x][y] = left_dir;
5159 else if (can_turn_right && rnd > rnd_value/8)
5160 MovDir[x][y] = right_dir;
5162 MovDir[x][y] = back_dir;
5164 xx = x + move_xy[MovDir[x][y]].dx;
5165 yy = y + move_xy[MovDir[x][y]].dy;
5167 if (!IN_LEV_FIELD(xx, yy) ||
5168 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5169 MovDir[x][y] = old_move_dir;
5173 else if (element == EL_DRAGON)
5175 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5176 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5177 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5179 int rnd = RND(rnd_value);
5181 if (can_move_on && rnd > rnd_value / 8)
5182 MovDir[x][y] = old_move_dir;
5183 else if (can_turn_left && can_turn_right)
5184 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5185 else if (can_turn_left && rnd > rnd_value / 8)
5186 MovDir[x][y] = left_dir;
5187 else if (can_turn_right && rnd > rnd_value / 8)
5188 MovDir[x][y] = right_dir;
5190 MovDir[x][y] = back_dir;
5192 xx = x + move_xy[MovDir[x][y]].dx;
5193 yy = y + move_xy[MovDir[x][y]].dy;
5195 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5196 MovDir[x][y] = old_move_dir;
5200 else if (element == EL_MOLE)
5202 boolean can_move_on =
5203 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5204 IS_AMOEBOID(Feld[move_x][move_y]) ||
5205 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5208 boolean can_turn_left =
5209 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5210 IS_AMOEBOID(Feld[left_x][left_y])));
5212 boolean can_turn_right =
5213 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5214 IS_AMOEBOID(Feld[right_x][right_y])));
5216 if (can_turn_left && can_turn_right)
5217 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5218 else if (can_turn_left)
5219 MovDir[x][y] = left_dir;
5221 MovDir[x][y] = right_dir;
5224 if (MovDir[x][y] != old_move_dir)
5227 else if (element == EL_BALLOON)
5229 MovDir[x][y] = game.wind_direction;
5232 else if (element == EL_SPRING)
5234 #if USE_NEW_SPRING_BUMPER
5235 if (MovDir[x][y] & MV_HORIZONTAL)
5237 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5238 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5240 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5241 ResetGfxAnimation(move_x, move_y);
5242 DrawLevelField(move_x, move_y);
5244 MovDir[x][y] = back_dir;
5246 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5247 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5248 MovDir[x][y] = MV_NONE;
5251 if (MovDir[x][y] & MV_HORIZONTAL &&
5252 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5253 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5254 MovDir[x][y] = MV_NONE;
5259 else if (element == EL_ROBOT ||
5260 element == EL_SATELLITE ||
5261 element == EL_PENGUIN ||
5262 element == EL_EMC_ANDROID)
5264 int attr_x = -1, attr_y = -1;
5275 for (i = 0; i < MAX_PLAYERS; i++)
5277 struct PlayerInfo *player = &stored_player[i];
5278 int jx = player->jx, jy = player->jy;
5280 if (!player->active)
5284 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5292 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5293 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5294 game.engine_version < VERSION_IDENT(3,1,0,0)))
5300 if (element == EL_PENGUIN)
5303 static int xy[4][2] =
5311 for (i = 0; i < NUM_DIRECTIONS; i++)
5313 int ex = x + xy[i][0];
5314 int ey = y + xy[i][1];
5316 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5325 MovDir[x][y] = MV_NONE;
5327 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5328 else if (attr_x > x)
5329 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5331 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5332 else if (attr_y > y)
5333 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5335 if (element == EL_ROBOT)
5339 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5340 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5341 Moving2Blocked(x, y, &newx, &newy);
5343 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5344 MovDelay[x][y] = 8 + 8 * !RND(3);
5346 MovDelay[x][y] = 16;
5348 else if (element == EL_PENGUIN)
5354 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5356 boolean first_horiz = RND(2);
5357 int new_move_dir = MovDir[x][y];
5360 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5361 Moving2Blocked(x, y, &newx, &newy);
5363 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5367 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5368 Moving2Blocked(x, y, &newx, &newy);
5370 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5373 MovDir[x][y] = old_move_dir;
5377 else if (element == EL_SATELLITE)
5383 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5385 boolean first_horiz = RND(2);
5386 int new_move_dir = MovDir[x][y];
5389 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5390 Moving2Blocked(x, y, &newx, &newy);
5392 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5396 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5397 Moving2Blocked(x, y, &newx, &newy);
5399 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5402 MovDir[x][y] = old_move_dir;
5406 else if (element == EL_EMC_ANDROID)
5408 static int check_pos[16] =
5410 -1, /* 0 => (invalid) */
5411 7, /* 1 => MV_LEFT */
5412 3, /* 2 => MV_RIGHT */
5413 -1, /* 3 => (invalid) */
5415 0, /* 5 => MV_LEFT | MV_UP */
5416 2, /* 6 => MV_RIGHT | MV_UP */
5417 -1, /* 7 => (invalid) */
5418 5, /* 8 => MV_DOWN */
5419 6, /* 9 => MV_LEFT | MV_DOWN */
5420 4, /* 10 => MV_RIGHT | MV_DOWN */
5421 -1, /* 11 => (invalid) */
5422 -1, /* 12 => (invalid) */
5423 -1, /* 13 => (invalid) */
5424 -1, /* 14 => (invalid) */
5425 -1, /* 15 => (invalid) */
5433 { -1, -1, MV_LEFT | MV_UP },
5435 { +1, -1, MV_RIGHT | MV_UP },
5436 { +1, 0, MV_RIGHT },
5437 { +1, +1, MV_RIGHT | MV_DOWN },
5439 { -1, +1, MV_LEFT | MV_DOWN },
5442 int start_pos, check_order;
5443 boolean can_clone = FALSE;
5446 /* check if there is any free field around current position */
5447 for (i = 0; i < 8; i++)
5449 int newx = x + check_xy[i].dx;
5450 int newy = y + check_xy[i].dy;
5452 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5460 if (can_clone) /* randomly find an element to clone */
5464 start_pos = check_pos[RND(8)];
5465 check_order = (RND(2) ? -1 : +1);
5467 for (i = 0; i < 8; i++)
5469 int pos_raw = start_pos + i * check_order;
5470 int pos = (pos_raw + 8) % 8;
5471 int newx = x + check_xy[pos].dx;
5472 int newy = y + check_xy[pos].dy;
5474 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5476 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5477 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5479 Store[x][y] = Feld[newx][newy];
5488 if (can_clone) /* randomly find a direction to move */
5492 start_pos = check_pos[RND(8)];
5493 check_order = (RND(2) ? -1 : +1);
5495 for (i = 0; i < 8; i++)
5497 int pos_raw = start_pos + i * check_order;
5498 int pos = (pos_raw + 8) % 8;
5499 int newx = x + check_xy[pos].dx;
5500 int newy = y + check_xy[pos].dy;
5501 int new_move_dir = check_xy[pos].dir;
5503 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5505 MovDir[x][y] = new_move_dir;
5506 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5515 if (can_clone) /* cloning and moving successful */
5518 /* cannot clone -- try to move towards player */
5520 start_pos = check_pos[MovDir[x][y] & 0x0f];
5521 check_order = (RND(2) ? -1 : +1);
5523 for (i = 0; i < 3; i++)
5525 /* first check start_pos, then previous/next or (next/previous) pos */
5526 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5527 int pos = (pos_raw + 8) % 8;
5528 int newx = x + check_xy[pos].dx;
5529 int newy = y + check_xy[pos].dy;
5530 int new_move_dir = check_xy[pos].dir;
5532 if (IS_PLAYER(newx, newy))
5535 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5537 MovDir[x][y] = new_move_dir;
5538 MovDelay[x][y] = level.android_move_time * 8 + 1;
5545 else if (move_pattern == MV_TURNING_LEFT ||
5546 move_pattern == MV_TURNING_RIGHT ||
5547 move_pattern == MV_TURNING_LEFT_RIGHT ||
5548 move_pattern == MV_TURNING_RIGHT_LEFT ||
5549 move_pattern == MV_TURNING_RANDOM ||
5550 move_pattern == MV_ALL_DIRECTIONS)
5552 boolean can_turn_left =
5553 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5554 boolean can_turn_right =
5555 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5557 if (element_info[element].move_stepsize == 0) /* "not moving" */
5560 if (move_pattern == MV_TURNING_LEFT)
5561 MovDir[x][y] = left_dir;
5562 else if (move_pattern == MV_TURNING_RIGHT)
5563 MovDir[x][y] = right_dir;
5564 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5565 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5566 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5567 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5568 else if (move_pattern == MV_TURNING_RANDOM)
5569 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5570 can_turn_right && !can_turn_left ? right_dir :
5571 RND(2) ? left_dir : right_dir);
5572 else if (can_turn_left && can_turn_right)
5573 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5574 else if (can_turn_left)
5575 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5576 else if (can_turn_right)
5577 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5579 MovDir[x][y] = back_dir;
5581 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5583 else if (move_pattern == MV_HORIZONTAL ||
5584 move_pattern == MV_VERTICAL)
5586 if (move_pattern & old_move_dir)
5587 MovDir[x][y] = back_dir;
5588 else if (move_pattern == MV_HORIZONTAL)
5589 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5590 else if (move_pattern == MV_VERTICAL)
5591 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5593 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5595 else if (move_pattern & MV_ANY_DIRECTION)
5597 MovDir[x][y] = move_pattern;
5598 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5600 else if (move_pattern & MV_WIND_DIRECTION)
5602 MovDir[x][y] = game.wind_direction;
5603 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5605 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5607 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5608 MovDir[x][y] = left_dir;
5609 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5610 MovDir[x][y] = right_dir;
5612 if (MovDir[x][y] != old_move_dir)
5613 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5615 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5617 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5618 MovDir[x][y] = right_dir;
5619 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5620 MovDir[x][y] = left_dir;
5622 if (MovDir[x][y] != old_move_dir)
5623 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5625 else if (move_pattern == MV_TOWARDS_PLAYER ||
5626 move_pattern == MV_AWAY_FROM_PLAYER)
5628 int attr_x = -1, attr_y = -1;
5630 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5641 for (i = 0; i < MAX_PLAYERS; i++)
5643 struct PlayerInfo *player = &stored_player[i];
5644 int jx = player->jx, jy = player->jy;
5646 if (!player->active)
5650 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5658 MovDir[x][y] = MV_NONE;
5660 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5661 else if (attr_x > x)
5662 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5664 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5665 else if (attr_y > y)
5666 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5668 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5670 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5672 boolean first_horiz = RND(2);
5673 int new_move_dir = MovDir[x][y];
5675 if (element_info[element].move_stepsize == 0) /* "not moving" */
5677 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5678 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5684 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5685 Moving2Blocked(x, y, &newx, &newy);
5687 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5691 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5692 Moving2Blocked(x, y, &newx, &newy);
5694 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5697 MovDir[x][y] = old_move_dir;
5700 else if (move_pattern == MV_WHEN_PUSHED ||
5701 move_pattern == MV_WHEN_DROPPED)
5703 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5704 MovDir[x][y] = MV_NONE;
5708 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5710 static int test_xy[7][2] =
5720 static int test_dir[7] =
5730 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5731 int move_preference = -1000000; /* start with very low preference */
5732 int new_move_dir = MV_NONE;
5733 int start_test = RND(4);
5736 for (i = 0; i < NUM_DIRECTIONS; i++)
5738 int move_dir = test_dir[start_test + i];
5739 int move_dir_preference;
5741 xx = x + test_xy[start_test + i][0];
5742 yy = y + test_xy[start_test + i][1];
5744 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5745 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5747 new_move_dir = move_dir;
5752 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5755 move_dir_preference = -1 * RunnerVisit[xx][yy];
5756 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5757 move_dir_preference = PlayerVisit[xx][yy];
5759 if (move_dir_preference > move_preference)
5761 /* prefer field that has not been visited for the longest time */
5762 move_preference = move_dir_preference;
5763 new_move_dir = move_dir;
5765 else if (move_dir_preference == move_preference &&
5766 move_dir == old_move_dir)
5768 /* prefer last direction when all directions are preferred equally */
5769 move_preference = move_dir_preference;
5770 new_move_dir = move_dir;
5774 MovDir[x][y] = new_move_dir;
5775 if (old_move_dir != new_move_dir)
5776 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5780 static void TurnRound(int x, int y)
5782 int direction = MovDir[x][y];
5784 int element, graphic;
5789 GfxDir[x][y] = MovDir[x][y];
5791 if (direction != MovDir[x][y])
5795 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5798 ResetGfxFrame(x, y, FALSE);
5800 element = Feld[x][y];
5801 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5803 if (graphic_info[graphic].anim_global_sync)
5804 GfxFrame[x][y] = FrameCounter;
5805 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5806 GfxFrame[x][y] = CustomValue[x][y];
5807 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5808 GfxFrame[x][y] = element_info[element].collect_score;
5809 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5810 GfxFrame[x][y] = ChangeDelay[x][y];
5814 static boolean JustBeingPushed(int x, int y)
5818 for (i = 0; i < MAX_PLAYERS; i++)
5820 struct PlayerInfo *player = &stored_player[i];
5822 if (player->active && player->is_pushing && player->MovPos)
5824 int next_jx = player->jx + (player->jx - player->last_jx);
5825 int next_jy = player->jy + (player->jy - player->last_jy);
5827 if (x == next_jx && y == next_jy)
5835 void StartMoving(int x, int y)
5837 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5838 int element = Feld[x][y];
5843 if (MovDelay[x][y] == 0)
5844 GfxAction[x][y] = ACTION_DEFAULT;
5846 if (CAN_FALL(element) && y < lev_fieldy - 1)
5848 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5849 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5850 if (JustBeingPushed(x, y))
5853 if (element == EL_QUICKSAND_FULL)
5855 if (IS_FREE(x, y + 1))
5857 InitMovingField(x, y, MV_DOWN);
5858 started_moving = TRUE;
5860 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5861 Store[x][y] = EL_ROCK;
5863 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5865 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5867 if (!MovDelay[x][y])
5868 MovDelay[x][y] = TILEY + 1;
5877 Feld[x][y] = EL_QUICKSAND_EMPTY;
5878 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5879 Store[x][y + 1] = Store[x][y];
5882 PlayLevelSoundAction(x, y, ACTION_FILLING);
5885 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5886 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5888 InitMovingField(x, y, MV_DOWN);
5889 started_moving = TRUE;
5891 Feld[x][y] = EL_QUICKSAND_FILLING;
5892 Store[x][y] = element;
5894 PlayLevelSoundAction(x, y, ACTION_FILLING);
5896 else if (element == EL_MAGIC_WALL_FULL)
5898 if (IS_FREE(x, y + 1))
5900 InitMovingField(x, y, MV_DOWN);
5901 started_moving = TRUE;
5903 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5904 Store[x][y] = EL_CHANGED(Store[x][y]);
5906 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5908 if (!MovDelay[x][y])
5909 MovDelay[x][y] = TILEY/4 + 1;
5918 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5919 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5920 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5924 else if (element == EL_BD_MAGIC_WALL_FULL)
5926 if (IS_FREE(x, y + 1))
5928 InitMovingField(x, y, MV_DOWN);
5929 started_moving = TRUE;
5931 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5932 Store[x][y] = EL_CHANGED2(Store[x][y]);
5934 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5936 if (!MovDelay[x][y])
5937 MovDelay[x][y] = TILEY/4 + 1;
5946 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5947 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5948 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5952 else if (CAN_PASS_MAGIC_WALL(element) &&
5953 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5954 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5956 InitMovingField(x, y, MV_DOWN);
5957 started_moving = TRUE;
5960 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5961 EL_BD_MAGIC_WALL_FILLING);
5962 Store[x][y] = element;
5964 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5966 SplashAcid(x, y + 1);
5968 InitMovingField(x, y, MV_DOWN);
5969 started_moving = TRUE;
5971 Store[x][y] = EL_ACID;
5973 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5974 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5976 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5977 CAN_FALL(element) && WasJustFalling[x][y] &&
5978 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5980 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5981 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5982 (Feld[x][y + 1] == EL_BLOCKED)))
5984 /* this is needed for a special case not covered by calling "Impact()"
5985 from "ContinueMoving()": if an element moves to a tile directly below
5986 another element which was just falling on that tile (which was empty
5987 in the previous frame), the falling element above would just stop
5988 instead of smashing the element below (in previous version, the above
5989 element was just checked for "moving" instead of "falling", resulting
5990 in incorrect smashes caused by horizontal movement of the above
5991 element; also, the case of the player being the element to smash was
5992 simply not covered here... :-/ ) */
5994 CheckCollision[x][y] = 0;
5998 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6000 if (MovDir[x][y] == MV_NONE)
6002 InitMovingField(x, y, MV_DOWN);
6003 started_moving = TRUE;
6006 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6008 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6009 MovDir[x][y] = MV_DOWN;
6011 InitMovingField(x, y, MV_DOWN);
6012 started_moving = TRUE;
6014 else if (element == EL_AMOEBA_DROP)
6016 Feld[x][y] = EL_AMOEBA_GROWING;
6017 Store[x][y] = EL_AMOEBA_WET;
6019 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6020 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6021 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6022 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6024 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6025 (IS_FREE(x - 1, y + 1) ||
6026 Feld[x - 1][y + 1] == EL_ACID));
6027 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6028 (IS_FREE(x + 1, y + 1) ||
6029 Feld[x + 1][y + 1] == EL_ACID));
6030 boolean can_fall_any = (can_fall_left || can_fall_right);
6031 boolean can_fall_both = (can_fall_left && can_fall_right);
6032 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6034 #if USE_NEW_ALL_SLIPPERY
6035 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6037 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6038 can_fall_right = FALSE;
6039 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6040 can_fall_left = FALSE;
6041 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6042 can_fall_right = FALSE;
6043 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6044 can_fall_left = FALSE;
6046 can_fall_any = (can_fall_left || can_fall_right);
6047 can_fall_both = FALSE;
6050 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6052 if (slippery_type == SLIPPERY_ONLY_LEFT)
6053 can_fall_right = FALSE;
6054 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6055 can_fall_left = FALSE;
6056 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6057 can_fall_right = FALSE;
6058 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6059 can_fall_left = FALSE;
6061 can_fall_any = (can_fall_left || can_fall_right);
6062 can_fall_both = (can_fall_left && can_fall_right);
6066 #if USE_NEW_ALL_SLIPPERY
6068 #if USE_NEW_SP_SLIPPERY
6069 /* !!! better use the same properties as for custom elements here !!! */
6070 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6071 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6073 can_fall_right = FALSE; /* slip down on left side */
6074 can_fall_both = FALSE;
6079 #if USE_NEW_ALL_SLIPPERY
6082 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6083 can_fall_right = FALSE; /* slip down on left side */
6085 can_fall_left = !(can_fall_right = RND(2));
6087 can_fall_both = FALSE;
6092 if (game.emulation == EMU_BOULDERDASH ||
6093 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6094 can_fall_right = FALSE; /* slip down on left side */
6096 can_fall_left = !(can_fall_right = RND(2));
6098 can_fall_both = FALSE;
6104 /* if not determined otherwise, prefer left side for slipping down */
6105 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6106 started_moving = TRUE;
6110 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6112 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6115 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6116 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6117 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6118 int belt_dir = game.belt_dir[belt_nr];
6120 if ((belt_dir == MV_LEFT && left_is_free) ||
6121 (belt_dir == MV_RIGHT && right_is_free))
6123 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6125 InitMovingField(x, y, belt_dir);
6126 started_moving = TRUE;
6128 Pushed[x][y] = TRUE;
6129 Pushed[nextx][y] = TRUE;
6131 GfxAction[x][y] = ACTION_DEFAULT;
6135 MovDir[x][y] = 0; /* if element was moving, stop it */
6140 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6142 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6144 if (CAN_MOVE(element) && !started_moving)
6147 int move_pattern = element_info[element].move_pattern;
6152 if (MovDir[x][y] == MV_NONE)
6154 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6155 x, y, element, element_info[element].token_name);
6156 printf("StartMoving(): This should never happen!\n");
6161 Moving2Blocked(x, y, &newx, &newy);
6163 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6166 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6167 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6169 WasJustMoving[x][y] = 0;
6170 CheckCollision[x][y] = 0;
6172 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6174 if (Feld[x][y] != element) /* element has changed */
6178 if (!MovDelay[x][y]) /* start new movement phase */
6180 /* all objects that can change their move direction after each step
6181 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6183 if (element != EL_YAMYAM &&
6184 element != EL_DARK_YAMYAM &&
6185 element != EL_PACMAN &&
6186 !(move_pattern & MV_ANY_DIRECTION) &&
6187 move_pattern != MV_TURNING_LEFT &&
6188 move_pattern != MV_TURNING_RIGHT &&
6189 move_pattern != MV_TURNING_LEFT_RIGHT &&
6190 move_pattern != MV_TURNING_RIGHT_LEFT &&
6191 move_pattern != MV_TURNING_RANDOM)
6195 if (MovDelay[x][y] && (element == EL_BUG ||
6196 element == EL_SPACESHIP ||
6197 element == EL_SP_SNIKSNAK ||
6198 element == EL_SP_ELECTRON ||
6199 element == EL_MOLE))
6200 DrawLevelField(x, y);
6204 if (MovDelay[x][y]) /* wait some time before next movement */
6208 if (element == EL_ROBOT ||
6209 element == EL_YAMYAM ||
6210 element == EL_DARK_YAMYAM)
6212 DrawLevelElementAnimationIfNeeded(x, y, element);
6213 PlayLevelSoundAction(x, y, ACTION_WAITING);
6215 else if (element == EL_SP_ELECTRON)
6216 DrawLevelElementAnimationIfNeeded(x, y, element);
6217 else if (element == EL_DRAGON)
6220 int dir = MovDir[x][y];
6221 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6222 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6223 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6224 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6225 dir == MV_UP ? IMG_FLAMES_1_UP :
6226 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6227 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6229 GfxAction[x][y] = ACTION_ATTACKING;
6231 if (IS_PLAYER(x, y))
6232 DrawPlayerField(x, y);
6234 DrawLevelField(x, y);
6236 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6238 for (i = 1; i <= 3; i++)
6240 int xx = x + i * dx;
6241 int yy = y + i * dy;
6242 int sx = SCREENX(xx);
6243 int sy = SCREENY(yy);
6244 int flame_graphic = graphic + (i - 1);
6246 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6251 int flamed = MovingOrBlocked2Element(xx, yy);
6255 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6257 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6258 RemoveMovingField(xx, yy);
6260 RemoveField(xx, yy);
6262 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6265 RemoveMovingField(xx, yy);
6268 ChangeDelay[xx][yy] = 0;
6270 Feld[xx][yy] = EL_FLAMES;
6272 if (IN_SCR_FIELD(sx, sy))
6274 DrawLevelFieldCrumbledSand(xx, yy);
6275 DrawGraphic(sx, sy, flame_graphic, frame);
6280 if (Feld[xx][yy] == EL_FLAMES)
6281 Feld[xx][yy] = EL_EMPTY;
6282 DrawLevelField(xx, yy);
6287 if (MovDelay[x][y]) /* element still has to wait some time */
6289 PlayLevelSoundAction(x, y, ACTION_WAITING);
6295 /* now make next step */
6297 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6299 if (DONT_COLLIDE_WITH(element) &&
6300 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6301 !PLAYER_ENEMY_PROTECTED(newx, newy))
6303 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6308 else if (CAN_MOVE_INTO_ACID(element) &&
6309 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6310 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6311 (MovDir[x][y] == MV_DOWN ||
6312 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6314 SplashAcid(newx, newy);
6315 Store[x][y] = EL_ACID;
6317 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6319 if (Feld[newx][newy] == EL_EXIT_OPEN)
6322 DrawLevelField(x, y);
6324 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6325 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6326 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6328 local_player->friends_still_needed--;
6329 if (!local_player->friends_still_needed &&
6330 !local_player->GameOver && AllPlayersGone)
6331 local_player->LevelSolved = local_player->GameOver = TRUE;
6335 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6337 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6338 DrawLevelField(newx, newy);
6340 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6342 else if (!IS_FREE(newx, newy))
6344 GfxAction[x][y] = ACTION_WAITING;
6346 if (IS_PLAYER(x, y))
6347 DrawPlayerField(x, y);
6349 DrawLevelField(x, y);
6354 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6356 if (IS_FOOD_PIG(Feld[newx][newy]))
6358 if (IS_MOVING(newx, newy))
6359 RemoveMovingField(newx, newy);
6362 Feld[newx][newy] = EL_EMPTY;
6363 DrawLevelField(newx, newy);
6366 PlayLevelSound(x, y, SND_PIG_DIGGING);
6368 else if (!IS_FREE(newx, newy))
6370 if (IS_PLAYER(x, y))
6371 DrawPlayerField(x, y);
6373 DrawLevelField(x, y);
6378 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6380 if (Store[x][y] != EL_EMPTY)
6382 boolean can_clone = FALSE;
6385 /* check if element to clone is still there */
6386 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6388 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6396 /* cannot clone or target field not free anymore -- do not clone */
6397 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6398 Store[x][y] = EL_EMPTY;
6401 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6403 if (IS_MV_DIAGONAL(MovDir[x][y]))
6405 int diagonal_move_dir = MovDir[x][y];
6406 int stored = Store[x][y];
6407 int change_delay = 8;
6410 /* android is moving diagonally */
6412 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6414 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6415 GfxElement[x][y] = EL_EMC_ANDROID;
6416 GfxAction[x][y] = ACTION_SHRINKING;
6417 GfxDir[x][y] = diagonal_move_dir;
6418 ChangeDelay[x][y] = change_delay;
6420 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6423 DrawLevelGraphicAnimation(x, y, graphic);
6424 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6426 if (Feld[newx][newy] == EL_ACID)
6428 SplashAcid(newx, newy);
6433 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6435 Store[newx][newy] = EL_EMC_ANDROID;
6436 GfxElement[newx][newy] = EL_EMC_ANDROID;
6437 GfxAction[newx][newy] = ACTION_GROWING;
6438 GfxDir[newx][newy] = diagonal_move_dir;
6439 ChangeDelay[newx][newy] = change_delay;
6441 graphic = el_act_dir2img(GfxElement[newx][newy],
6442 GfxAction[newx][newy], GfxDir[newx][newy]);
6444 DrawLevelGraphicAnimation(newx, newy, graphic);
6445 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6451 Feld[newx][newy] = EL_EMPTY;
6452 DrawLevelField(newx, newy);
6454 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6457 else if (!IS_FREE(newx, newy))
6460 if (IS_PLAYER(x, y))
6461 DrawPlayerField(x, y);
6463 DrawLevelField(x, y);
6469 else if (IS_CUSTOM_ELEMENT(element) &&
6470 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6472 int new_element = Feld[newx][newy];
6474 if (!IS_FREE(newx, newy))
6476 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6477 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6480 /* no element can dig solid indestructible elements */
6481 if (IS_INDESTRUCTIBLE(new_element) &&
6482 !IS_DIGGABLE(new_element) &&
6483 !IS_COLLECTIBLE(new_element))
6486 if (AmoebaNr[newx][newy] &&
6487 (new_element == EL_AMOEBA_FULL ||
6488 new_element == EL_BD_AMOEBA ||
6489 new_element == EL_AMOEBA_GROWING))
6491 AmoebaCnt[AmoebaNr[newx][newy]]--;
6492 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6495 if (IS_MOVING(newx, newy))
6496 RemoveMovingField(newx, newy);
6499 RemoveField(newx, newy);
6500 DrawLevelField(newx, newy);
6503 /* if digged element was about to explode, prevent the explosion */
6504 ExplodeField[newx][newy] = EX_TYPE_NONE;
6506 PlayLevelSoundAction(x, y, action);
6509 Store[newx][newy] = EL_EMPTY;
6511 /* this makes it possible to leave the removed element again */
6512 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6513 Store[newx][newy] = new_element;
6515 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6517 int move_leave_element = element_info[element].move_leave_element;
6519 /* this makes it possible to leave the removed element again */
6520 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6521 new_element : move_leave_element);
6525 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6527 RunnerVisit[x][y] = FrameCounter;
6528 PlayerVisit[x][y] /= 8; /* expire player visit path */
6531 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6533 if (!IS_FREE(newx, newy))
6535 if (IS_PLAYER(x, y))
6536 DrawPlayerField(x, y);
6538 DrawLevelField(x, y);
6544 boolean wanna_flame = !RND(10);
6545 int dx = newx - x, dy = newy - y;
6546 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6547 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6548 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6549 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6550 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6551 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6554 IS_CLASSIC_ENEMY(element1) ||
6555 IS_CLASSIC_ENEMY(element2)) &&
6556 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6557 element1 != EL_FLAMES && element2 != EL_FLAMES)
6559 ResetGfxAnimation(x, y);
6560 GfxAction[x][y] = ACTION_ATTACKING;
6562 if (IS_PLAYER(x, y))
6563 DrawPlayerField(x, y);
6565 DrawLevelField(x, y);
6567 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6569 MovDelay[x][y] = 50;
6573 RemoveField(newx, newy);
6575 Feld[newx][newy] = EL_FLAMES;
6576 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6579 RemoveField(newx1, newy1);
6581 Feld[newx1][newy1] = EL_FLAMES;
6583 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6586 RemoveField(newx2, newy2);
6588 Feld[newx2][newy2] = EL_FLAMES;
6595 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6596 Feld[newx][newy] == EL_DIAMOND)
6598 if (IS_MOVING(newx, newy))
6599 RemoveMovingField(newx, newy);
6602 Feld[newx][newy] = EL_EMPTY;
6603 DrawLevelField(newx, newy);
6606 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6608 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6609 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6611 if (AmoebaNr[newx][newy])
6613 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6614 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6615 Feld[newx][newy] == EL_BD_AMOEBA)
6616 AmoebaCnt[AmoebaNr[newx][newy]]--;
6621 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6623 RemoveMovingField(newx, newy);
6626 if (IS_MOVING(newx, newy))
6628 RemoveMovingField(newx, newy);
6633 Feld[newx][newy] = EL_EMPTY;
6634 DrawLevelField(newx, newy);
6637 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6639 else if ((element == EL_PACMAN || element == EL_MOLE)
6640 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6642 if (AmoebaNr[newx][newy])
6644 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6645 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6646 Feld[newx][newy] == EL_BD_AMOEBA)
6647 AmoebaCnt[AmoebaNr[newx][newy]]--;
6650 if (element == EL_MOLE)
6652 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6653 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6655 ResetGfxAnimation(x, y);
6656 GfxAction[x][y] = ACTION_DIGGING;
6657 DrawLevelField(x, y);
6659 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6661 return; /* wait for shrinking amoeba */
6663 else /* element == EL_PACMAN */
6665 Feld[newx][newy] = EL_EMPTY;
6666 DrawLevelField(newx, newy);
6667 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6670 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6671 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6672 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6674 /* wait for shrinking amoeba to completely disappear */
6677 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6679 /* object was running against a wall */
6684 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6685 if (move_pattern & MV_ANY_DIRECTION &&
6686 move_pattern == MovDir[x][y])
6688 int blocking_element =
6689 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6691 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6694 element = Feld[x][y]; /* element might have changed */
6698 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6699 DrawLevelElementAnimation(x, y, element);
6701 if (DONT_TOUCH(element))
6702 TestIfBadThingTouchesPlayer(x, y);
6707 InitMovingField(x, y, MovDir[x][y]);
6709 PlayLevelSoundAction(x, y, ACTION_MOVING);
6713 ContinueMoving(x, y);
6716 void ContinueMoving(int x, int y)
6718 int element = Feld[x][y];
6719 struct ElementInfo *ei = &element_info[element];
6720 int direction = MovDir[x][y];
6721 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6722 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6723 int newx = x + dx, newy = y + dy;
6724 int stored = Store[x][y];
6725 int stored_new = Store[newx][newy];
6726 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6727 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6728 boolean last_line = (newy == lev_fieldy - 1);
6730 MovPos[x][y] += getElementMoveStepsize(x, y);
6732 if (pushed_by_player) /* special case: moving object pushed by player */
6733 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6735 if (ABS(MovPos[x][y]) < TILEX)
6737 DrawLevelField(x, y);
6739 return; /* element is still moving */
6742 /* element reached destination field */
6744 Feld[x][y] = EL_EMPTY;
6745 Feld[newx][newy] = element;
6746 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6748 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6750 element = Feld[newx][newy] = EL_ACID;
6752 else if (element == EL_MOLE)
6754 Feld[x][y] = EL_SAND;
6756 DrawLevelFieldCrumbledSandNeighbours(x, y);
6758 else if (element == EL_QUICKSAND_FILLING)
6760 element = Feld[newx][newy] = get_next_element(element);
6761 Store[newx][newy] = Store[x][y];
6763 else if (element == EL_QUICKSAND_EMPTYING)
6765 Feld[x][y] = get_next_element(element);
6766 element = Feld[newx][newy] = Store[x][y];
6768 else if (element == EL_MAGIC_WALL_FILLING)
6770 element = Feld[newx][newy] = get_next_element(element);
6771 if (!game.magic_wall_active)
6772 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6773 Store[newx][newy] = Store[x][y];
6775 else if (element == EL_MAGIC_WALL_EMPTYING)
6777 Feld[x][y] = get_next_element(element);
6778 if (!game.magic_wall_active)
6779 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6780 element = Feld[newx][newy] = Store[x][y];
6782 #if USE_NEW_CUSTOM_VALUE
6783 InitField(newx, newy, FALSE);
6786 else if (element == EL_BD_MAGIC_WALL_FILLING)
6788 element = Feld[newx][newy] = get_next_element(element);
6789 if (!game.magic_wall_active)
6790 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6791 Store[newx][newy] = Store[x][y];
6793 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6795 Feld[x][y] = get_next_element(element);
6796 if (!game.magic_wall_active)
6797 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6798 element = Feld[newx][newy] = Store[x][y];
6800 #if USE_NEW_CUSTOM_VALUE
6801 InitField(newx, newy, FALSE);
6804 else if (element == EL_AMOEBA_DROPPING)
6806 Feld[x][y] = get_next_element(element);
6807 element = Feld[newx][newy] = Store[x][y];
6809 else if (element == EL_SOKOBAN_OBJECT)
6812 Feld[x][y] = Back[x][y];
6814 if (Back[newx][newy])
6815 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6817 Back[x][y] = Back[newx][newy] = 0;
6820 Store[x][y] = EL_EMPTY;
6825 MovDelay[newx][newy] = 0;
6828 if (CAN_CHANGE_OR_HAS_ACTION(element))
6830 if (CAN_CHANGE(element))
6833 /* copy element change control values to new field */
6834 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6835 ChangePage[newx][newy] = ChangePage[x][y];
6836 ChangeCount[newx][newy] = ChangeCount[x][y];
6837 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6840 #if USE_NEW_CUSTOM_VALUE
6841 CustomValue[newx][newy] = CustomValue[x][y];
6847 #if USE_NEW_CUSTOM_VALUE
6848 CustomValue[newx][newy] = CustomValue[x][y];
6852 ChangeDelay[x][y] = 0;
6853 ChangePage[x][y] = -1;
6854 ChangeCount[x][y] = 0;
6855 ChangeEvent[x][y] = -1;
6857 #if USE_NEW_CUSTOM_VALUE
6858 CustomValue[x][y] = 0;
6861 /* copy animation control values to new field */
6862 GfxFrame[newx][newy] = GfxFrame[x][y];
6863 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6864 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6865 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6867 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6869 /* some elements can leave other elements behind after moving */
6871 if (ei->move_leave_element != EL_EMPTY &&
6872 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6873 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6875 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6876 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6877 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6880 int move_leave_element = ei->move_leave_element;
6884 /* this makes it possible to leave the removed element again */
6885 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6886 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6888 /* this makes it possible to leave the removed element again */
6889 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6890 move_leave_element = stored;
6893 /* this makes it possible to leave the removed element again */
6894 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6895 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6896 move_leave_element = stored;
6899 Feld[x][y] = move_leave_element;
6901 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6902 MovDir[x][y] = direction;
6904 InitField(x, y, FALSE);
6906 if (GFX_CRUMBLED(Feld[x][y]))
6907 DrawLevelFieldCrumbledSandNeighbours(x, y);
6909 if (ELEM_IS_PLAYER(move_leave_element))
6910 RelocatePlayer(x, y, move_leave_element);
6913 /* do this after checking for left-behind element */
6914 ResetGfxAnimation(x, y); /* reset animation values for old field */
6916 if (!CAN_MOVE(element) ||
6917 (CAN_FALL(element) && direction == MV_DOWN &&
6918 (element == EL_SPRING ||
6919 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6920 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6921 GfxDir[x][y] = MovDir[newx][newy] = 0;
6923 DrawLevelField(x, y);
6924 DrawLevelField(newx, newy);
6926 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6928 /* prevent pushed element from moving on in pushed direction */
6929 if (pushed_by_player && CAN_MOVE(element) &&
6930 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6931 !(element_info[element].move_pattern & direction))
6932 TurnRound(newx, newy);
6934 /* prevent elements on conveyor belt from moving on in last direction */
6935 if (pushed_by_conveyor && CAN_FALL(element) &&
6936 direction & MV_HORIZONTAL)
6937 MovDir[newx][newy] = 0;
6939 if (!pushed_by_player)
6941 int nextx = newx + dx, nexty = newy + dy;
6942 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6944 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6946 if (CAN_FALL(element) && direction == MV_DOWN)
6947 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6949 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6950 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6953 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6955 TestIfBadThingTouchesPlayer(newx, newy);
6956 TestIfBadThingTouchesFriend(newx, newy);
6958 if (!IS_CUSTOM_ELEMENT(element))
6959 TestIfBadThingTouchesOtherBadThing(newx, newy);
6961 else if (element == EL_PENGUIN)
6962 TestIfFriendTouchesBadThing(newx, newy);
6964 /* give the player one last chance (one more frame) to move away */
6965 if (CAN_FALL(element) && direction == MV_DOWN &&
6966 (last_line || (!IS_FREE(x, newy + 1) &&
6967 (!IS_PLAYER(x, newy + 1) ||
6968 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6971 if (pushed_by_player && !game.use_change_when_pushing_bug)
6973 int push_side = MV_DIR_OPPOSITE(direction);
6974 struct PlayerInfo *player = PLAYERINFO(x, y);
6976 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6977 player->index_bit, push_side);
6978 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6979 player->index_bit, push_side);
6982 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6983 MovDelay[newx][newy] = 1;
6985 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6987 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6990 if (ChangePage[newx][newy] != -1) /* delayed change */
6992 int page = ChangePage[newx][newy];
6993 struct ElementChangeInfo *change = &ei->change_page[page];
6995 ChangePage[newx][newy] = -1;
6997 if (change->can_change)
6999 if (ChangeElement(newx, newy, element, page))
7001 if (change->post_change_function)
7002 change->post_change_function(newx, newy);
7006 if (change->has_action)
7007 ExecuteCustomElementAction(newx, newy, element, page);
7011 TestIfElementHitsCustomElement(newx, newy, direction);
7012 TestIfPlayerTouchesCustomElement(newx, newy);
7013 TestIfElementTouchesCustomElement(newx, newy);
7016 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7017 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7018 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7019 MV_DIR_OPPOSITE(direction));
7023 int AmoebeNachbarNr(int ax, int ay)
7026 int element = Feld[ax][ay];
7028 static int xy[4][2] =
7036 for (i = 0; i < NUM_DIRECTIONS; i++)
7038 int x = ax + xy[i][0];
7039 int y = ay + xy[i][1];
7041 if (!IN_LEV_FIELD(x, y))
7044 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7045 group_nr = AmoebaNr[x][y];
7051 void AmoebenVereinigen(int ax, int ay)
7053 int i, x, y, xx, yy;
7054 int new_group_nr = AmoebaNr[ax][ay];
7055 static int xy[4][2] =
7063 if (new_group_nr == 0)
7066 for (i = 0; i < NUM_DIRECTIONS; i++)
7071 if (!IN_LEV_FIELD(x, y))
7074 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7075 Feld[x][y] == EL_BD_AMOEBA ||
7076 Feld[x][y] == EL_AMOEBA_DEAD) &&
7077 AmoebaNr[x][y] != new_group_nr)
7079 int old_group_nr = AmoebaNr[x][y];
7081 if (old_group_nr == 0)
7084 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7085 AmoebaCnt[old_group_nr] = 0;
7086 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7087 AmoebaCnt2[old_group_nr] = 0;
7090 SCAN_PLAYFIELD(xx, yy)
7092 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7095 if (AmoebaNr[xx][yy] == old_group_nr)
7096 AmoebaNr[xx][yy] = new_group_nr;
7102 void AmoebeUmwandeln(int ax, int ay)
7106 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7108 int group_nr = AmoebaNr[ax][ay];
7113 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7114 printf("AmoebeUmwandeln(): This should never happen!\n");
7120 SCAN_PLAYFIELD(x, y)
7122 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7125 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7128 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7132 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7133 SND_AMOEBA_TURNING_TO_GEM :
7134 SND_AMOEBA_TURNING_TO_ROCK));
7139 static int xy[4][2] =
7147 for (i = 0; i < NUM_DIRECTIONS; i++)
7152 if (!IN_LEV_FIELD(x, y))
7155 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7157 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7158 SND_AMOEBA_TURNING_TO_GEM :
7159 SND_AMOEBA_TURNING_TO_ROCK));
7166 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7169 int group_nr = AmoebaNr[ax][ay];
7170 boolean done = FALSE;
7175 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7176 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7182 SCAN_PLAYFIELD(x, y)
7184 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7187 if (AmoebaNr[x][y] == group_nr &&
7188 (Feld[x][y] == EL_AMOEBA_DEAD ||
7189 Feld[x][y] == EL_BD_AMOEBA ||
7190 Feld[x][y] == EL_AMOEBA_GROWING))
7193 Feld[x][y] = new_element;
7194 InitField(x, y, FALSE);
7195 DrawLevelField(x, y);
7201 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7202 SND_BD_AMOEBA_TURNING_TO_ROCK :
7203 SND_BD_AMOEBA_TURNING_TO_GEM));
7206 void AmoebeWaechst(int x, int y)
7208 static unsigned long sound_delay = 0;
7209 static unsigned long sound_delay_value = 0;
7211 if (!MovDelay[x][y]) /* start new growing cycle */
7215 if (DelayReached(&sound_delay, sound_delay_value))
7217 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7218 sound_delay_value = 30;
7222 if (MovDelay[x][y]) /* wait some time before growing bigger */
7225 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7227 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7228 6 - MovDelay[x][y]);
7230 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7233 if (!MovDelay[x][y])
7235 Feld[x][y] = Store[x][y];
7237 DrawLevelField(x, y);
7242 void AmoebaDisappearing(int x, int y)
7244 static unsigned long sound_delay = 0;
7245 static unsigned long sound_delay_value = 0;
7247 if (!MovDelay[x][y]) /* start new shrinking cycle */
7251 if (DelayReached(&sound_delay, sound_delay_value))
7252 sound_delay_value = 30;
7255 if (MovDelay[x][y]) /* wait some time before shrinking */
7258 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7260 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7261 6 - MovDelay[x][y]);
7263 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7266 if (!MovDelay[x][y])
7268 Feld[x][y] = EL_EMPTY;
7269 DrawLevelField(x, y);
7271 /* don't let mole enter this field in this cycle;
7272 (give priority to objects falling to this field from above) */
7278 void AmoebeAbleger(int ax, int ay)
7281 int element = Feld[ax][ay];
7282 int graphic = el2img(element);
7283 int newax = ax, neway = ay;
7284 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7285 static int xy[4][2] =
7293 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7295 Feld[ax][ay] = EL_AMOEBA_DEAD;
7296 DrawLevelField(ax, ay);
7300 if (IS_ANIMATED(graphic))
7301 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7303 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7304 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7306 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7309 if (MovDelay[ax][ay])
7313 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7316 int x = ax + xy[start][0];
7317 int y = ay + xy[start][1];
7319 if (!IN_LEV_FIELD(x, y))
7322 if (IS_FREE(x, y) ||
7323 CAN_GROW_INTO(Feld[x][y]) ||
7324 Feld[x][y] == EL_QUICKSAND_EMPTY)
7330 if (newax == ax && neway == ay)
7333 else /* normal or "filled" (BD style) amoeba */
7336 boolean waiting_for_player = FALSE;
7338 for (i = 0; i < NUM_DIRECTIONS; i++)
7340 int j = (start + i) % 4;
7341 int x = ax + xy[j][0];
7342 int y = ay + xy[j][1];
7344 if (!IN_LEV_FIELD(x, y))
7347 if (IS_FREE(x, y) ||
7348 CAN_GROW_INTO(Feld[x][y]) ||
7349 Feld[x][y] == EL_QUICKSAND_EMPTY)
7355 else if (IS_PLAYER(x, y))
7356 waiting_for_player = TRUE;
7359 if (newax == ax && neway == ay) /* amoeba cannot grow */
7361 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7363 Feld[ax][ay] = EL_AMOEBA_DEAD;
7364 DrawLevelField(ax, ay);
7365 AmoebaCnt[AmoebaNr[ax][ay]]--;
7367 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7369 if (element == EL_AMOEBA_FULL)
7370 AmoebeUmwandeln(ax, ay);
7371 else if (element == EL_BD_AMOEBA)
7372 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7377 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7379 /* amoeba gets larger by growing in some direction */
7381 int new_group_nr = AmoebaNr[ax][ay];
7384 if (new_group_nr == 0)
7386 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7387 printf("AmoebeAbleger(): This should never happen!\n");
7392 AmoebaNr[newax][neway] = new_group_nr;
7393 AmoebaCnt[new_group_nr]++;
7394 AmoebaCnt2[new_group_nr]++;
7396 /* if amoeba touches other amoeba(s) after growing, unify them */
7397 AmoebenVereinigen(newax, neway);
7399 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7401 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7407 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7408 (neway == lev_fieldy - 1 && newax != ax))
7410 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7411 Store[newax][neway] = element;
7413 else if (neway == ay || element == EL_EMC_DRIPPER)
7415 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7417 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7421 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7422 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7423 Store[ax][ay] = EL_AMOEBA_DROP;
7424 ContinueMoving(ax, ay);
7428 DrawLevelField(newax, neway);
7431 void Life(int ax, int ay)
7435 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7438 int element = Feld[ax][ay];
7439 int graphic = el2img(element);
7440 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7442 boolean changed = FALSE;
7444 if (IS_ANIMATED(graphic))
7445 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7450 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7451 MovDelay[ax][ay] = life_time;
7453 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7456 if (MovDelay[ax][ay])
7460 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7462 int xx = ax+x1, yy = ay+y1;
7465 if (!IN_LEV_FIELD(xx, yy))
7468 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7470 int x = xx+x2, y = yy+y2;
7472 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7475 if (((Feld[x][y] == element ||
7476 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7478 (IS_FREE(x, y) && Stop[x][y]))
7482 if (xx == ax && yy == ay) /* field in the middle */
7484 if (nachbarn < life_parameter[0] ||
7485 nachbarn > life_parameter[1])
7487 Feld[xx][yy] = EL_EMPTY;
7489 DrawLevelField(xx, yy);
7490 Stop[xx][yy] = TRUE;
7494 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7495 { /* free border field */
7496 if (nachbarn >= life_parameter[2] &&
7497 nachbarn <= life_parameter[3])
7499 Feld[xx][yy] = element;
7500 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7502 DrawLevelField(xx, yy);
7503 Stop[xx][yy] = TRUE;
7510 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7511 SND_GAME_OF_LIFE_GROWING);
7514 static void InitRobotWheel(int x, int y)
7516 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7519 static void RunRobotWheel(int x, int y)
7521 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7524 static void StopRobotWheel(int x, int y)
7526 if (ZX == x && ZY == y)
7530 static void InitTimegateWheel(int x, int y)
7532 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7535 static void RunTimegateWheel(int x, int y)
7537 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7540 static void InitMagicBallDelay(int x, int y)
7543 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7545 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7549 static void ActivateMagicBall(int bx, int by)
7553 if (level.ball_random)
7555 int pos_border = RND(8); /* select one of the eight border elements */
7556 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7557 int xx = pos_content % 3;
7558 int yy = pos_content / 3;
7563 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7564 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7568 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7570 int xx = x - bx + 1;
7571 int yy = y - by + 1;
7573 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7574 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7578 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7581 static void InitDiagonalMovingElement(int x, int y)
7584 MovDelay[x][y] = level.android_move_time;
7588 void CheckExit(int x, int y)
7590 if (local_player->gems_still_needed > 0 ||
7591 local_player->sokobanfields_still_needed > 0 ||
7592 local_player->lights_still_needed > 0)
7594 int element = Feld[x][y];
7595 int graphic = el2img(element);
7597 if (IS_ANIMATED(graphic))
7598 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7603 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7606 Feld[x][y] = EL_EXIT_OPENING;
7608 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7611 void CheckExitSP(int x, int y)
7613 if (local_player->gems_still_needed > 0)
7615 int element = Feld[x][y];
7616 int graphic = el2img(element);
7618 if (IS_ANIMATED(graphic))
7619 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7624 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7627 Feld[x][y] = EL_SP_EXIT_OPENING;
7629 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7632 static void CloseAllOpenTimegates()
7637 SCAN_PLAYFIELD(x, y)
7639 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7642 int element = Feld[x][y];
7644 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7646 Feld[x][y] = EL_TIMEGATE_CLOSING;
7648 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7653 void EdelsteinFunkeln(int x, int y)
7655 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7658 if (Feld[x][y] == EL_BD_DIAMOND)
7661 if (MovDelay[x][y] == 0) /* next animation frame */
7662 MovDelay[x][y] = 11 * !SimpleRND(500);
7664 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7668 if (setup.direct_draw && MovDelay[x][y])
7669 SetDrawtoField(DRAW_BUFFERED);
7671 DrawLevelElementAnimation(x, y, Feld[x][y]);
7673 if (MovDelay[x][y] != 0)
7675 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7676 10 - MovDelay[x][y]);
7678 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7680 if (setup.direct_draw)
7684 dest_x = FX + SCREENX(x) * TILEX;
7685 dest_y = FY + SCREENY(y) * TILEY;
7687 BlitBitmap(drawto_field, window,
7688 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7689 SetDrawtoField(DRAW_DIRECT);
7695 void MauerWaechst(int x, int y)
7699 if (!MovDelay[x][y]) /* next animation frame */
7700 MovDelay[x][y] = 3 * delay;
7702 if (MovDelay[x][y]) /* wait some time before next frame */
7706 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7708 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7709 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7711 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7714 if (!MovDelay[x][y])
7716 if (MovDir[x][y] == MV_LEFT)
7718 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7719 DrawLevelField(x - 1, y);
7721 else if (MovDir[x][y] == MV_RIGHT)
7723 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7724 DrawLevelField(x + 1, y);
7726 else if (MovDir[x][y] == MV_UP)
7728 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7729 DrawLevelField(x, y - 1);
7733 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7734 DrawLevelField(x, y + 1);
7737 Feld[x][y] = Store[x][y];
7739 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7740 DrawLevelField(x, y);
7745 void MauerAbleger(int ax, int ay)
7747 int element = Feld[ax][ay];
7748 int graphic = el2img(element);
7749 boolean oben_frei = FALSE, unten_frei = FALSE;
7750 boolean links_frei = FALSE, rechts_frei = FALSE;
7751 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7752 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7753 boolean new_wall = FALSE;
7755 if (IS_ANIMATED(graphic))
7756 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7758 if (!MovDelay[ax][ay]) /* start building new wall */
7759 MovDelay[ax][ay] = 6;
7761 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7764 if (MovDelay[ax][ay])
7768 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7770 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7772 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7774 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7777 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7778 element == EL_EXPANDABLE_WALL_ANY)
7782 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7783 Store[ax][ay-1] = element;
7784 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7785 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7786 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7787 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7792 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7793 Store[ax][ay+1] = element;
7794 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7795 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7796 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7797 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7802 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7803 element == EL_EXPANDABLE_WALL_ANY ||
7804 element == EL_EXPANDABLE_WALL)
7808 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7809 Store[ax-1][ay] = element;
7810 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7811 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7812 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7813 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7819 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7820 Store[ax+1][ay] = element;
7821 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7822 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7823 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7824 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7829 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7830 DrawLevelField(ax, ay);
7832 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7834 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7835 unten_massiv = TRUE;
7836 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7837 links_massiv = TRUE;
7838 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7839 rechts_massiv = TRUE;
7841 if (((oben_massiv && unten_massiv) ||
7842 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7843 element == EL_EXPANDABLE_WALL) &&
7844 ((links_massiv && rechts_massiv) ||
7845 element == EL_EXPANDABLE_WALL_VERTICAL))
7846 Feld[ax][ay] = EL_WALL;
7849 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7852 void CheckForDragon(int x, int y)
7855 boolean dragon_found = FALSE;
7856 static int xy[4][2] =
7864 for (i = 0; i < NUM_DIRECTIONS; i++)
7866 for (j = 0; j < 4; j++)
7868 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7870 if (IN_LEV_FIELD(xx, yy) &&
7871 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7873 if (Feld[xx][yy] == EL_DRAGON)
7874 dragon_found = TRUE;
7883 for (i = 0; i < NUM_DIRECTIONS; i++)
7885 for (j = 0; j < 3; j++)
7887 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7889 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7891 Feld[xx][yy] = EL_EMPTY;
7892 DrawLevelField(xx, yy);
7901 static void InitBuggyBase(int x, int y)
7903 int element = Feld[x][y];
7904 int activating_delay = FRAMES_PER_SECOND / 4;
7907 (element == EL_SP_BUGGY_BASE ?
7908 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7909 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7911 element == EL_SP_BUGGY_BASE_ACTIVE ?
7912 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7915 static void WarnBuggyBase(int x, int y)
7918 static int xy[4][2] =
7926 for (i = 0; i < NUM_DIRECTIONS; i++)
7928 int xx = x + xy[i][0];
7929 int yy = y + xy[i][1];
7931 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7933 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7940 static void InitTrap(int x, int y)
7942 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7945 static void ActivateTrap(int x, int y)
7947 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7950 static void ChangeActiveTrap(int x, int y)
7952 int graphic = IMG_TRAP_ACTIVE;
7954 /* if new animation frame was drawn, correct crumbled sand border */
7955 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7956 DrawLevelFieldCrumbledSand(x, y);
7959 static int getSpecialActionElement(int element, int number, int base_element)
7961 return (element != EL_EMPTY ? element :
7962 number != -1 ? base_element + number - 1 :
7966 static int getModifiedActionNumber(int value_old, int operator, int operand,
7967 int value_min, int value_max)
7969 int value_new = (operator == CA_MODE_SET ? operand :
7970 operator == CA_MODE_ADD ? value_old + operand :
7971 operator == CA_MODE_SUBTRACT ? value_old - operand :
7972 operator == CA_MODE_MULTIPLY ? value_old * operand :
7973 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7974 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7977 return (value_new < value_min ? value_min :
7978 value_new > value_max ? value_max :
7982 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7984 struct ElementInfo *ei = &element_info[element];
7985 struct ElementChangeInfo *change = &ei->change_page[page];
7986 int target_element = change->target_element;
7987 int action_type = change->action_type;
7988 int action_mode = change->action_mode;
7989 int action_arg = change->action_arg;
7992 if (!change->has_action)
7995 /* ---------- determine action paramater values -------------------------- */
7997 int level_time_value =
7998 (level.time > 0 ? TimeLeft :
8001 int action_arg_element =
8002 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8003 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8004 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8007 int action_arg_direction =
8008 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8009 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8010 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8011 change->actual_trigger_side :
8012 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8013 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8016 int action_arg_number_min =
8017 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8020 int action_arg_number_max =
8021 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8022 action_type == CA_SET_LEVEL_GEMS ? 999 :
8023 action_type == CA_SET_LEVEL_TIME ? 9999 :
8024 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8025 action_type == CA_SET_CE_VALUE ? 9999 :
8026 action_type == CA_SET_CE_SCORE ? 9999 :
8029 int action_arg_number_reset =
8030 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8031 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8032 action_type == CA_SET_LEVEL_TIME ? level.time :
8033 action_type == CA_SET_LEVEL_SCORE ? 0 :
8035 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8037 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8039 action_type == CA_SET_CE_SCORE ? 0 :
8042 int action_arg_number =
8043 (action_arg <= CA_ARG_MAX ? action_arg :
8044 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8045 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8046 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8047 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8048 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8049 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8050 #if USE_NEW_CUSTOM_VALUE
8051 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8053 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8055 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8056 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8057 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8058 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8059 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8060 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8061 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8062 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8063 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8064 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8065 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8068 int action_arg_number_old =
8069 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8070 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8071 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8072 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8073 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8076 int action_arg_number_new =
8077 getModifiedActionNumber(action_arg_number_old,
8078 action_mode, action_arg_number,
8079 action_arg_number_min, action_arg_number_max);
8081 int trigger_player_bits =
8082 (change->actual_trigger_player >= EL_PLAYER_1 &&
8083 change->actual_trigger_player <= EL_PLAYER_4 ?
8084 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8087 int action_arg_player_bits =
8088 (action_arg >= CA_ARG_PLAYER_1 &&
8089 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8090 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8093 /* ---------- execute action -------------------------------------------- */
8102 /* ---------- level actions ------------------------------------------- */
8104 case CA_RESTART_LEVEL:
8106 game.restart_level = TRUE;
8111 case CA_SHOW_ENVELOPE:
8113 int element = getSpecialActionElement(action_arg_element,
8114 action_arg_number, EL_ENVELOPE_1);
8116 if (IS_ENVELOPE(element))
8117 local_player->show_envelope = element;
8122 case CA_SET_LEVEL_TIME:
8124 if (level.time > 0) /* only modify limited time value */
8126 TimeLeft = action_arg_number_new;
8128 DrawGameValue_Time(TimeLeft);
8130 if (!TimeLeft && setup.time_limit)
8131 for (i = 0; i < MAX_PLAYERS; i++)
8132 KillPlayer(&stored_player[i]);
8138 case CA_SET_LEVEL_SCORE:
8140 local_player->score = action_arg_number_new;
8142 DrawGameValue_Score(local_player->score);
8147 case CA_SET_LEVEL_GEMS:
8149 local_player->gems_still_needed = action_arg_number_new;
8151 DrawGameValue_Emeralds(local_player->gems_still_needed);
8156 #if !USE_PLAYER_GRAVITY
8157 case CA_SET_LEVEL_GRAVITY:
8159 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8160 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8161 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8167 case CA_SET_LEVEL_WIND:
8169 game.wind_direction = action_arg_direction;
8174 /* ---------- player actions ------------------------------------------ */
8176 case CA_MOVE_PLAYER:
8178 /* automatically move to the next field in specified direction */
8179 for (i = 0; i < MAX_PLAYERS; i++)
8180 if (trigger_player_bits & (1 << i))
8181 stored_player[i].programmed_action = action_arg_direction;
8186 case CA_EXIT_PLAYER:
8188 for (i = 0; i < MAX_PLAYERS; i++)
8189 if (action_arg_player_bits & (1 << i))
8190 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8195 case CA_KILL_PLAYER:
8197 for (i = 0; i < MAX_PLAYERS; i++)
8198 if (action_arg_player_bits & (1 << i))
8199 KillPlayer(&stored_player[i]);
8204 case CA_SET_PLAYER_KEYS:
8206 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8207 int element = getSpecialActionElement(action_arg_element,
8208 action_arg_number, EL_KEY_1);
8210 if (IS_KEY(element))
8212 for (i = 0; i < MAX_PLAYERS; i++)
8214 if (trigger_player_bits & (1 << i))
8216 stored_player[i].key[KEY_NR(element)] = key_state;
8219 DrawGameDoorValues();
8221 DrawGameValue_Keys(stored_player[i].key);
8224 redraw_mask |= REDRAW_DOOR_1;
8232 case CA_SET_PLAYER_SPEED:
8234 for (i = 0; i < MAX_PLAYERS; i++)
8236 if (trigger_player_bits & (1 << i))
8238 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8240 if (action_arg == CA_ARG_SPEED_FASTER &&
8241 stored_player[i].cannot_move)
8243 action_arg_number = STEPSIZE_VERY_SLOW;
8245 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8246 action_arg == CA_ARG_SPEED_FASTER)
8248 action_arg_number = 2;
8249 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8252 else if (action_arg == CA_ARG_NUMBER_RESET)
8254 action_arg_number = level.initial_player_stepsize[i];
8258 getModifiedActionNumber(move_stepsize,
8261 action_arg_number_min,
8262 action_arg_number_max);
8265 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8267 /* make sure that value is power of 2 */
8268 move_stepsize = (1 << log_2(move_stepsize));
8270 /* do no immediately change -- the player might just be moving */
8271 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8273 stored_player[i].cannot_move =
8274 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8282 case CA_SET_PLAYER_SHIELD:
8284 for (i = 0; i < MAX_PLAYERS; i++)
8286 if (trigger_player_bits & (1 << i))
8288 if (action_arg == CA_ARG_SHIELD_OFF)
8290 stored_player[i].shield_normal_time_left = 0;
8291 stored_player[i].shield_deadly_time_left = 0;
8293 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8295 stored_player[i].shield_normal_time_left = 999999;
8297 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8299 stored_player[i].shield_normal_time_left = 999999;
8300 stored_player[i].shield_deadly_time_left = 999999;
8308 #if USE_PLAYER_GRAVITY
8309 case CA_SET_PLAYER_GRAVITY:
8311 for (i = 0; i < MAX_PLAYERS; i++)
8313 if (trigger_player_bits & (1 << i))
8315 stored_player[i].gravity =
8316 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8317 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8318 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8319 stored_player[i].gravity);
8327 case CA_SET_PLAYER_ARTWORK:
8329 for (i = 0; i < MAX_PLAYERS; i++)
8331 if (trigger_player_bits & (1 << i))
8333 int artwork_element = action_arg_element;
8335 if (action_arg == CA_ARG_ELEMENT_RESET)
8337 (level.use_artwork_element[i] ? level.artwork_element[i] :
8338 stored_player[i].element_nr);
8340 stored_player[i].artwork_element = artwork_element;
8342 SetPlayerWaiting(&stored_player[i], FALSE);
8344 /* set number of special actions for bored and sleeping animation */
8345 stored_player[i].num_special_action_bored =
8346 get_num_special_action(artwork_element,
8347 ACTION_BORING_1, ACTION_BORING_LAST);
8348 stored_player[i].num_special_action_sleeping =
8349 get_num_special_action(artwork_element,
8350 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8357 /* ---------- CE actions ---------------------------------------------- */
8359 case CA_SET_CE_VALUE:
8361 #if USE_NEW_CUSTOM_VALUE
8362 int last_ce_value = CustomValue[x][y];
8364 CustomValue[x][y] = action_arg_number_new;
8367 printf("::: CE value == %d\n", CustomValue[x][y]);
8370 if (CustomValue[x][y] != last_ce_value)
8372 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8373 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8375 if (CustomValue[x][y] == 0)
8378 printf("::: CE_VALUE_GETS_ZERO\n");
8381 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8382 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8385 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8395 case CA_SET_CE_SCORE:
8397 #if USE_NEW_CUSTOM_VALUE
8398 int last_ce_score = ei->collect_score;
8400 ei->collect_score = action_arg_number_new;
8403 printf("::: CE score == %d\n", ei->collect_score);
8406 if (ei->collect_score != last_ce_score)
8408 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8409 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8411 if (ei->collect_score == 0)
8414 printf("::: CE_SCORE_GETS_ZERO\n");
8417 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8418 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8421 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8431 /* ---------- engine actions ------------------------------------------ */
8433 case CA_SET_ENGINE_SCAN_MODE:
8435 InitPlayfieldScanMode(action_arg);
8445 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8447 int old_element = Feld[x][y];
8448 int new_element = get_element_from_group_element(element);
8449 int previous_move_direction = MovDir[x][y];
8450 #if USE_NEW_CUSTOM_VALUE
8451 int last_ce_value = CustomValue[x][y];
8453 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8454 boolean add_player_onto_element = (new_element_is_player &&
8455 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8456 IS_WALKABLE(old_element));
8459 /* check if element under the player changes from accessible to unaccessible
8460 (needed for special case of dropping element which then changes) */
8461 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8462 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8470 if (!add_player_onto_element)
8472 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8473 RemoveMovingField(x, y);
8477 Feld[x][y] = new_element;
8479 #if !USE_GFX_RESET_GFX_ANIMATION
8480 ResetGfxAnimation(x, y);
8481 ResetRandomAnimationValue(x, y);
8484 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8485 MovDir[x][y] = previous_move_direction;
8487 #if USE_NEW_CUSTOM_VALUE
8488 if (element_info[new_element].use_last_ce_value)
8489 CustomValue[x][y] = last_ce_value;
8492 InitField_WithBug1(x, y, FALSE);
8494 new_element = Feld[x][y]; /* element may have changed */
8496 #if USE_GFX_RESET_GFX_ANIMATION
8497 ResetGfxAnimation(x, y);
8498 ResetRandomAnimationValue(x, y);
8501 DrawLevelField(x, y);
8503 if (GFX_CRUMBLED(new_element))
8504 DrawLevelFieldCrumbledSandNeighbours(x, y);
8508 /* check if element under the player changes from accessible to unaccessible
8509 (needed for special case of dropping element which then changes) */
8510 /* (must be checked after creating new element for walkable group elements) */
8511 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8512 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8520 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8521 if (new_element_is_player)
8522 RelocatePlayer(x, y, new_element);
8525 ChangeCount[x][y]++; /* count number of changes in the same frame */
8527 TestIfBadThingTouchesPlayer(x, y);
8528 TestIfPlayerTouchesCustomElement(x, y);
8529 TestIfElementTouchesCustomElement(x, y);
8532 static void CreateField(int x, int y, int element)
8534 CreateFieldExt(x, y, element, FALSE);
8537 static void CreateElementFromChange(int x, int y, int element)
8539 element = GET_VALID_RUNTIME_ELEMENT(element);
8541 #if USE_STOP_CHANGED_ELEMENTS
8542 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8544 int old_element = Feld[x][y];
8546 /* prevent changed element from moving in same engine frame
8547 unless both old and new element can either fall or move */
8548 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8549 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8554 CreateFieldExt(x, y, element, TRUE);
8557 static boolean ChangeElement(int x, int y, int element, int page)
8559 struct ElementInfo *ei = &element_info[element];
8560 struct ElementChangeInfo *change = &ei->change_page[page];
8561 int ce_value = CustomValue[x][y];
8562 int ce_score = ei->collect_score;
8564 int old_element = Feld[x][y];
8566 /* always use default change event to prevent running into a loop */
8567 if (ChangeEvent[x][y] == -1)
8568 ChangeEvent[x][y] = CE_DELAY;
8570 if (ChangeEvent[x][y] == CE_DELAY)
8572 /* reset actual trigger element, trigger player and action element */
8573 change->actual_trigger_element = EL_EMPTY;
8574 change->actual_trigger_player = EL_PLAYER_1;
8575 change->actual_trigger_side = CH_SIDE_NONE;
8576 change->actual_trigger_ce_value = 0;
8577 change->actual_trigger_ce_score = 0;
8580 /* do not change elements more than a specified maximum number of changes */
8581 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8584 ChangeCount[x][y]++; /* count number of changes in the same frame */
8586 if (change->explode)
8593 if (change->use_target_content)
8595 boolean complete_replace = TRUE;
8596 boolean can_replace[3][3];
8599 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8602 boolean is_walkable;
8603 boolean is_diggable;
8604 boolean is_collectible;
8605 boolean is_removable;
8606 boolean is_destructible;
8607 int ex = x + xx - 1;
8608 int ey = y + yy - 1;
8609 int content_element = change->target_content.e[xx][yy];
8612 can_replace[xx][yy] = TRUE;
8614 if (ex == x && ey == y) /* do not check changing element itself */
8617 if (content_element == EL_EMPTY_SPACE)
8619 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8624 if (!IN_LEV_FIELD(ex, ey))
8626 can_replace[xx][yy] = FALSE;
8627 complete_replace = FALSE;
8634 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8635 e = MovingOrBlocked2Element(ex, ey);
8637 is_empty = (IS_FREE(ex, ey) ||
8638 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8640 is_walkable = (is_empty || IS_WALKABLE(e));
8641 is_diggable = (is_empty || IS_DIGGABLE(e));
8642 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8643 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8644 is_removable = (is_diggable || is_collectible);
8646 can_replace[xx][yy] =
8647 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8648 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8649 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8650 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8651 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8652 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8653 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8655 if (!can_replace[xx][yy])
8656 complete_replace = FALSE;
8659 if (!change->only_if_complete || complete_replace)
8661 boolean something_has_changed = FALSE;
8663 if (change->only_if_complete && change->use_random_replace &&
8664 RND(100) < change->random_percentage)
8667 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8669 int ex = x + xx - 1;
8670 int ey = y + yy - 1;
8671 int content_element;
8673 if (can_replace[xx][yy] && (!change->use_random_replace ||
8674 RND(100) < change->random_percentage))
8676 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8677 RemoveMovingField(ex, ey);
8679 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8681 content_element = change->target_content.e[xx][yy];
8682 target_element = GET_TARGET_ELEMENT(content_element, change,
8683 ce_value, ce_score);
8685 CreateElementFromChange(ex, ey, target_element);
8687 something_has_changed = TRUE;
8689 /* for symmetry reasons, freeze newly created border elements */
8690 if (ex != x || ey != y)
8691 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8695 if (something_has_changed)
8697 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8698 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8704 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8705 ce_value, ce_score);
8707 if (element == EL_DIAGONAL_GROWING ||
8708 element == EL_DIAGONAL_SHRINKING)
8710 target_element = Store[x][y];
8712 Store[x][y] = EL_EMPTY;
8715 CreateElementFromChange(x, y, target_element);
8717 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8718 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8721 /* this uses direct change before indirect change */
8722 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8727 #if USE_NEW_DELAYED_ACTION
8729 static void HandleElementChange(int x, int y, int page)
8731 int element = MovingOrBlocked2Element(x, y);
8732 struct ElementInfo *ei = &element_info[element];
8733 struct ElementChangeInfo *change = &ei->change_page[page];
8736 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8737 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8740 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8741 x, y, element, element_info[element].token_name);
8742 printf("HandleElementChange(): This should never happen!\n");
8747 /* this can happen with classic bombs on walkable, changing elements */
8748 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8751 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8752 ChangeDelay[x][y] = 0;
8758 if (ChangeDelay[x][y] == 0) /* initialize element change */
8760 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8762 if (change->can_change)
8764 ResetGfxAnimation(x, y);
8765 ResetRandomAnimationValue(x, y);
8767 if (change->pre_change_function)
8768 change->pre_change_function(x, y);
8772 ChangeDelay[x][y]--;
8774 if (ChangeDelay[x][y] != 0) /* continue element change */
8776 if (change->can_change)
8778 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8780 if (IS_ANIMATED(graphic))
8781 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8783 if (change->change_function)
8784 change->change_function(x, y);
8787 else /* finish element change */
8789 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8791 page = ChangePage[x][y];
8792 ChangePage[x][y] = -1;
8794 change = &ei->change_page[page];
8797 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8799 ChangeDelay[x][y] = 1; /* try change after next move step */
8800 ChangePage[x][y] = page; /* remember page to use for change */
8805 if (change->can_change)
8807 if (ChangeElement(x, y, element, page))
8809 if (change->post_change_function)
8810 change->post_change_function(x, y);
8814 if (change->has_action)
8815 ExecuteCustomElementAction(x, y, element, page);
8821 static void HandleElementChange(int x, int y, int page)
8823 int element = MovingOrBlocked2Element(x, y);
8824 struct ElementInfo *ei = &element_info[element];
8825 struct ElementChangeInfo *change = &ei->change_page[page];
8828 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8831 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8832 x, y, element, element_info[element].token_name);
8833 printf("HandleElementChange(): This should never happen!\n");
8838 /* this can happen with classic bombs on walkable, changing elements */
8839 if (!CAN_CHANGE(element))
8842 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8843 ChangeDelay[x][y] = 0;
8849 if (ChangeDelay[x][y] == 0) /* initialize element change */
8851 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8853 ResetGfxAnimation(x, y);
8854 ResetRandomAnimationValue(x, y);
8856 if (change->pre_change_function)
8857 change->pre_change_function(x, y);
8860 ChangeDelay[x][y]--;
8862 if (ChangeDelay[x][y] != 0) /* continue element change */
8864 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8866 if (IS_ANIMATED(graphic))
8867 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8869 if (change->change_function)
8870 change->change_function(x, y);
8872 else /* finish element change */
8874 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8876 page = ChangePage[x][y];
8877 ChangePage[x][y] = -1;
8879 change = &ei->change_page[page];
8882 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8884 ChangeDelay[x][y] = 1; /* try change after next move step */
8885 ChangePage[x][y] = page; /* remember page to use for change */
8890 if (ChangeElement(x, y, element, page))
8892 if (change->post_change_function)
8893 change->post_change_function(x, y);
8900 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8901 int trigger_element,
8907 boolean change_done_any = FALSE;
8908 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8911 if (!(trigger_events[trigger_element][trigger_event]))
8914 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8916 int element = EL_CUSTOM_START + i;
8917 boolean change_done = FALSE;
8920 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8921 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8924 for (p = 0; p < element_info[element].num_change_pages; p++)
8926 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8928 if (change->can_change_or_has_action &&
8929 change->has_event[trigger_event] &&
8930 change->trigger_side & trigger_side &&
8931 change->trigger_player & trigger_player &&
8932 change->trigger_page & trigger_page_bits &&
8933 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8935 change->actual_trigger_element = trigger_element;
8936 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8937 change->actual_trigger_side = trigger_side;
8938 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8939 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8941 if ((change->can_change && !change_done) || change->has_action)
8946 SCAN_PLAYFIELD(x, y)
8948 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8951 if (Feld[x][y] == element)
8953 if (change->can_change && !change_done)
8955 ChangeDelay[x][y] = 1;
8956 ChangeEvent[x][y] = trigger_event;
8958 HandleElementChange(x, y, p);
8960 #if USE_NEW_DELAYED_ACTION
8961 else if (change->has_action)
8963 ExecuteCustomElementAction(x, y, element, p);
8964 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8967 if (change->has_action)
8969 ExecuteCustomElementAction(x, y, element, p);
8970 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8976 if (change->can_change)
8979 change_done_any = TRUE;
8986 return change_done_any;
8989 static boolean CheckElementChangeExt(int x, int y,
8991 int trigger_element,
8996 boolean change_done = FALSE;
8999 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9000 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9003 if (Feld[x][y] == EL_BLOCKED)
9005 Blocked2Moving(x, y, &x, &y);
9006 element = Feld[x][y];
9010 /* check if element has already changed */
9011 if (Feld[x][y] != element)
9014 /* check if element has already changed or is about to change after moving */
9015 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9016 Feld[x][y] != element) ||
9018 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9019 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9020 ChangePage[x][y] != -1)))
9024 for (p = 0; p < element_info[element].num_change_pages; p++)
9026 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9028 boolean check_trigger_element =
9029 (trigger_event == CE_TOUCHING_X ||
9030 trigger_event == CE_HITTING_X ||
9031 trigger_event == CE_HIT_BY_X);
9033 if (change->can_change_or_has_action &&
9034 change->has_event[trigger_event] &&
9035 change->trigger_side & trigger_side &&
9036 change->trigger_player & trigger_player &&
9037 (!check_trigger_element ||
9038 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9040 change->actual_trigger_element = trigger_element;
9041 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9042 change->actual_trigger_side = trigger_side;
9043 change->actual_trigger_ce_value = CustomValue[x][y];
9044 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9046 /* special case: trigger element not at (x,y) position for some events */
9047 if (check_trigger_element)
9059 { 0, 0 }, { 0, 0 }, { 0, 0 },
9063 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9064 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9066 change->actual_trigger_ce_value = CustomValue[xx][yy];
9067 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9070 if (change->can_change && !change_done)
9072 ChangeDelay[x][y] = 1;
9073 ChangeEvent[x][y] = trigger_event;
9075 HandleElementChange(x, y, p);
9079 #if USE_NEW_DELAYED_ACTION
9080 else if (change->has_action)
9082 ExecuteCustomElementAction(x, y, element, p);
9083 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9086 if (change->has_action)
9088 ExecuteCustomElementAction(x, y, element, p);
9089 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9098 static void PlayPlayerSound(struct PlayerInfo *player)
9100 int jx = player->jx, jy = player->jy;
9101 int sound_element = player->artwork_element;
9102 int last_action = player->last_action_waiting;
9103 int action = player->action_waiting;
9105 if (player->is_waiting)
9107 if (action != last_action)
9108 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9110 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9114 if (action != last_action)
9115 StopSound(element_info[sound_element].sound[last_action]);
9117 if (last_action == ACTION_SLEEPING)
9118 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9122 static void PlayAllPlayersSound()
9126 for (i = 0; i < MAX_PLAYERS; i++)
9127 if (stored_player[i].active)
9128 PlayPlayerSound(&stored_player[i]);
9131 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9133 boolean last_waiting = player->is_waiting;
9134 int move_dir = player->MovDir;
9136 player->dir_waiting = move_dir;
9137 player->last_action_waiting = player->action_waiting;
9141 if (!last_waiting) /* not waiting -> waiting */
9143 player->is_waiting = TRUE;
9145 player->frame_counter_bored =
9147 game.player_boring_delay_fixed +
9148 SimpleRND(game.player_boring_delay_random);
9149 player->frame_counter_sleeping =
9151 game.player_sleeping_delay_fixed +
9152 SimpleRND(game.player_sleeping_delay_random);
9155 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9157 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9161 if (game.player_sleeping_delay_fixed +
9162 game.player_sleeping_delay_random > 0 &&
9163 player->anim_delay_counter == 0 &&
9164 player->post_delay_counter == 0 &&
9165 FrameCounter >= player->frame_counter_sleeping)
9166 player->is_sleeping = TRUE;
9167 else if (game.player_boring_delay_fixed +
9168 game.player_boring_delay_random > 0 &&
9169 FrameCounter >= player->frame_counter_bored)
9170 player->is_bored = TRUE;
9172 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9173 player->is_bored ? ACTION_BORING :
9177 if (player->is_sleeping && player->use_murphy)
9179 /* special case for sleeping Murphy when leaning against non-free tile */
9181 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9182 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9183 !IS_MOVING(player->jx - 1, player->jy)))
9185 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9186 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9187 !IS_MOVING(player->jx + 1, player->jy)))
9188 move_dir = MV_RIGHT;
9190 player->is_sleeping = FALSE;
9192 player->dir_waiting = move_dir;
9196 if (player->is_sleeping)
9198 if (player->num_special_action_sleeping > 0)
9200 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9202 int last_special_action = player->special_action_sleeping;
9203 int num_special_action = player->num_special_action_sleeping;
9204 int special_action =
9205 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9206 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9207 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9208 last_special_action + 1 : ACTION_SLEEPING);
9209 int special_graphic =
9210 el_act_dir2img(player->artwork_element, special_action, move_dir);
9212 player->anim_delay_counter =
9213 graphic_info[special_graphic].anim_delay_fixed +
9214 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9215 player->post_delay_counter =
9216 graphic_info[special_graphic].post_delay_fixed +
9217 SimpleRND(graphic_info[special_graphic].post_delay_random);
9219 player->special_action_sleeping = special_action;
9222 if (player->anim_delay_counter > 0)
9224 player->action_waiting = player->special_action_sleeping;
9225 player->anim_delay_counter--;
9227 else if (player->post_delay_counter > 0)
9229 player->post_delay_counter--;
9233 else if (player->is_bored)
9235 if (player->num_special_action_bored > 0)
9237 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9239 int special_action =
9240 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9241 int special_graphic =
9242 el_act_dir2img(player->artwork_element, special_action, move_dir);
9244 player->anim_delay_counter =
9245 graphic_info[special_graphic].anim_delay_fixed +
9246 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9247 player->post_delay_counter =
9248 graphic_info[special_graphic].post_delay_fixed +
9249 SimpleRND(graphic_info[special_graphic].post_delay_random);
9251 player->special_action_bored = special_action;
9254 if (player->anim_delay_counter > 0)
9256 player->action_waiting = player->special_action_bored;
9257 player->anim_delay_counter--;
9259 else if (player->post_delay_counter > 0)
9261 player->post_delay_counter--;
9266 else if (last_waiting) /* waiting -> not waiting */
9268 player->is_waiting = FALSE;
9269 player->is_bored = FALSE;
9270 player->is_sleeping = FALSE;
9272 player->frame_counter_bored = -1;
9273 player->frame_counter_sleeping = -1;
9275 player->anim_delay_counter = 0;
9276 player->post_delay_counter = 0;
9278 player->dir_waiting = player->MovDir;
9279 player->action_waiting = ACTION_DEFAULT;
9281 player->special_action_bored = ACTION_DEFAULT;
9282 player->special_action_sleeping = ACTION_DEFAULT;
9286 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9288 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9289 int left = player_action & JOY_LEFT;
9290 int right = player_action & JOY_RIGHT;
9291 int up = player_action & JOY_UP;
9292 int down = player_action & JOY_DOWN;
9293 int button1 = player_action & JOY_BUTTON_1;
9294 int button2 = player_action & JOY_BUTTON_2;
9295 int dx = (left ? -1 : right ? 1 : 0);
9296 int dy = (up ? -1 : down ? 1 : 0);
9298 if (!player->active || tape.pausing)
9304 snapped = SnapField(player, dx, dy);
9308 dropped = DropElement(player);
9310 moved = MovePlayer(player, dx, dy);
9313 if (tape.single_step && tape.recording && !tape.pausing)
9315 if (button1 || (dropped && !moved))
9317 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9318 SnapField(player, 0, 0); /* stop snapping */
9322 SetPlayerWaiting(player, FALSE);
9324 return player_action;
9328 /* no actions for this player (no input at player's configured device) */
9330 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9331 SnapField(player, 0, 0);
9332 CheckGravityMovementWhenNotMoving(player);
9334 if (player->MovPos == 0)
9335 SetPlayerWaiting(player, TRUE);
9337 if (player->MovPos == 0) /* needed for tape.playing */
9338 player->is_moving = FALSE;
9340 player->is_dropping = FALSE;
9341 player->is_dropping_pressed = FALSE;
9342 player->drop_pressed_delay = 0;
9348 static void CheckLevelTime()
9352 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9354 if (level.native_em_level->lev->home == 0) /* all players at home */
9356 local_player->LevelSolved = TRUE;
9357 AllPlayersGone = TRUE;
9359 level.native_em_level->lev->home = -1;
9362 if (level.native_em_level->ply[0]->alive == 0 &&
9363 level.native_em_level->ply[1]->alive == 0 &&
9364 level.native_em_level->ply[2]->alive == 0 &&
9365 level.native_em_level->ply[3]->alive == 0) /* all dead */
9366 AllPlayersGone = TRUE;
9369 if (TimeFrames >= FRAMES_PER_SECOND)
9374 for (i = 0; i < MAX_PLAYERS; i++)
9376 struct PlayerInfo *player = &stored_player[i];
9378 if (SHIELD_ON(player))
9380 player->shield_normal_time_left--;
9382 if (player->shield_deadly_time_left > 0)
9383 player->shield_deadly_time_left--;
9387 if (!level.use_step_counter)
9395 if (TimeLeft <= 10 && setup.time_limit)
9396 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9398 DrawGameValue_Time(TimeLeft);
9400 if (!TimeLeft && setup.time_limit)
9402 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9403 level.native_em_level->lev->killed_out_of_time = TRUE;
9405 for (i = 0; i < MAX_PLAYERS; i++)
9406 KillPlayer(&stored_player[i]);
9409 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9410 DrawGameValue_Time(TimePlayed);
9412 level.native_em_level->lev->time =
9413 (level.time == 0 ? TimePlayed : TimeLeft);
9416 if (tape.recording || tape.playing)
9417 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9421 void AdvanceFrameAndPlayerCounters(int player_nr)
9426 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9427 FrameCounter, FrameCounter + 1);
9430 /* advance frame counters (global frame counter and time frame counter) */
9434 /* advance player counters (counters for move delay, move animation etc.) */
9435 for (i = 0; i < MAX_PLAYERS; i++)
9437 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9438 int move_delay_value = stored_player[i].move_delay_value;
9439 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9441 if (!advance_player_counters) /* not all players may be affected */
9444 #if USE_NEW_PLAYER_ANIM
9445 if (move_frames == 0) /* less than one move per game frame */
9447 int stepsize = TILEX / move_delay_value;
9448 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9449 int count = (stored_player[i].is_moving ?
9450 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9452 if (count % delay == 0)
9457 stored_player[i].Frame += move_frames;
9459 if (stored_player[i].MovPos != 0)
9460 stored_player[i].StepFrame += move_frames;
9462 if (stored_player[i].move_delay > 0)
9463 stored_player[i].move_delay--;
9465 /* due to bugs in previous versions, counter must count up, not down */
9466 if (stored_player[i].push_delay != -1)
9467 stored_player[i].push_delay++;
9469 if (stored_player[i].drop_delay > 0)
9470 stored_player[i].drop_delay--;
9472 if (stored_player[i].is_dropping_pressed)
9473 stored_player[i].drop_pressed_delay++;
9477 void StartGameActions(boolean init_network_game, boolean record_tape,
9480 unsigned long new_random_seed = InitRND(random_seed);
9483 TapeStartRecording(new_random_seed);
9485 #if defined(NETWORK_AVALIABLE)
9486 if (init_network_game)
9488 SendToServer_StartPlaying();
9496 game_status = GAME_MODE_PLAYING;
9503 static unsigned long game_frame_delay = 0;
9504 unsigned long game_frame_delay_value;
9505 byte *recorded_player_action;
9506 byte summarized_player_action = 0;
9507 byte tape_action[MAX_PLAYERS];
9510 if (game.restart_level)
9511 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9513 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9515 if (level.native_em_level->lev->home == 0) /* all players at home */
9517 local_player->LevelSolved = TRUE;
9518 AllPlayersGone = TRUE;
9520 level.native_em_level->lev->home = -1;
9523 if (level.native_em_level->ply[0]->alive == 0 &&
9524 level.native_em_level->ply[1]->alive == 0 &&
9525 level.native_em_level->ply[2]->alive == 0 &&
9526 level.native_em_level->ply[3]->alive == 0) /* all dead */
9527 AllPlayersGone = TRUE;
9530 if (local_player->LevelSolved)
9533 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9536 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9539 game_frame_delay_value =
9540 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9542 if (tape.playing && tape.warp_forward && !tape.pausing)
9543 game_frame_delay_value = 0;
9545 /* ---------- main game synchronization point ---------- */
9547 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9549 if (network_playing && !network_player_action_received)
9551 /* try to get network player actions in time */
9553 #if defined(NETWORK_AVALIABLE)
9554 /* last chance to get network player actions without main loop delay */
9558 /* game was quit by network peer */
9559 if (game_status != GAME_MODE_PLAYING)
9562 if (!network_player_action_received)
9563 return; /* failed to get network player actions in time */
9565 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9571 /* at this point we know that we really continue executing the game */
9574 network_player_action_received = FALSE;
9577 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9579 if (tape.set_centered_player)
9581 game.centered_player_nr_next = tape.centered_player_nr_next;
9582 game.set_centered_player = TRUE;
9585 for (i = 0; i < MAX_PLAYERS; i++)
9587 summarized_player_action |= stored_player[i].action;
9589 if (!network_playing)
9590 stored_player[i].effective_action = stored_player[i].action;
9593 #if defined(NETWORK_AVALIABLE)
9594 if (network_playing)
9595 SendToServer_MovePlayer(summarized_player_action);
9598 if (!options.network && !setup.team_mode)
9599 local_player->effective_action = summarized_player_action;
9601 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9603 for (i = 0; i < MAX_PLAYERS; i++)
9604 stored_player[i].effective_action =
9605 (i == game.centered_player_nr ? summarized_player_action : 0);
9608 if (recorded_player_action != NULL)
9609 for (i = 0; i < MAX_PLAYERS; i++)
9610 stored_player[i].effective_action = recorded_player_action[i];
9612 for (i = 0; i < MAX_PLAYERS; i++)
9614 tape_action[i] = stored_player[i].effective_action;
9616 /* (this can only happen in the R'n'D game engine) */
9617 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9618 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9621 /* only record actions from input devices, but not programmed actions */
9623 TapeRecordAction(tape_action);
9625 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9627 GameActions_EM_Main();
9635 void GameActions_EM_Main()
9637 byte effective_action[MAX_PLAYERS];
9638 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9641 for (i = 0; i < MAX_PLAYERS; i++)
9642 effective_action[i] = stored_player[i].effective_action;
9644 GameActions_EM(effective_action, warp_mode);
9648 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9651 void GameActions_RND()
9653 int magic_wall_x = 0, magic_wall_y = 0;
9654 int i, x, y, element, graphic;
9656 InitPlayfieldScanModeVars();
9658 #if USE_ONE_MORE_CHANGE_PER_FRAME
9659 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9661 SCAN_PLAYFIELD(x, y)
9663 ChangeCount[x][y] = 0;
9664 ChangeEvent[x][y] = -1;
9670 if (game.set_centered_player)
9672 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9674 /* switching to "all players" only possible if all players fit to screen */
9675 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9677 game.centered_player_nr_next = game.centered_player_nr;
9678 game.set_centered_player = FALSE;
9681 /* do not switch focus to non-existing (or non-active) player */
9682 if (game.centered_player_nr_next >= 0 &&
9683 !stored_player[game.centered_player_nr_next].active)
9685 game.centered_player_nr_next = game.centered_player_nr;
9686 game.set_centered_player = FALSE;
9690 if (game.set_centered_player &&
9691 ScreenMovPos == 0) /* screen currently aligned at tile position */
9695 if (game.centered_player_nr_next == -1)
9697 setScreenCenteredToAllPlayers(&sx, &sy);
9701 sx = stored_player[game.centered_player_nr_next].jx;
9702 sy = stored_player[game.centered_player_nr_next].jy;
9705 game.centered_player_nr = game.centered_player_nr_next;
9706 game.set_centered_player = FALSE;
9708 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9709 DrawGameDoorValues();
9713 for (i = 0; i < MAX_PLAYERS; i++)
9715 int actual_player_action = stored_player[i].effective_action;
9718 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9719 - rnd_equinox_tetrachloride 048
9720 - rnd_equinox_tetrachloride_ii 096
9721 - rnd_emanuel_schmieg 002
9722 - doctor_sloan_ww 001, 020
9724 if (stored_player[i].MovPos == 0)
9725 CheckGravityMovement(&stored_player[i]);
9728 /* overwrite programmed action with tape action */
9729 if (stored_player[i].programmed_action)
9730 actual_player_action = stored_player[i].programmed_action;
9733 PlayerActions(&stored_player[i], actual_player_action);
9735 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9737 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9738 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9741 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9745 network_player_action_received = FALSE;
9748 ScrollScreen(NULL, SCROLL_GO_ON);
9750 /* for backwards compatibility, the following code emulates a fixed bug that
9751 occured when pushing elements (causing elements that just made their last
9752 pushing step to already (if possible) make their first falling step in the
9753 same game frame, which is bad); this code is also needed to use the famous
9754 "spring push bug" which is used in older levels and might be wanted to be
9755 used also in newer levels, but in this case the buggy pushing code is only
9756 affecting the "spring" element and no other elements */
9758 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9760 for (i = 0; i < MAX_PLAYERS; i++)
9762 struct PlayerInfo *player = &stored_player[i];
9766 if (player->active && player->is_pushing && player->is_moving &&
9768 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9769 Feld[x][y] == EL_SPRING))
9771 ContinueMoving(x, y);
9773 /* continue moving after pushing (this is actually a bug) */
9774 if (!IS_MOVING(x, y))
9783 SCAN_PLAYFIELD(x, y)
9785 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9788 ChangeCount[x][y] = 0;
9789 ChangeEvent[x][y] = -1;
9791 /* this must be handled before main playfield loop */
9792 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9795 if (MovDelay[x][y] <= 0)
9799 #if USE_NEW_SNAP_DELAY
9800 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9803 if (MovDelay[x][y] <= 0)
9806 DrawLevelField(x, y);
9808 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9814 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9816 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9817 printf("GameActions(): This should never happen!\n");
9819 ChangePage[x][y] = -1;
9824 if (WasJustMoving[x][y] > 0)
9825 WasJustMoving[x][y]--;
9826 if (WasJustFalling[x][y] > 0)
9827 WasJustFalling[x][y]--;
9828 if (CheckCollision[x][y] > 0)
9829 CheckCollision[x][y]--;
9833 /* reset finished pushing action (not done in ContinueMoving() to allow
9834 continuous pushing animation for elements with zero push delay) */
9835 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9837 ResetGfxAnimation(x, y);
9838 DrawLevelField(x, y);
9842 if (IS_BLOCKED(x, y))
9846 Blocked2Moving(x, y, &oldx, &oldy);
9847 if (!IS_MOVING(oldx, oldy))
9849 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9850 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9851 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9852 printf("GameActions(): This should never happen!\n");
9859 SCAN_PLAYFIELD(x, y)
9861 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9864 element = Feld[x][y];
9865 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9868 printf("::: %d,%d\n", x, y);
9870 if (element == EL_ROCK)
9871 printf("::: Yo man! Rocks can fall!\n");
9875 ResetGfxFrame(x, y, TRUE);
9877 if (graphic_info[graphic].anim_global_sync)
9878 GfxFrame[x][y] = FrameCounter;
9879 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9881 int old_gfx_frame = GfxFrame[x][y];
9883 GfxFrame[x][y] = CustomValue[x][y];
9886 if (GfxFrame[x][y] != old_gfx_frame)
9888 DrawLevelGraphicAnimation(x, y, graphic);
9890 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9892 int old_gfx_frame = GfxFrame[x][y];
9894 GfxFrame[x][y] = element_info[element].collect_score;
9897 if (GfxFrame[x][y] != old_gfx_frame)
9899 DrawLevelGraphicAnimation(x, y, graphic);
9901 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9903 int old_gfx_frame = GfxFrame[x][y];
9905 GfxFrame[x][y] = ChangeDelay[x][y];
9908 if (GfxFrame[x][y] != old_gfx_frame)
9910 DrawLevelGraphicAnimation(x, y, graphic);
9914 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9915 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9916 ResetRandomAnimationValue(x, y);
9918 SetRandomAnimationValue(x, y);
9920 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9922 if (IS_INACTIVE(element))
9924 if (IS_ANIMATED(graphic))
9925 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9930 /* this may take place after moving, so 'element' may have changed */
9931 if (IS_CHANGING(x, y) &&
9932 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9934 int page = element_info[element].event_page_nr[CE_DELAY];
9936 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9940 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9944 if (element == EL_CUSTOM_255)
9945 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9949 HandleElementChange(x, y, page);
9951 if (CAN_CHANGE(element))
9952 HandleElementChange(x, y, page);
9954 if (HAS_ACTION(element))
9955 ExecuteCustomElementAction(x, y, element, page);
9960 element = Feld[x][y];
9961 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9964 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9968 element = Feld[x][y];
9969 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9971 if (IS_ANIMATED(graphic) &&
9974 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9976 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9977 EdelsteinFunkeln(x, y);
9979 else if ((element == EL_ACID ||
9980 element == EL_EXIT_OPEN ||
9981 element == EL_SP_EXIT_OPEN ||
9982 element == EL_SP_TERMINAL ||
9983 element == EL_SP_TERMINAL_ACTIVE ||
9984 element == EL_EXTRA_TIME ||
9985 element == EL_SHIELD_NORMAL ||
9986 element == EL_SHIELD_DEADLY) &&
9987 IS_ANIMATED(graphic))
9988 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9989 else if (IS_MOVING(x, y))
9990 ContinueMoving(x, y);
9991 else if (IS_ACTIVE_BOMB(element))
9992 CheckDynamite(x, y);
9993 else if (element == EL_AMOEBA_GROWING)
9994 AmoebeWaechst(x, y);
9995 else if (element == EL_AMOEBA_SHRINKING)
9996 AmoebaDisappearing(x, y);
9998 #if !USE_NEW_AMOEBA_CODE
9999 else if (IS_AMOEBALIVE(element))
10000 AmoebeAbleger(x, y);
10003 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10005 else if (element == EL_EXIT_CLOSED)
10007 else if (element == EL_SP_EXIT_CLOSED)
10009 else if (element == EL_EXPANDABLE_WALL_GROWING)
10010 MauerWaechst(x, y);
10011 else if (element == EL_EXPANDABLE_WALL ||
10012 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10013 element == EL_EXPANDABLE_WALL_VERTICAL ||
10014 element == EL_EXPANDABLE_WALL_ANY)
10015 MauerAbleger(x, y);
10016 else if (element == EL_FLAMES)
10017 CheckForDragon(x, y);
10018 else if (element == EL_EXPLOSION)
10019 ; /* drawing of correct explosion animation is handled separately */
10020 else if (element == EL_ELEMENT_SNAPPING ||
10021 element == EL_DIAGONAL_SHRINKING ||
10022 element == EL_DIAGONAL_GROWING)
10025 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10027 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10030 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10031 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10034 if (element == EL_CUSTOM_255 ||
10035 element == EL_CUSTOM_256)
10036 DrawLevelGraphicAnimation(x, y, graphic);
10039 if (IS_BELT_ACTIVE(element))
10040 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10042 if (game.magic_wall_active)
10044 int jx = local_player->jx, jy = local_player->jy;
10046 /* play the element sound at the position nearest to the player */
10047 if ((element == EL_MAGIC_WALL_FULL ||
10048 element == EL_MAGIC_WALL_ACTIVE ||
10049 element == EL_MAGIC_WALL_EMPTYING ||
10050 element == EL_BD_MAGIC_WALL_FULL ||
10051 element == EL_BD_MAGIC_WALL_ACTIVE ||
10052 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10053 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10061 #if USE_NEW_AMOEBA_CODE
10062 /* new experimental amoeba growth stuff */
10063 if (!(FrameCounter % 8))
10065 static unsigned long random = 1684108901;
10067 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10069 x = RND(lev_fieldx);
10070 y = RND(lev_fieldy);
10071 element = Feld[x][y];
10073 if (!IS_PLAYER(x,y) &&
10074 (element == EL_EMPTY ||
10075 CAN_GROW_INTO(element) ||
10076 element == EL_QUICKSAND_EMPTY ||
10077 element == EL_ACID_SPLASH_LEFT ||
10078 element == EL_ACID_SPLASH_RIGHT))
10080 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10081 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10082 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10083 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10084 Feld[x][y] = EL_AMOEBA_DROP;
10087 random = random * 129 + 1;
10093 if (game.explosions_delayed)
10096 game.explosions_delayed = FALSE;
10099 SCAN_PLAYFIELD(x, y)
10101 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10104 element = Feld[x][y];
10106 if (ExplodeField[x][y])
10107 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10108 else if (element == EL_EXPLOSION)
10109 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10111 ExplodeField[x][y] = EX_TYPE_NONE;
10114 game.explosions_delayed = TRUE;
10117 if (game.magic_wall_active)
10119 if (!(game.magic_wall_time_left % 4))
10121 int element = Feld[magic_wall_x][magic_wall_y];
10123 if (element == EL_BD_MAGIC_WALL_FULL ||
10124 element == EL_BD_MAGIC_WALL_ACTIVE ||
10125 element == EL_BD_MAGIC_WALL_EMPTYING)
10126 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10128 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10131 if (game.magic_wall_time_left > 0)
10133 game.magic_wall_time_left--;
10134 if (!game.magic_wall_time_left)
10137 SCAN_PLAYFIELD(x, y)
10139 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10142 element = Feld[x][y];
10144 if (element == EL_MAGIC_WALL_ACTIVE ||
10145 element == EL_MAGIC_WALL_FULL)
10147 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10148 DrawLevelField(x, y);
10150 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10151 element == EL_BD_MAGIC_WALL_FULL)
10153 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10154 DrawLevelField(x, y);
10158 game.magic_wall_active = FALSE;
10163 if (game.light_time_left > 0)
10165 game.light_time_left--;
10167 if (game.light_time_left == 0)
10168 RedrawAllLightSwitchesAndInvisibleElements();
10171 if (game.timegate_time_left > 0)
10173 game.timegate_time_left--;
10175 if (game.timegate_time_left == 0)
10176 CloseAllOpenTimegates();
10179 if (game.lenses_time_left > 0)
10181 game.lenses_time_left--;
10183 if (game.lenses_time_left == 0)
10184 RedrawAllInvisibleElementsForLenses();
10187 if (game.magnify_time_left > 0)
10189 game.magnify_time_left--;
10191 if (game.magnify_time_left == 0)
10192 RedrawAllInvisibleElementsForMagnifier();
10195 for (i = 0; i < MAX_PLAYERS; i++)
10197 struct PlayerInfo *player = &stored_player[i];
10199 if (SHIELD_ON(player))
10201 if (player->shield_deadly_time_left)
10202 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10203 else if (player->shield_normal_time_left)
10204 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10211 PlayAllPlayersSound();
10213 if (options.debug) /* calculate frames per second */
10215 static unsigned long fps_counter = 0;
10216 static int fps_frames = 0;
10217 unsigned long fps_delay_ms = Counter() - fps_counter;
10221 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10223 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10226 fps_counter = Counter();
10229 redraw_mask |= REDRAW_FPS;
10232 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10234 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10236 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10238 local_player->show_envelope = 0;
10241 /* use random number generator in every frame to make it less predictable */
10242 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10246 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10248 int min_x = x, min_y = y, max_x = x, max_y = y;
10251 for (i = 0; i < MAX_PLAYERS; i++)
10253 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10255 if (!stored_player[i].active || &stored_player[i] == player)
10258 min_x = MIN(min_x, jx);
10259 min_y = MIN(min_y, jy);
10260 max_x = MAX(max_x, jx);
10261 max_y = MAX(max_y, jy);
10264 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10267 static boolean AllPlayersInVisibleScreen()
10271 for (i = 0; i < MAX_PLAYERS; i++)
10273 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10275 if (!stored_player[i].active)
10278 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10285 void ScrollLevel(int dx, int dy)
10287 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10290 BlitBitmap(drawto_field, drawto_field,
10291 FX + TILEX * (dx == -1) - softscroll_offset,
10292 FY + TILEY * (dy == -1) - softscroll_offset,
10293 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10294 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10295 FX + TILEX * (dx == 1) - softscroll_offset,
10296 FY + TILEY * (dy == 1) - softscroll_offset);
10300 x = (dx == 1 ? BX1 : BX2);
10301 for (y = BY1; y <= BY2; y++)
10302 DrawScreenField(x, y);
10307 y = (dy == 1 ? BY1 : BY2);
10308 for (x = BX1; x <= BX2; x++)
10309 DrawScreenField(x, y);
10312 redraw_mask |= REDRAW_FIELD;
10315 static boolean canFallDown(struct PlayerInfo *player)
10317 int jx = player->jx, jy = player->jy;
10319 return (IN_LEV_FIELD(jx, jy + 1) &&
10320 (IS_FREE(jx, jy + 1) ||
10321 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10322 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10323 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10326 static boolean canPassField(int x, int y, int move_dir)
10328 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10329 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10330 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10331 int nextx = x + dx;
10332 int nexty = y + dy;
10333 int element = Feld[x][y];
10335 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10336 !CAN_MOVE(element) &&
10337 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10338 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10339 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10342 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10344 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10345 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10346 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10350 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10351 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10352 (IS_DIGGABLE(Feld[newx][newy]) ||
10353 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10354 canPassField(newx, newy, move_dir)));
10357 static void CheckGravityMovement(struct PlayerInfo *player)
10359 #if USE_PLAYER_GRAVITY
10360 if (player->gravity && !player->programmed_action)
10362 if (game.gravity && !player->programmed_action)
10365 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10366 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10367 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10368 int jx = player->jx, jy = player->jy;
10369 boolean player_is_moving_to_valid_field =
10370 (!player_is_snapping &&
10371 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10372 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10373 boolean player_can_fall_down = canFallDown(player);
10375 if (player_can_fall_down &&
10376 !player_is_moving_to_valid_field)
10377 player->programmed_action = MV_DOWN;
10381 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10383 return CheckGravityMovement(player);
10385 #if USE_PLAYER_GRAVITY
10386 if (player->gravity && !player->programmed_action)
10388 if (game.gravity && !player->programmed_action)
10391 int jx = player->jx, jy = player->jy;
10392 boolean field_under_player_is_free =
10393 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10394 boolean player_is_standing_on_valid_field =
10395 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10396 (IS_WALKABLE(Feld[jx][jy]) &&
10397 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10399 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10400 player->programmed_action = MV_DOWN;
10405 MovePlayerOneStep()
10406 -----------------------------------------------------------------------------
10407 dx, dy: direction (non-diagonal) to try to move the player to
10408 real_dx, real_dy: direction as read from input device (can be diagonal)
10411 boolean MovePlayerOneStep(struct PlayerInfo *player,
10412 int dx, int dy, int real_dx, int real_dy)
10414 int jx = player->jx, jy = player->jy;
10415 int new_jx = jx + dx, new_jy = jy + dy;
10416 #if !USE_FIXED_DONT_RUN_INTO
10420 boolean player_can_move = !player->cannot_move;
10422 if (!player->active || (!dx && !dy))
10423 return MP_NO_ACTION;
10425 player->MovDir = (dx < 0 ? MV_LEFT :
10426 dx > 0 ? MV_RIGHT :
10428 dy > 0 ? MV_DOWN : MV_NONE);
10430 if (!IN_LEV_FIELD(new_jx, new_jy))
10431 return MP_NO_ACTION;
10433 if (!player_can_move)
10436 if (player->MovPos == 0)
10438 player->is_moving = FALSE;
10439 player->is_digging = FALSE;
10440 player->is_collecting = FALSE;
10441 player->is_snapping = FALSE;
10442 player->is_pushing = FALSE;
10445 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10446 SnapField(player, 0, 0);
10450 return MP_NO_ACTION;
10455 if (!options.network && game.centered_player_nr == -1 &&
10456 !AllPlayersInSight(player, new_jx, new_jy))
10457 return MP_NO_ACTION;
10459 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10460 return MP_NO_ACTION;
10463 #if !USE_FIXED_DONT_RUN_INTO
10464 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10466 /* (moved to DigField()) */
10467 if (player_can_move && DONT_RUN_INTO(element))
10469 if (element == EL_ACID && dx == 0 && dy == 1)
10471 SplashAcid(new_jx, new_jy);
10472 Feld[jx][jy] = EL_PLAYER_1;
10473 InitMovingField(jx, jy, MV_DOWN);
10474 Store[jx][jy] = EL_ACID;
10475 ContinueMoving(jx, jy);
10476 BuryPlayer(player);
10479 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10485 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10487 #if USE_FIXED_DONT_RUN_INTO
10488 if (can_move == MP_DONT_RUN_INTO)
10492 if (can_move != MP_MOVING)
10495 #if USE_FIXED_DONT_RUN_INTO
10498 /* check if DigField() has caused relocation of the player */
10499 if (player->jx != jx || player->jy != jy)
10500 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10502 StorePlayer[jx][jy] = 0;
10503 player->last_jx = jx;
10504 player->last_jy = jy;
10505 player->jx = new_jx;
10506 player->jy = new_jy;
10507 StorePlayer[new_jx][new_jy] = player->element_nr;
10509 if (player->move_delay_value_next != -1)
10511 player->move_delay_value = player->move_delay_value_next;
10512 player->move_delay_value_next = -1;
10516 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10518 player->step_counter++;
10520 PlayerVisit[jx][jy] = FrameCounter;
10522 ScrollPlayer(player, SCROLL_INIT);
10527 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10529 int jx = player->jx, jy = player->jy;
10530 int old_jx = jx, old_jy = jy;
10531 int moved = MP_NO_ACTION;
10533 if (!player->active)
10538 if (player->MovPos == 0)
10540 player->is_moving = FALSE;
10541 player->is_digging = FALSE;
10542 player->is_collecting = FALSE;
10543 player->is_snapping = FALSE;
10544 player->is_pushing = FALSE;
10550 if (player->move_delay > 0)
10553 player->move_delay = -1; /* set to "uninitialized" value */
10555 /* store if player is automatically moved to next field */
10556 player->is_auto_moving = (player->programmed_action != MV_NONE);
10558 /* remove the last programmed player action */
10559 player->programmed_action = 0;
10561 if (player->MovPos)
10563 /* should only happen if pre-1.2 tape recordings are played */
10564 /* this is only for backward compatibility */
10566 int original_move_delay_value = player->move_delay_value;
10569 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10573 /* scroll remaining steps with finest movement resolution */
10574 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10576 while (player->MovPos)
10578 ScrollPlayer(player, SCROLL_GO_ON);
10579 ScrollScreen(NULL, SCROLL_GO_ON);
10581 AdvanceFrameAndPlayerCounters(player->index_nr);
10587 player->move_delay_value = original_move_delay_value;
10590 if (player->last_move_dir & MV_HORIZONTAL)
10592 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10593 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10597 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10598 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10605 if (moved & MP_MOVING && !ScreenMovPos &&
10606 (player->index_nr == game.centered_player_nr ||
10607 game.centered_player_nr == -1))
10609 if (moved & MP_MOVING && !ScreenMovPos &&
10610 (player == local_player || !options.network))
10613 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10614 int offset = (setup.scroll_delay ? 3 : 0);
10616 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10618 /* actual player has left the screen -- scroll in that direction */
10619 if (jx != old_jx) /* player has moved horizontally */
10620 scroll_x += (jx - old_jx);
10621 else /* player has moved vertically */
10622 scroll_y += (jy - old_jy);
10626 if (jx != old_jx) /* player has moved horizontally */
10628 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10629 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10630 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10632 /* don't scroll over playfield boundaries */
10633 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10634 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10636 /* don't scroll more than one field at a time */
10637 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10639 /* don't scroll against the player's moving direction */
10640 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10641 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10642 scroll_x = old_scroll_x;
10644 else /* player has moved vertically */
10646 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10647 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10648 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10650 /* don't scroll over playfield boundaries */
10651 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10652 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10654 /* don't scroll more than one field at a time */
10655 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10657 /* don't scroll against the player's moving direction */
10658 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10659 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10660 scroll_y = old_scroll_y;
10664 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10667 if (!options.network && game.centered_player_nr == -1 &&
10668 !AllPlayersInVisibleScreen())
10670 scroll_x = old_scroll_x;
10671 scroll_y = old_scroll_y;
10675 if (!options.network && !AllPlayersInVisibleScreen())
10677 scroll_x = old_scroll_x;
10678 scroll_y = old_scroll_y;
10683 ScrollScreen(player, SCROLL_INIT);
10684 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10689 player->StepFrame = 0;
10691 if (moved & MP_MOVING)
10693 if (old_jx != jx && old_jy == jy)
10694 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10695 else if (old_jx == jx && old_jy != jy)
10696 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10698 DrawLevelField(jx, jy); /* for "crumbled sand" */
10700 player->last_move_dir = player->MovDir;
10701 player->is_moving = TRUE;
10702 player->is_snapping = FALSE;
10703 player->is_switching = FALSE;
10704 player->is_dropping = FALSE;
10705 player->is_dropping_pressed = FALSE;
10706 player->drop_pressed_delay = 0;
10710 CheckGravityMovementWhenNotMoving(player);
10712 player->is_moving = FALSE;
10714 /* at this point, the player is allowed to move, but cannot move right now
10715 (e.g. because of something blocking the way) -- ensure that the player
10716 is also allowed to move in the next frame (in old versions before 3.1.1,
10717 the player was forced to wait again for eight frames before next try) */
10719 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10720 player->move_delay = 0; /* allow direct movement in the next frame */
10723 if (player->move_delay == -1) /* not yet initialized by DigField() */
10724 player->move_delay = player->move_delay_value;
10726 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10728 TestIfPlayerTouchesBadThing(jx, jy);
10729 TestIfPlayerTouchesCustomElement(jx, jy);
10732 if (!player->active)
10733 RemovePlayer(player);
10738 void ScrollPlayer(struct PlayerInfo *player, int mode)
10740 int jx = player->jx, jy = player->jy;
10741 int last_jx = player->last_jx, last_jy = player->last_jy;
10742 int move_stepsize = TILEX / player->move_delay_value;
10744 #if USE_NEW_PLAYER_SPEED
10745 if (!player->active)
10748 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10751 if (!player->active || player->MovPos == 0)
10755 if (mode == SCROLL_INIT)
10757 player->actual_frame_counter = FrameCounter;
10758 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10760 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10761 Feld[last_jx][last_jy] == EL_EMPTY)
10763 int last_field_block_delay = 0; /* start with no blocking at all */
10764 int block_delay_adjustment = player->block_delay_adjustment;
10766 /* if player blocks last field, add delay for exactly one move */
10767 if (player->block_last_field)
10769 last_field_block_delay += player->move_delay_value;
10771 /* when blocking enabled, prevent moving up despite gravity */
10772 #if USE_PLAYER_GRAVITY
10773 if (player->gravity && player->MovDir == MV_UP)
10774 block_delay_adjustment = -1;
10776 if (game.gravity && player->MovDir == MV_UP)
10777 block_delay_adjustment = -1;
10781 /* add block delay adjustment (also possible when not blocking) */
10782 last_field_block_delay += block_delay_adjustment;
10784 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10785 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10788 #if USE_NEW_PLAYER_SPEED
10789 if (player->MovPos != 0) /* player has not yet reached destination */
10795 else if (!FrameReached(&player->actual_frame_counter, 1))
10799 printf("::: player->MovPos: %d -> %d\n",
10801 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10804 #if USE_NEW_PLAYER_SPEED
10805 if (player->MovPos != 0)
10807 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10808 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10810 /* before DrawPlayer() to draw correct player graphic for this case */
10811 if (player->MovPos == 0)
10812 CheckGravityMovement(player);
10815 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10816 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10818 /* before DrawPlayer() to draw correct player graphic for this case */
10819 if (player->MovPos == 0)
10820 CheckGravityMovement(player);
10823 if (player->MovPos == 0) /* player reached destination field */
10826 printf("::: player reached destination field\n");
10829 if (player->move_delay_reset_counter > 0)
10831 player->move_delay_reset_counter--;
10833 if (player->move_delay_reset_counter == 0)
10835 /* continue with normal speed after quickly moving through gate */
10836 HALVE_PLAYER_SPEED(player);
10838 /* be able to make the next move without delay */
10839 player->move_delay = 0;
10843 player->last_jx = jx;
10844 player->last_jy = jy;
10846 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10847 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10848 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10850 DrawPlayer(player); /* needed here only to cleanup last field */
10851 RemovePlayer(player);
10853 if (local_player->friends_still_needed == 0 ||
10854 IS_SP_ELEMENT(Feld[jx][jy]))
10855 player->LevelSolved = player->GameOver = TRUE;
10858 /* this breaks one level: "machine", level 000 */
10860 int move_direction = player->MovDir;
10861 int enter_side = MV_DIR_OPPOSITE(move_direction);
10862 int leave_side = move_direction;
10863 int old_jx = last_jx;
10864 int old_jy = last_jy;
10865 int old_element = Feld[old_jx][old_jy];
10866 int new_element = Feld[jx][jy];
10868 if (IS_CUSTOM_ELEMENT(old_element))
10869 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10871 player->index_bit, leave_side);
10873 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10874 CE_PLAYER_LEAVES_X,
10875 player->index_bit, leave_side);
10877 if (IS_CUSTOM_ELEMENT(new_element))
10878 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10879 player->index_bit, enter_side);
10881 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10882 CE_PLAYER_ENTERS_X,
10883 player->index_bit, enter_side);
10885 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10886 CE_MOVE_OF_X, move_direction);
10889 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10891 TestIfPlayerTouchesBadThing(jx, jy);
10892 TestIfPlayerTouchesCustomElement(jx, jy);
10894 /* needed because pushed element has not yet reached its destination,
10895 so it would trigger a change event at its previous field location */
10896 if (!player->is_pushing)
10897 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10899 if (!player->active)
10900 RemovePlayer(player);
10903 if (level.use_step_counter)
10913 if (TimeLeft <= 10 && setup.time_limit)
10914 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10916 DrawGameValue_Time(TimeLeft);
10918 if (!TimeLeft && setup.time_limit)
10919 for (i = 0; i < MAX_PLAYERS; i++)
10920 KillPlayer(&stored_player[i]);
10922 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10923 DrawGameValue_Time(TimePlayed);
10926 if (tape.single_step && tape.recording && !tape.pausing &&
10927 !player->programmed_action)
10928 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10932 void ScrollScreen(struct PlayerInfo *player, int mode)
10934 static unsigned long screen_frame_counter = 0;
10936 if (mode == SCROLL_INIT)
10938 /* set scrolling step size according to actual player's moving speed */
10939 ScrollStepSize = TILEX / player->move_delay_value;
10941 screen_frame_counter = FrameCounter;
10942 ScreenMovDir = player->MovDir;
10943 ScreenMovPos = player->MovPos;
10944 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10947 else if (!FrameReached(&screen_frame_counter, 1))
10952 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10953 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10954 redraw_mask |= REDRAW_FIELD;
10957 ScreenMovDir = MV_NONE;
10960 void TestIfPlayerTouchesCustomElement(int x, int y)
10962 static int xy[4][2] =
10969 static int trigger_sides[4][2] =
10971 /* center side border side */
10972 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10973 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10974 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10975 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10977 static int touch_dir[4] =
10979 MV_LEFT | MV_RIGHT,
10984 int center_element = Feld[x][y]; /* should always be non-moving! */
10987 for (i = 0; i < NUM_DIRECTIONS; i++)
10989 int xx = x + xy[i][0];
10990 int yy = y + xy[i][1];
10991 int center_side = trigger_sides[i][0];
10992 int border_side = trigger_sides[i][1];
10993 int border_element;
10995 if (!IN_LEV_FIELD(xx, yy))
10998 if (IS_PLAYER(x, y))
11000 struct PlayerInfo *player = PLAYERINFO(x, y);
11002 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11003 border_element = Feld[xx][yy]; /* may be moving! */
11004 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11005 border_element = Feld[xx][yy];
11006 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11007 border_element = MovingOrBlocked2Element(xx, yy);
11009 continue; /* center and border element do not touch */
11011 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11012 player->index_bit, border_side);
11013 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11014 CE_PLAYER_TOUCHES_X,
11015 player->index_bit, border_side);
11017 else if (IS_PLAYER(xx, yy))
11019 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11021 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11023 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11024 continue; /* center and border element do not touch */
11027 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11028 player->index_bit, center_side);
11029 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11030 CE_PLAYER_TOUCHES_X,
11031 player->index_bit, center_side);
11037 #if USE_ELEMENT_TOUCHING_BUGFIX
11039 void TestIfElementTouchesCustomElement(int x, int y)
11041 static int xy[4][2] =
11048 static int trigger_sides[4][2] =
11050 /* center side border side */
11051 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11052 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11053 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11054 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11056 static int touch_dir[4] =
11058 MV_LEFT | MV_RIGHT,
11063 boolean change_center_element = FALSE;
11064 int center_element = Feld[x][y]; /* should always be non-moving! */
11065 int border_element_old[NUM_DIRECTIONS];
11068 for (i = 0; i < NUM_DIRECTIONS; i++)
11070 int xx = x + xy[i][0];
11071 int yy = y + xy[i][1];
11072 int border_element;
11074 border_element_old[i] = -1;
11076 if (!IN_LEV_FIELD(xx, yy))
11079 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11080 border_element = Feld[xx][yy]; /* may be moving! */
11081 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11082 border_element = Feld[xx][yy];
11083 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11084 border_element = MovingOrBlocked2Element(xx, yy);
11086 continue; /* center and border element do not touch */
11088 border_element_old[i] = border_element;
11091 for (i = 0; i < NUM_DIRECTIONS; i++)
11093 int xx = x + xy[i][0];
11094 int yy = y + xy[i][1];
11095 int center_side = trigger_sides[i][0];
11096 int border_element = border_element_old[i];
11098 if (border_element == -1)
11101 /* check for change of border element */
11102 CheckElementChangeBySide(xx, yy, border_element, center_element,
11103 CE_TOUCHING_X, center_side);
11106 for (i = 0; i < NUM_DIRECTIONS; i++)
11108 int border_side = trigger_sides[i][1];
11109 int border_element = border_element_old[i];
11111 if (border_element == -1)
11114 /* check for change of center element (but change it only once) */
11115 if (!change_center_element)
11116 change_center_element =
11117 CheckElementChangeBySide(x, y, center_element, border_element,
11118 CE_TOUCHING_X, border_side);
11124 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11126 static int xy[4][2] =
11133 static int trigger_sides[4][2] =
11135 /* center side border side */
11136 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11137 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11138 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11139 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11141 static int touch_dir[4] =
11143 MV_LEFT | MV_RIGHT,
11148 boolean change_center_element = FALSE;
11149 int center_element = Feld[x][y]; /* should always be non-moving! */
11152 for (i = 0; i < NUM_DIRECTIONS; i++)
11154 int xx = x + xy[i][0];
11155 int yy = y + xy[i][1];
11156 int center_side = trigger_sides[i][0];
11157 int border_side = trigger_sides[i][1];
11158 int border_element;
11160 if (!IN_LEV_FIELD(xx, yy))
11163 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11164 border_element = Feld[xx][yy]; /* may be moving! */
11165 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11166 border_element = Feld[xx][yy];
11167 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11168 border_element = MovingOrBlocked2Element(xx, yy);
11170 continue; /* center and border element do not touch */
11172 /* check for change of center element (but change it only once) */
11173 if (!change_center_element)
11174 change_center_element =
11175 CheckElementChangeBySide(x, y, center_element, border_element,
11176 CE_TOUCHING_X, border_side);
11178 /* check for change of border element */
11179 CheckElementChangeBySide(xx, yy, border_element, center_element,
11180 CE_TOUCHING_X, center_side);
11186 void TestIfElementHitsCustomElement(int x, int y, int direction)
11188 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11189 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11190 int hitx = x + dx, hity = y + dy;
11191 int hitting_element = Feld[x][y];
11192 int touched_element;
11194 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11197 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11198 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11200 if (IN_LEV_FIELD(hitx, hity))
11202 int opposite_direction = MV_DIR_OPPOSITE(direction);
11203 int hitting_side = direction;
11204 int touched_side = opposite_direction;
11205 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11206 MovDir[hitx][hity] != direction ||
11207 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11213 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11214 CE_HITTING_X, touched_side);
11216 CheckElementChangeBySide(hitx, hity, touched_element,
11217 hitting_element, CE_HIT_BY_X, hitting_side);
11219 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11220 CE_HIT_BY_SOMETHING, opposite_direction);
11224 /* "hitting something" is also true when hitting the playfield border */
11225 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11226 CE_HITTING_SOMETHING, direction);
11230 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11232 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11233 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11234 int hitx = x + dx, hity = y + dy;
11235 int hitting_element = Feld[x][y];
11236 int touched_element;
11238 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11239 !IS_FREE(hitx, hity) &&
11240 (!IS_MOVING(hitx, hity) ||
11241 MovDir[hitx][hity] != direction ||
11242 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11245 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11249 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11253 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11254 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11256 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11257 EP_CAN_SMASH_EVERYTHING, direction);
11259 if (IN_LEV_FIELD(hitx, hity))
11261 int opposite_direction = MV_DIR_OPPOSITE(direction);
11262 int hitting_side = direction;
11263 int touched_side = opposite_direction;
11265 int touched_element = MovingOrBlocked2Element(hitx, hity);
11268 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11269 MovDir[hitx][hity] != direction ||
11270 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11279 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11280 CE_SMASHED_BY_SOMETHING, opposite_direction);
11282 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11283 CE_OTHER_IS_SMASHING, touched_side);
11285 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11286 CE_OTHER_GETS_SMASHED, hitting_side);
11292 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11294 int i, kill_x = -1, kill_y = -1;
11296 int bad_element = -1;
11297 static int test_xy[4][2] =
11304 static int test_dir[4] =
11312 for (i = 0; i < NUM_DIRECTIONS; i++)
11314 int test_x, test_y, test_move_dir, test_element;
11316 test_x = good_x + test_xy[i][0];
11317 test_y = good_y + test_xy[i][1];
11319 if (!IN_LEV_FIELD(test_x, test_y))
11323 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11325 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11327 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11328 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11330 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11331 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11335 bad_element = test_element;
11341 if (kill_x != -1 || kill_y != -1)
11343 if (IS_PLAYER(good_x, good_y))
11345 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11347 if (player->shield_deadly_time_left > 0 &&
11348 !IS_INDESTRUCTIBLE(bad_element))
11349 Bang(kill_x, kill_y);
11350 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11351 KillPlayer(player);
11354 Bang(good_x, good_y);
11358 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11360 int i, kill_x = -1, kill_y = -1;
11361 int bad_element = Feld[bad_x][bad_y];
11362 static int test_xy[4][2] =
11369 static int touch_dir[4] =
11371 MV_LEFT | MV_RIGHT,
11376 static int test_dir[4] =
11384 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11387 for (i = 0; i < NUM_DIRECTIONS; i++)
11389 int test_x, test_y, test_move_dir, test_element;
11391 test_x = bad_x + test_xy[i][0];
11392 test_y = bad_y + test_xy[i][1];
11393 if (!IN_LEV_FIELD(test_x, test_y))
11397 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11399 test_element = Feld[test_x][test_y];
11401 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11402 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11404 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11405 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11407 /* good thing is player or penguin that does not move away */
11408 if (IS_PLAYER(test_x, test_y))
11410 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11412 if (bad_element == EL_ROBOT && player->is_moving)
11413 continue; /* robot does not kill player if he is moving */
11415 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11417 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11418 continue; /* center and border element do not touch */
11425 else if (test_element == EL_PENGUIN)
11434 if (kill_x != -1 || kill_y != -1)
11436 if (IS_PLAYER(kill_x, kill_y))
11438 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11440 if (player->shield_deadly_time_left > 0 &&
11441 !IS_INDESTRUCTIBLE(bad_element))
11442 Bang(bad_x, bad_y);
11443 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11444 KillPlayer(player);
11447 Bang(kill_x, kill_y);
11451 void TestIfPlayerTouchesBadThing(int x, int y)
11453 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11456 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11458 TestIfGoodThingHitsBadThing(x, y, move_dir);
11461 void TestIfBadThingTouchesPlayer(int x, int y)
11463 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11466 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11468 TestIfBadThingHitsGoodThing(x, y, move_dir);
11471 void TestIfFriendTouchesBadThing(int x, int y)
11473 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11476 void TestIfBadThingTouchesFriend(int x, int y)
11478 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11481 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11483 int i, kill_x = bad_x, kill_y = bad_y;
11484 static int xy[4][2] =
11492 for (i = 0; i < NUM_DIRECTIONS; i++)
11496 x = bad_x + xy[i][0];
11497 y = bad_y + xy[i][1];
11498 if (!IN_LEV_FIELD(x, y))
11501 element = Feld[x][y];
11502 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11503 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11511 if (kill_x != bad_x || kill_y != bad_y)
11512 Bang(bad_x, bad_y);
11515 void KillPlayer(struct PlayerInfo *player)
11517 int jx = player->jx, jy = player->jy;
11519 if (!player->active)
11522 /* remove accessible field at the player's position */
11523 Feld[jx][jy] = EL_EMPTY;
11525 /* deactivate shield (else Bang()/Explode() would not work right) */
11526 player->shield_normal_time_left = 0;
11527 player->shield_deadly_time_left = 0;
11530 BuryPlayer(player);
11533 static void KillPlayerUnlessEnemyProtected(int x, int y)
11535 if (!PLAYER_ENEMY_PROTECTED(x, y))
11536 KillPlayer(PLAYERINFO(x, y));
11539 static void KillPlayerUnlessExplosionProtected(int x, int y)
11541 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11542 KillPlayer(PLAYERINFO(x, y));
11545 void BuryPlayer(struct PlayerInfo *player)
11547 int jx = player->jx, jy = player->jy;
11549 if (!player->active)
11552 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11553 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11555 player->GameOver = TRUE;
11556 RemovePlayer(player);
11559 void RemovePlayer(struct PlayerInfo *player)
11561 int jx = player->jx, jy = player->jy;
11562 int i, found = FALSE;
11564 player->present = FALSE;
11565 player->active = FALSE;
11567 if (!ExplodeField[jx][jy])
11568 StorePlayer[jx][jy] = 0;
11570 if (player->is_moving)
11571 DrawLevelField(player->last_jx, player->last_jy);
11573 for (i = 0; i < MAX_PLAYERS; i++)
11574 if (stored_player[i].active)
11578 AllPlayersGone = TRUE;
11584 #if USE_NEW_SNAP_DELAY
11585 static void setFieldForSnapping(int x, int y, int element, int direction)
11587 struct ElementInfo *ei = &element_info[element];
11588 int direction_bit = MV_DIR_TO_BIT(direction);
11589 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11590 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11591 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11593 Feld[x][y] = EL_ELEMENT_SNAPPING;
11594 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11596 ResetGfxAnimation(x, y);
11598 GfxElement[x][y] = element;
11599 GfxAction[x][y] = action;
11600 GfxDir[x][y] = direction;
11601 GfxFrame[x][y] = -1;
11606 =============================================================================
11607 checkDiagonalPushing()
11608 -----------------------------------------------------------------------------
11609 check if diagonal input device direction results in pushing of object
11610 (by checking if the alternative direction is walkable, diggable, ...)
11611 =============================================================================
11614 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11615 int x, int y, int real_dx, int real_dy)
11617 int jx, jy, dx, dy, xx, yy;
11619 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11622 /* diagonal direction: check alternative direction */
11627 xx = jx + (dx == 0 ? real_dx : 0);
11628 yy = jy + (dy == 0 ? real_dy : 0);
11630 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11634 =============================================================================
11636 -----------------------------------------------------------------------------
11637 x, y: field next to player (non-diagonal) to try to dig to
11638 real_dx, real_dy: direction as read from input device (can be diagonal)
11639 =============================================================================
11642 int DigField(struct PlayerInfo *player,
11643 int oldx, int oldy, int x, int y,
11644 int real_dx, int real_dy, int mode)
11646 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11647 boolean player_was_pushing = player->is_pushing;
11648 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11649 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11650 int jx = oldx, jy = oldy;
11651 int dx = x - jx, dy = y - jy;
11652 int nextx = x + dx, nexty = y + dy;
11653 int move_direction = (dx == -1 ? MV_LEFT :
11654 dx == +1 ? MV_RIGHT :
11656 dy == +1 ? MV_DOWN : MV_NONE);
11657 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11658 int dig_side = MV_DIR_OPPOSITE(move_direction);
11659 int old_element = Feld[jx][jy];
11660 #if USE_FIXED_DONT_RUN_INTO
11661 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11667 if (is_player) /* function can also be called by EL_PENGUIN */
11669 if (player->MovPos == 0)
11671 player->is_digging = FALSE;
11672 player->is_collecting = FALSE;
11675 if (player->MovPos == 0) /* last pushing move finished */
11676 player->is_pushing = FALSE;
11678 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11680 player->is_switching = FALSE;
11681 player->push_delay = -1;
11683 return MP_NO_ACTION;
11687 #if !USE_FIXED_DONT_RUN_INTO
11688 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11689 return MP_NO_ACTION;
11692 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11693 old_element = Back[jx][jy];
11695 /* in case of element dropped at player position, check background */
11696 else if (Back[jx][jy] != EL_EMPTY &&
11697 game.engine_version >= VERSION_IDENT(2,2,0,0))
11698 old_element = Back[jx][jy];
11700 /* checking here causes player to move into acid even if the current field
11701 cannot be left to that direction */
11703 #if USE_FIXED_DONT_RUN_INTO
11704 if (player_can_move && DONT_RUN_INTO(element))
11706 if (element == EL_ACID && dx == 0 && dy == 1)
11709 Feld[jx][jy] = EL_PLAYER_1;
11710 InitMovingField(jx, jy, MV_DOWN);
11711 Store[jx][jy] = EL_ACID;
11712 ContinueMoving(jx, jy);
11713 BuryPlayer(player);
11716 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11718 return MP_DONT_RUN_INTO;
11723 #if 1 /* ------------------------------ NEW ------------------------------ */
11725 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11726 return MP_NO_ACTION; /* field has no opening in this direction */
11728 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11729 return MP_NO_ACTION; /* field has no opening in this direction */
11731 #if USE_FIXED_DONT_RUN_INTO
11732 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11735 Feld[jx][jy] = EL_PLAYER_1;
11736 InitMovingField(jx, jy, MV_DOWN);
11737 Store[jx][jy] = EL_ACID;
11738 ContinueMoving(jx, jy);
11739 BuryPlayer(player);
11741 return MP_DONT_RUN_INTO;
11745 #if USE_FIXED_DONT_RUN_INTO
11746 if (player_can_move && DONT_RUN_INTO(element))
11748 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11750 return MP_DONT_RUN_INTO;
11754 #else /* ------------------------------ OLD ------------------------------ */
11757 #if USE_FIXED_DONT_RUN_INTO
11758 if (player_can_move && DONT_RUN_INTO(element))
11760 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11762 return MP_DONT_RUN_INTO;
11767 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11768 return MP_NO_ACTION; /* field has no opening in this direction */
11770 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11771 return MP_NO_ACTION; /* field has no opening in this direction */
11773 /* checking here causes player to explode when moving into acid */
11775 #if USE_FIXED_DONT_RUN_INTO
11776 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11779 Feld[jx][jy] = EL_PLAYER_1;
11780 InitMovingField(jx, jy, MV_DOWN);
11781 Store[jx][jy] = EL_ACID;
11782 ContinueMoving(jx, jy);
11783 BuryPlayer(player);
11785 return MP_DONT_RUN_INTO;
11790 #endif /* ------------------------------ END ------------------------------ */
11793 #if USE_FIXED_DONT_RUN_INTO
11794 if (player_can_move && DONT_RUN_INTO(element))
11796 if (element == EL_ACID && dx == 0 && dy == 1)
11799 Feld[jx][jy] = EL_PLAYER_1;
11800 InitMovingField(jx, jy, MV_DOWN);
11801 Store[jx][jy] = EL_ACID;
11802 ContinueMoving(jx, jy);
11803 BuryPlayer(player);
11806 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11808 return MP_DONT_RUN_INTO;
11813 #if USE_FIXED_DONT_RUN_INTO
11814 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11815 return MP_NO_ACTION;
11818 #if !USE_FIXED_DONT_RUN_INTO
11819 element = Feld[x][y];
11822 collect_count = element_info[element].collect_count_initial;
11824 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11825 return MP_NO_ACTION;
11827 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11828 player_can_move = player_can_move_or_snap;
11830 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11831 game.engine_version >= VERSION_IDENT(2,2,0,0))
11833 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11834 player->index_bit, dig_side);
11835 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11836 player->index_bit, dig_side);
11838 if (Feld[x][y] != element) /* field changed by snapping */
11841 return MP_NO_ACTION;
11844 #if USE_PLAYER_GRAVITY
11845 if (player->gravity && is_player && !player->is_auto_moving &&
11846 canFallDown(player) && move_direction != MV_DOWN &&
11847 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11848 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11850 if (game.gravity && is_player && !player->is_auto_moving &&
11851 canFallDown(player) && move_direction != MV_DOWN &&
11852 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11853 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11856 if (player_can_move &&
11857 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11859 int sound_element = SND_ELEMENT(element);
11860 int sound_action = ACTION_WALKING;
11862 if (IS_RND_GATE(element))
11864 if (!player->key[RND_GATE_NR(element)])
11865 return MP_NO_ACTION;
11867 else if (IS_RND_GATE_GRAY(element))
11869 if (!player->key[RND_GATE_GRAY_NR(element)])
11870 return MP_NO_ACTION;
11872 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11874 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11875 return MP_NO_ACTION;
11877 else if (element == EL_EXIT_OPEN ||
11878 element == EL_SP_EXIT_OPEN ||
11879 element == EL_SP_EXIT_OPENING)
11881 sound_action = ACTION_PASSING; /* player is passing exit */
11883 else if (element == EL_EMPTY)
11885 sound_action = ACTION_MOVING; /* nothing to walk on */
11888 /* play sound from background or player, whatever is available */
11889 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11890 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11892 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11894 else if (player_can_move &&
11895 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11897 if (!ACCESS_FROM(element, opposite_direction))
11898 return MP_NO_ACTION; /* field not accessible from this direction */
11900 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11901 return MP_NO_ACTION;
11903 if (IS_EM_GATE(element))
11905 if (!player->key[EM_GATE_NR(element)])
11906 return MP_NO_ACTION;
11908 else if (IS_EM_GATE_GRAY(element))
11910 if (!player->key[EM_GATE_GRAY_NR(element)])
11911 return MP_NO_ACTION;
11913 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11915 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11916 return MP_NO_ACTION;
11918 else if (IS_EMC_GATE(element))
11920 if (!player->key[EMC_GATE_NR(element)])
11921 return MP_NO_ACTION;
11923 else if (IS_EMC_GATE_GRAY(element))
11925 if (!player->key[EMC_GATE_GRAY_NR(element)])
11926 return MP_NO_ACTION;
11928 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11930 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11931 return MP_NO_ACTION;
11933 else if (IS_SP_PORT(element))
11935 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11936 element == EL_SP_GRAVITY_PORT_RIGHT ||
11937 element == EL_SP_GRAVITY_PORT_UP ||
11938 element == EL_SP_GRAVITY_PORT_DOWN)
11939 #if USE_PLAYER_GRAVITY
11940 player->gravity = !player->gravity;
11942 game.gravity = !game.gravity;
11944 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11945 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11946 element == EL_SP_GRAVITY_ON_PORT_UP ||
11947 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11948 #if USE_PLAYER_GRAVITY
11949 player->gravity = TRUE;
11951 game.gravity = TRUE;
11953 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11954 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11955 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11956 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11957 #if USE_PLAYER_GRAVITY
11958 player->gravity = FALSE;
11960 game.gravity = FALSE;
11964 /* automatically move to the next field with double speed */
11965 player->programmed_action = move_direction;
11967 if (player->move_delay_reset_counter == 0)
11969 player->move_delay_reset_counter = 2; /* two double speed steps */
11971 DOUBLE_PLAYER_SPEED(player);
11974 PlayLevelSoundAction(x, y, ACTION_PASSING);
11976 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11980 if (mode != DF_SNAP)
11982 GfxElement[x][y] = GFX_ELEMENT(element);
11983 player->is_digging = TRUE;
11986 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11988 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11989 player->index_bit, dig_side);
11991 if (mode == DF_SNAP)
11993 #if USE_NEW_SNAP_DELAY
11994 if (level.block_snap_field)
11995 setFieldForSnapping(x, y, element, move_direction);
11997 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11999 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12002 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12003 player->index_bit, dig_side);
12006 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12010 if (is_player && mode != DF_SNAP)
12012 GfxElement[x][y] = element;
12013 player->is_collecting = TRUE;
12016 if (element == EL_SPEED_PILL)
12018 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12020 else if (element == EL_EXTRA_TIME && level.time > 0)
12022 TimeLeft += level.extra_time;
12023 DrawGameValue_Time(TimeLeft);
12025 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12027 player->shield_normal_time_left += level.shield_normal_time;
12028 if (element == EL_SHIELD_DEADLY)
12029 player->shield_deadly_time_left += level.shield_deadly_time;
12031 else if (element == EL_DYNAMITE ||
12032 element == EL_EM_DYNAMITE ||
12033 element == EL_SP_DISK_RED)
12035 if (player->inventory_size < MAX_INVENTORY_SIZE)
12036 player->inventory_element[player->inventory_size++] = element;
12039 DrawGameDoorValues();
12041 DrawGameValue_Dynamite(local_player->inventory_size);
12044 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12046 player->dynabomb_count++;
12047 player->dynabombs_left++;
12049 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12051 player->dynabomb_size++;
12053 else if (element == EL_DYNABOMB_INCREASE_POWER)
12055 player->dynabomb_xl = TRUE;
12057 else if (IS_KEY(element))
12059 player->key[KEY_NR(element)] = TRUE;
12062 DrawGameDoorValues();
12064 DrawGameValue_Keys(player->key);
12067 redraw_mask |= REDRAW_DOOR_1;
12069 else if (IS_ENVELOPE(element))
12071 player->show_envelope = element;
12073 else if (element == EL_EMC_LENSES)
12075 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12077 RedrawAllInvisibleElementsForLenses();
12079 else if (element == EL_EMC_MAGNIFIER)
12081 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12083 RedrawAllInvisibleElementsForMagnifier();
12085 else if (IS_DROPPABLE(element) ||
12086 IS_THROWABLE(element)) /* can be collected and dropped */
12090 if (collect_count == 0)
12091 player->inventory_infinite_element = element;
12093 for (i = 0; i < collect_count; i++)
12094 if (player->inventory_size < MAX_INVENTORY_SIZE)
12095 player->inventory_element[player->inventory_size++] = element;
12098 DrawGameDoorValues();
12100 DrawGameValue_Dynamite(local_player->inventory_size);
12103 else if (collect_count > 0)
12105 local_player->gems_still_needed -= collect_count;
12106 if (local_player->gems_still_needed < 0)
12107 local_player->gems_still_needed = 0;
12109 DrawGameValue_Emeralds(local_player->gems_still_needed);
12112 RaiseScoreElement(element);
12113 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12116 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12117 player->index_bit, dig_side);
12119 if (mode == DF_SNAP)
12121 #if USE_NEW_SNAP_DELAY
12122 if (level.block_snap_field)
12123 setFieldForSnapping(x, y, element, move_direction);
12125 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12127 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12130 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12131 player->index_bit, dig_side);
12134 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12136 if (mode == DF_SNAP && element != EL_BD_ROCK)
12137 return MP_NO_ACTION;
12139 if (CAN_FALL(element) && dy)
12140 return MP_NO_ACTION;
12142 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12143 !(element == EL_SPRING && level.use_spring_bug))
12144 return MP_NO_ACTION;
12146 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12147 ((move_direction & MV_VERTICAL &&
12148 ((element_info[element].move_pattern & MV_LEFT &&
12149 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12150 (element_info[element].move_pattern & MV_RIGHT &&
12151 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12152 (move_direction & MV_HORIZONTAL &&
12153 ((element_info[element].move_pattern & MV_UP &&
12154 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12155 (element_info[element].move_pattern & MV_DOWN &&
12156 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12157 return MP_NO_ACTION;
12159 /* do not push elements already moving away faster than player */
12160 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12161 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12162 return MP_NO_ACTION;
12164 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12166 if (player->push_delay_value == -1 || !player_was_pushing)
12167 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12169 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12171 if (player->push_delay_value == -1)
12172 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12174 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12176 if (!player->is_pushing)
12177 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12180 player->is_pushing = TRUE;
12182 if (!(IN_LEV_FIELD(nextx, nexty) &&
12183 (IS_FREE(nextx, nexty) ||
12184 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12185 IS_SB_ELEMENT(element)))))
12186 return MP_NO_ACTION;
12188 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12189 return MP_NO_ACTION;
12191 if (player->push_delay == -1) /* new pushing; restart delay */
12192 player->push_delay = 0;
12194 if (player->push_delay < player->push_delay_value &&
12195 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12196 element != EL_SPRING && element != EL_BALLOON)
12198 /* make sure that there is no move delay before next try to push */
12199 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12200 player->move_delay = 0;
12202 return MP_NO_ACTION;
12205 if (IS_SB_ELEMENT(element))
12207 if (element == EL_SOKOBAN_FIELD_FULL)
12209 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12210 local_player->sokobanfields_still_needed++;
12213 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12215 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12216 local_player->sokobanfields_still_needed--;
12219 Feld[x][y] = EL_SOKOBAN_OBJECT;
12221 if (Back[x][y] == Back[nextx][nexty])
12222 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12223 else if (Back[x][y] != 0)
12224 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12227 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12230 if (local_player->sokobanfields_still_needed == 0 &&
12231 game.emulation == EMU_SOKOBAN)
12233 player->LevelSolved = player->GameOver = TRUE;
12234 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12238 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12240 InitMovingField(x, y, move_direction);
12241 GfxAction[x][y] = ACTION_PUSHING;
12243 if (mode == DF_SNAP)
12244 ContinueMoving(x, y);
12246 MovPos[x][y] = (dx != 0 ? dx : dy);
12248 Pushed[x][y] = TRUE;
12249 Pushed[nextx][nexty] = TRUE;
12251 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12252 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12254 player->push_delay_value = -1; /* get new value later */
12256 /* check for element change _after_ element has been pushed */
12257 if (game.use_change_when_pushing_bug)
12259 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12260 player->index_bit, dig_side);
12261 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12262 player->index_bit, dig_side);
12265 else if (IS_SWITCHABLE(element))
12267 if (PLAYER_SWITCHING(player, x, y))
12269 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12270 player->index_bit, dig_side);
12275 player->is_switching = TRUE;
12276 player->switch_x = x;
12277 player->switch_y = y;
12279 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12281 if (element == EL_ROBOT_WHEEL)
12283 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12287 DrawLevelField(x, y);
12289 else if (element == EL_SP_TERMINAL)
12294 SCAN_PLAYFIELD(xx, yy)
12296 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12299 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12301 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12302 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12305 else if (IS_BELT_SWITCH(element))
12307 ToggleBeltSwitch(x, y);
12309 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12310 element == EL_SWITCHGATE_SWITCH_DOWN)
12312 ToggleSwitchgateSwitch(x, y);
12314 else if (element == EL_LIGHT_SWITCH ||
12315 element == EL_LIGHT_SWITCH_ACTIVE)
12317 ToggleLightSwitch(x, y);
12319 else if (element == EL_TIMEGATE_SWITCH)
12321 ActivateTimegateSwitch(x, y);
12323 else if (element == EL_BALLOON_SWITCH_LEFT ||
12324 element == EL_BALLOON_SWITCH_RIGHT ||
12325 element == EL_BALLOON_SWITCH_UP ||
12326 element == EL_BALLOON_SWITCH_DOWN ||
12327 element == EL_BALLOON_SWITCH_NONE ||
12328 element == EL_BALLOON_SWITCH_ANY)
12330 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12331 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12332 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12333 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12334 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12337 else if (element == EL_LAMP)
12339 Feld[x][y] = EL_LAMP_ACTIVE;
12340 local_player->lights_still_needed--;
12342 ResetGfxAnimation(x, y);
12343 DrawLevelField(x, y);
12345 else if (element == EL_TIME_ORB_FULL)
12347 Feld[x][y] = EL_TIME_ORB_EMPTY;
12349 if (level.time > 0 || level.use_time_orb_bug)
12351 TimeLeft += level.time_orb_time;
12352 DrawGameValue_Time(TimeLeft);
12355 ResetGfxAnimation(x, y);
12356 DrawLevelField(x, y);
12358 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12359 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12363 game.ball_state = !game.ball_state;
12366 SCAN_PLAYFIELD(xx, yy)
12368 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12371 int e = Feld[xx][yy];
12373 if (game.ball_state)
12375 if (e == EL_EMC_MAGIC_BALL)
12376 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12377 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12378 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12382 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12383 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12384 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12385 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12390 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12391 player->index_bit, dig_side);
12393 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12394 player->index_bit, dig_side);
12396 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12397 player->index_bit, dig_side);
12403 if (!PLAYER_SWITCHING(player, x, y))
12405 player->is_switching = TRUE;
12406 player->switch_x = x;
12407 player->switch_y = y;
12409 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12410 player->index_bit, dig_side);
12411 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12412 player->index_bit, dig_side);
12414 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12415 player->index_bit, dig_side);
12416 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12417 player->index_bit, dig_side);
12420 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12421 player->index_bit, dig_side);
12422 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12423 player->index_bit, dig_side);
12425 return MP_NO_ACTION;
12428 player->push_delay = -1;
12430 if (is_player) /* function can also be called by EL_PENGUIN */
12432 if (Feld[x][y] != element) /* really digged/collected something */
12433 player->is_collecting = !player->is_digging;
12439 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12441 int jx = player->jx, jy = player->jy;
12442 int x = jx + dx, y = jy + dy;
12443 int snap_direction = (dx == -1 ? MV_LEFT :
12444 dx == +1 ? MV_RIGHT :
12446 dy == +1 ? MV_DOWN : MV_NONE);
12447 boolean can_continue_snapping = (level.continuous_snapping &&
12448 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12450 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12453 if (!player->active || !IN_LEV_FIELD(x, y))
12461 if (player->MovPos == 0)
12462 player->is_pushing = FALSE;
12464 player->is_snapping = FALSE;
12466 if (player->MovPos == 0)
12468 player->is_moving = FALSE;
12469 player->is_digging = FALSE;
12470 player->is_collecting = FALSE;
12476 #if USE_NEW_CONTINUOUS_SNAPPING
12477 /* prevent snapping with already pressed snap key when not allowed */
12478 if (player->is_snapping && !can_continue_snapping)
12481 if (player->is_snapping)
12485 player->MovDir = snap_direction;
12487 if (player->MovPos == 0)
12489 player->is_moving = FALSE;
12490 player->is_digging = FALSE;
12491 player->is_collecting = FALSE;
12494 player->is_dropping = FALSE;
12495 player->is_dropping_pressed = FALSE;
12496 player->drop_pressed_delay = 0;
12498 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12501 player->is_snapping = TRUE;
12503 if (player->MovPos == 0)
12505 player->is_moving = FALSE;
12506 player->is_digging = FALSE;
12507 player->is_collecting = FALSE;
12510 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12511 DrawLevelField(player->last_jx, player->last_jy);
12513 DrawLevelField(x, y);
12518 boolean DropElement(struct PlayerInfo *player)
12520 int old_element, new_element;
12521 int dropx = player->jx, dropy = player->jy;
12522 int drop_direction = player->MovDir;
12523 int drop_side = drop_direction;
12524 int drop_element = (player->inventory_size > 0 ?
12525 player->inventory_element[player->inventory_size - 1] :
12526 player->inventory_infinite_element != EL_UNDEFINED ?
12527 player->inventory_infinite_element :
12528 player->dynabombs_left > 0 ?
12529 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12532 player->is_dropping_pressed = TRUE;
12534 /* do not drop an element on top of another element; when holding drop key
12535 pressed without moving, dropped element must move away before the next
12536 element can be dropped (this is especially important if the next element
12537 is dynamite, which can be placed on background for historical reasons) */
12538 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12541 if (IS_THROWABLE(drop_element))
12543 dropx += GET_DX_FROM_DIR(drop_direction);
12544 dropy += GET_DY_FROM_DIR(drop_direction);
12546 if (!IN_LEV_FIELD(dropx, dropy))
12550 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12551 new_element = drop_element; /* default: no change when dropping */
12553 /* check if player is active, not moving and ready to drop */
12554 if (!player->active || player->MovPos || player->drop_delay > 0)
12557 /* check if player has anything that can be dropped */
12558 if (new_element == EL_UNDEFINED)
12561 /* check if drop key was pressed long enough for EM style dynamite */
12562 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12565 /* check if anything can be dropped at the current position */
12566 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12569 /* collected custom elements can only be dropped on empty fields */
12570 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12573 if (old_element != EL_EMPTY)
12574 Back[dropx][dropy] = old_element; /* store old element on this field */
12576 ResetGfxAnimation(dropx, dropy);
12577 ResetRandomAnimationValue(dropx, dropy);
12579 if (player->inventory_size > 0 ||
12580 player->inventory_infinite_element != EL_UNDEFINED)
12582 if (player->inventory_size > 0)
12584 player->inventory_size--;
12587 DrawGameDoorValues();
12589 DrawGameValue_Dynamite(local_player->inventory_size);
12592 if (new_element == EL_DYNAMITE)
12593 new_element = EL_DYNAMITE_ACTIVE;
12594 else if (new_element == EL_EM_DYNAMITE)
12595 new_element = EL_EM_DYNAMITE_ACTIVE;
12596 else if (new_element == EL_SP_DISK_RED)
12597 new_element = EL_SP_DISK_RED_ACTIVE;
12600 Feld[dropx][dropy] = new_element;
12602 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12603 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12604 el2img(Feld[dropx][dropy]), 0);
12606 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12608 /* needed if previous element just changed to "empty" in the last frame */
12609 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12611 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12612 player->index_bit, drop_side);
12613 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12615 player->index_bit, drop_side);
12617 TestIfElementTouchesCustomElement(dropx, dropy);
12619 else /* player is dropping a dyna bomb */
12621 player->dynabombs_left--;
12623 Feld[dropx][dropy] = new_element;
12625 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12626 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12627 el2img(Feld[dropx][dropy]), 0);
12629 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12632 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12633 InitField_WithBug1(dropx, dropy, FALSE);
12635 new_element = Feld[dropx][dropy]; /* element might have changed */
12637 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12638 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12640 int move_direction, nextx, nexty;
12642 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12643 MovDir[dropx][dropy] = drop_direction;
12645 move_direction = MovDir[dropx][dropy];
12646 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12647 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12649 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12650 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12653 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12654 player->is_dropping = TRUE;
12656 player->drop_pressed_delay = 0;
12657 player->is_dropping_pressed = FALSE;
12659 player->drop_x = dropx;
12660 player->drop_y = dropy;
12665 /* ------------------------------------------------------------------------- */
12666 /* game sound playing functions */
12667 /* ------------------------------------------------------------------------- */
12669 static int *loop_sound_frame = NULL;
12670 static int *loop_sound_volume = NULL;
12672 void InitPlayLevelSound()
12674 int num_sounds = getSoundListSize();
12676 checked_free(loop_sound_frame);
12677 checked_free(loop_sound_volume);
12679 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12680 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12683 static void PlayLevelSound(int x, int y, int nr)
12685 int sx = SCREENX(x), sy = SCREENY(y);
12686 int volume, stereo_position;
12687 int max_distance = 8;
12688 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12690 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12691 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12694 if (!IN_LEV_FIELD(x, y) ||
12695 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12696 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12699 volume = SOUND_MAX_VOLUME;
12701 if (!IN_SCR_FIELD(sx, sy))
12703 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12704 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12706 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12709 stereo_position = (SOUND_MAX_LEFT +
12710 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12711 (SCR_FIELDX + 2 * max_distance));
12713 if (IS_LOOP_SOUND(nr))
12715 /* This assures that quieter loop sounds do not overwrite louder ones,
12716 while restarting sound volume comparison with each new game frame. */
12718 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12721 loop_sound_volume[nr] = volume;
12722 loop_sound_frame[nr] = FrameCounter;
12725 PlaySoundExt(nr, volume, stereo_position, type);
12728 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12730 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12731 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12732 y < LEVELY(BY1) ? LEVELY(BY1) :
12733 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12737 static void PlayLevelSoundAction(int x, int y, int action)
12739 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12742 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12744 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12746 if (sound_effect != SND_UNDEFINED)
12747 PlayLevelSound(x, y, sound_effect);
12750 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12753 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12755 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12756 PlayLevelSound(x, y, sound_effect);
12759 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12761 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12763 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12764 PlayLevelSound(x, y, sound_effect);
12767 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12769 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12771 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12772 StopSound(sound_effect);
12775 static void PlayLevelMusic()
12777 if (levelset.music[level_nr] != MUS_UNDEFINED)
12778 PlayMusic(levelset.music[level_nr]); /* from config file */
12780 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12783 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12785 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12790 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12794 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12798 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12802 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12806 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12810 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12814 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12817 case SAMPLE_android_clone:
12818 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12821 case SAMPLE_android_move:
12822 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12825 case SAMPLE_spring:
12826 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12830 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12834 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12837 case SAMPLE_eater_eat:
12838 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12842 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12845 case SAMPLE_collect:
12846 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12849 case SAMPLE_diamond:
12850 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12853 case SAMPLE_squash:
12854 /* !!! CHECK THIS !!! */
12856 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12858 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12862 case SAMPLE_wonderfall:
12863 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12867 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12871 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12875 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12879 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12883 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12887 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12890 case SAMPLE_wonder:
12891 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12895 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12898 case SAMPLE_exit_open:
12899 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12902 case SAMPLE_exit_leave:
12903 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12906 case SAMPLE_dynamite:
12907 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12911 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12915 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12919 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12923 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12927 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12931 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12935 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12940 void RaiseScore(int value)
12942 local_player->score += value;
12944 DrawGameValue_Score(local_player->score);
12947 void RaiseScoreElement(int element)
12952 case EL_BD_DIAMOND:
12953 case EL_EMERALD_YELLOW:
12954 case EL_EMERALD_RED:
12955 case EL_EMERALD_PURPLE:
12956 case EL_SP_INFOTRON:
12957 RaiseScore(level.score[SC_EMERALD]);
12960 RaiseScore(level.score[SC_DIAMOND]);
12963 RaiseScore(level.score[SC_CRYSTAL]);
12966 RaiseScore(level.score[SC_PEARL]);
12969 case EL_BD_BUTTERFLY:
12970 case EL_SP_ELECTRON:
12971 RaiseScore(level.score[SC_BUG]);
12974 case EL_BD_FIREFLY:
12975 case EL_SP_SNIKSNAK:
12976 RaiseScore(level.score[SC_SPACESHIP]);
12979 case EL_DARK_YAMYAM:
12980 RaiseScore(level.score[SC_YAMYAM]);
12983 RaiseScore(level.score[SC_ROBOT]);
12986 RaiseScore(level.score[SC_PACMAN]);
12989 RaiseScore(level.score[SC_NUT]);
12992 case EL_EM_DYNAMITE:
12993 case EL_SP_DISK_RED:
12994 case EL_DYNABOMB_INCREASE_NUMBER:
12995 case EL_DYNABOMB_INCREASE_SIZE:
12996 case EL_DYNABOMB_INCREASE_POWER:
12997 RaiseScore(level.score[SC_DYNAMITE]);
12999 case EL_SHIELD_NORMAL:
13000 case EL_SHIELD_DEADLY:
13001 RaiseScore(level.score[SC_SHIELD]);
13003 case EL_EXTRA_TIME:
13004 RaiseScore(level.extra_time_score);
13018 RaiseScore(level.score[SC_KEY]);
13021 RaiseScore(element_info[element].collect_score);
13026 void RequestQuitGame(boolean ask_if_really_quit)
13028 if (AllPlayersGone ||
13029 !ask_if_really_quit ||
13030 level_editor_test_game ||
13031 Request("Do you really want to quit the game ?",
13032 REQ_ASK | REQ_STAY_CLOSED))
13034 #if defined(NETWORK_AVALIABLE)
13035 if (options.network)
13036 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13040 game_status = GAME_MODE_MAIN;
13046 if (tape.playing && tape.deactivate_display)
13047 TapeDeactivateDisplayOff(TRUE);
13049 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13051 if (tape.playing && tape.deactivate_display)
13052 TapeDeactivateDisplayOn();
13057 /* ---------- new game button stuff ---------------------------------------- */
13059 /* graphic position values for game buttons */
13060 #define GAME_BUTTON_XSIZE 30
13061 #define GAME_BUTTON_YSIZE 30
13062 #define GAME_BUTTON_XPOS 5
13063 #define GAME_BUTTON_YPOS 215
13064 #define SOUND_BUTTON_XPOS 5
13065 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13067 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13068 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13069 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13070 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13071 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13072 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13079 } gamebutton_info[NUM_GAME_BUTTONS] =
13082 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13087 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13088 GAME_CTRL_ID_PAUSE,
13092 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13097 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13098 SOUND_CTRL_ID_MUSIC,
13099 "background music on/off"
13102 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13103 SOUND_CTRL_ID_LOOPS,
13104 "sound loops on/off"
13107 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13108 SOUND_CTRL_ID_SIMPLE,
13109 "normal sounds on/off"
13113 void CreateGameButtons()
13117 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13119 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13120 struct GadgetInfo *gi;
13123 unsigned long event_mask;
13124 int gd_xoffset, gd_yoffset;
13125 int gd_x1, gd_x2, gd_y1, gd_y2;
13128 gd_xoffset = gamebutton_info[i].x;
13129 gd_yoffset = gamebutton_info[i].y;
13130 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13131 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13133 if (id == GAME_CTRL_ID_STOP ||
13134 id == GAME_CTRL_ID_PAUSE ||
13135 id == GAME_CTRL_ID_PLAY)
13137 button_type = GD_TYPE_NORMAL_BUTTON;
13139 event_mask = GD_EVENT_RELEASED;
13140 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13141 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13145 button_type = GD_TYPE_CHECK_BUTTON;
13147 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13148 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13149 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13150 event_mask = GD_EVENT_PRESSED;
13151 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13152 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13155 gi = CreateGadget(GDI_CUSTOM_ID, id,
13156 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13157 GDI_X, DX + gd_xoffset,
13158 GDI_Y, DY + gd_yoffset,
13159 GDI_WIDTH, GAME_BUTTON_XSIZE,
13160 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13161 GDI_TYPE, button_type,
13162 GDI_STATE, GD_BUTTON_UNPRESSED,
13163 GDI_CHECKED, checked,
13164 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13165 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13166 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13167 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13168 GDI_EVENT_MASK, event_mask,
13169 GDI_CALLBACK_ACTION, HandleGameButtons,
13173 Error(ERR_EXIT, "cannot create gadget");
13175 game_gadget[id] = gi;
13179 void FreeGameButtons()
13183 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13184 FreeGadget(game_gadget[i]);
13187 static void MapGameButtons()
13191 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13192 MapGadget(game_gadget[i]);
13195 void UnmapGameButtons()
13199 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13200 UnmapGadget(game_gadget[i]);
13203 static void HandleGameButtons(struct GadgetInfo *gi)
13205 int id = gi->custom_id;
13207 if (game_status != GAME_MODE_PLAYING)
13212 case GAME_CTRL_ID_STOP:
13216 RequestQuitGame(TRUE);
13219 case GAME_CTRL_ID_PAUSE:
13220 if (options.network)
13222 #if defined(NETWORK_AVALIABLE)
13224 SendToServer_ContinuePlaying();
13226 SendToServer_PausePlaying();
13230 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13233 case GAME_CTRL_ID_PLAY:
13236 #if defined(NETWORK_AVALIABLE)
13237 if (options.network)
13238 SendToServer_ContinuePlaying();
13242 tape.pausing = FALSE;
13243 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13248 case SOUND_CTRL_ID_MUSIC:
13249 if (setup.sound_music)
13251 setup.sound_music = FALSE;
13254 else if (audio.music_available)
13256 setup.sound = setup.sound_music = TRUE;
13258 SetAudioMode(setup.sound);
13264 case SOUND_CTRL_ID_LOOPS:
13265 if (setup.sound_loops)
13266 setup.sound_loops = FALSE;
13267 else if (audio.loops_available)
13269 setup.sound = setup.sound_loops = TRUE;
13270 SetAudioMode(setup.sound);
13274 case SOUND_CTRL_ID_SIMPLE:
13275 if (setup.sound_simple)
13276 setup.sound_simple = FALSE;
13277 else if (audio.sound_available)
13279 setup.sound = setup.sound_simple = TRUE;
13280 SetAudioMode(setup.sound);