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 add_player = (ELEM_IS_PLAYER(new_element) &&
8454 IS_WALKABLE(old_element));
8457 /* check if element under the player changes from accessible to unaccessible
8458 (needed for special case of dropping element which then changes) */
8459 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8460 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8470 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8471 RemoveMovingField(x, y);
8475 Feld[x][y] = new_element;
8477 #if !USE_GFX_RESET_GFX_ANIMATION
8478 ResetGfxAnimation(x, y);
8479 ResetRandomAnimationValue(x, y);
8482 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8483 MovDir[x][y] = previous_move_direction;
8485 #if USE_NEW_CUSTOM_VALUE
8486 if (element_info[new_element].use_last_ce_value)
8487 CustomValue[x][y] = last_ce_value;
8490 InitField_WithBug1(x, y, FALSE);
8492 new_element = Feld[x][y]; /* element may have changed */
8494 #if USE_GFX_RESET_GFX_ANIMATION
8495 ResetGfxAnimation(x, y);
8496 ResetRandomAnimationValue(x, y);
8499 DrawLevelField(x, y);
8501 if (GFX_CRUMBLED(new_element))
8502 DrawLevelFieldCrumbledSandNeighbours(x, y);
8506 /* check if element under the player changes from accessible to unaccessible
8507 (needed for special case of dropping element which then changes) */
8508 /* (must be checked after creating new element for walkable group elements) */
8509 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8510 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8518 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8519 if (ELEM_IS_PLAYER(new_element))
8520 RelocatePlayer(x, y, new_element);
8523 ChangeCount[x][y]++; /* count number of changes in the same frame */
8525 TestIfBadThingTouchesPlayer(x, y);
8526 TestIfPlayerTouchesCustomElement(x, y);
8527 TestIfElementTouchesCustomElement(x, y);
8530 static void CreateField(int x, int y, int element)
8532 CreateFieldExt(x, y, element, FALSE);
8535 static void CreateElementFromChange(int x, int y, int element)
8537 element = GET_VALID_RUNTIME_ELEMENT(element);
8539 #if USE_STOP_CHANGED_ELEMENTS
8540 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8542 int old_element = Feld[x][y];
8544 /* prevent changed element from moving in same engine frame
8545 unless both old and new element can either fall or move */
8546 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8547 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8552 CreateFieldExt(x, y, element, TRUE);
8555 static boolean ChangeElement(int x, int y, int element, int page)
8557 struct ElementInfo *ei = &element_info[element];
8558 struct ElementChangeInfo *change = &ei->change_page[page];
8559 int ce_value = CustomValue[x][y];
8560 int ce_score = ei->collect_score;
8562 int old_element = Feld[x][y];
8564 /* always use default change event to prevent running into a loop */
8565 if (ChangeEvent[x][y] == -1)
8566 ChangeEvent[x][y] = CE_DELAY;
8568 if (ChangeEvent[x][y] == CE_DELAY)
8570 /* reset actual trigger element, trigger player and action element */
8571 change->actual_trigger_element = EL_EMPTY;
8572 change->actual_trigger_player = EL_PLAYER_1;
8573 change->actual_trigger_side = CH_SIDE_NONE;
8574 change->actual_trigger_ce_value = 0;
8575 change->actual_trigger_ce_score = 0;
8578 /* do not change elements more than a specified maximum number of changes */
8579 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8582 ChangeCount[x][y]++; /* count number of changes in the same frame */
8584 if (change->explode)
8591 if (change->use_target_content)
8593 boolean complete_replace = TRUE;
8594 boolean can_replace[3][3];
8597 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8600 boolean is_walkable;
8601 boolean is_diggable;
8602 boolean is_collectible;
8603 boolean is_removable;
8604 boolean is_destructible;
8605 int ex = x + xx - 1;
8606 int ey = y + yy - 1;
8607 int content_element = change->target_content.e[xx][yy];
8610 can_replace[xx][yy] = TRUE;
8612 if (ex == x && ey == y) /* do not check changing element itself */
8615 if (content_element == EL_EMPTY_SPACE)
8617 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8622 if (!IN_LEV_FIELD(ex, ey))
8624 can_replace[xx][yy] = FALSE;
8625 complete_replace = FALSE;
8632 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8633 e = MovingOrBlocked2Element(ex, ey);
8635 is_empty = (IS_FREE(ex, ey) ||
8636 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8638 is_walkable = (is_empty || IS_WALKABLE(e));
8639 is_diggable = (is_empty || IS_DIGGABLE(e));
8640 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8641 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8642 is_removable = (is_diggable || is_collectible);
8644 can_replace[xx][yy] =
8645 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8646 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8647 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8648 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8649 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8650 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8651 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8653 if (!can_replace[xx][yy])
8654 complete_replace = FALSE;
8657 if (!change->only_if_complete || complete_replace)
8659 boolean something_has_changed = FALSE;
8661 if (change->only_if_complete && change->use_random_replace &&
8662 RND(100) < change->random_percentage)
8665 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8667 int ex = x + xx - 1;
8668 int ey = y + yy - 1;
8669 int content_element;
8671 if (can_replace[xx][yy] && (!change->use_random_replace ||
8672 RND(100) < change->random_percentage))
8674 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8675 RemoveMovingField(ex, ey);
8677 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8679 content_element = change->target_content.e[xx][yy];
8680 target_element = GET_TARGET_ELEMENT(content_element, change,
8681 ce_value, ce_score);
8683 CreateElementFromChange(ex, ey, target_element);
8685 something_has_changed = TRUE;
8687 /* for symmetry reasons, freeze newly created border elements */
8688 if (ex != x || ey != y)
8689 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8693 if (something_has_changed)
8695 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8696 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8702 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8703 ce_value, ce_score);
8705 if (element == EL_DIAGONAL_GROWING ||
8706 element == EL_DIAGONAL_SHRINKING)
8708 target_element = Store[x][y];
8710 Store[x][y] = EL_EMPTY;
8713 CreateElementFromChange(x, y, target_element);
8715 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8716 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8719 /* this uses direct change before indirect change */
8720 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8725 #if USE_NEW_DELAYED_ACTION
8727 static void HandleElementChange(int x, int y, int page)
8729 int element = MovingOrBlocked2Element(x, y);
8730 struct ElementInfo *ei = &element_info[element];
8731 struct ElementChangeInfo *change = &ei->change_page[page];
8734 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8735 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8738 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8739 x, y, element, element_info[element].token_name);
8740 printf("HandleElementChange(): This should never happen!\n");
8745 /* this can happen with classic bombs on walkable, changing elements */
8746 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8749 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8750 ChangeDelay[x][y] = 0;
8756 if (ChangeDelay[x][y] == 0) /* initialize element change */
8758 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8760 if (change->can_change)
8762 ResetGfxAnimation(x, y);
8763 ResetRandomAnimationValue(x, y);
8765 if (change->pre_change_function)
8766 change->pre_change_function(x, y);
8770 ChangeDelay[x][y]--;
8772 if (ChangeDelay[x][y] != 0) /* continue element change */
8774 if (change->can_change)
8776 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8778 if (IS_ANIMATED(graphic))
8779 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8781 if (change->change_function)
8782 change->change_function(x, y);
8785 else /* finish element change */
8787 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8789 page = ChangePage[x][y];
8790 ChangePage[x][y] = -1;
8792 change = &ei->change_page[page];
8795 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8797 ChangeDelay[x][y] = 1; /* try change after next move step */
8798 ChangePage[x][y] = page; /* remember page to use for change */
8803 if (change->can_change)
8805 if (ChangeElement(x, y, element, page))
8807 if (change->post_change_function)
8808 change->post_change_function(x, y);
8812 if (change->has_action)
8813 ExecuteCustomElementAction(x, y, element, page);
8819 static void HandleElementChange(int x, int y, int page)
8821 int element = MovingOrBlocked2Element(x, y);
8822 struct ElementInfo *ei = &element_info[element];
8823 struct ElementChangeInfo *change = &ei->change_page[page];
8826 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8829 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8830 x, y, element, element_info[element].token_name);
8831 printf("HandleElementChange(): This should never happen!\n");
8836 /* this can happen with classic bombs on walkable, changing elements */
8837 if (!CAN_CHANGE(element))
8840 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8841 ChangeDelay[x][y] = 0;
8847 if (ChangeDelay[x][y] == 0) /* initialize element change */
8849 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8851 ResetGfxAnimation(x, y);
8852 ResetRandomAnimationValue(x, y);
8854 if (change->pre_change_function)
8855 change->pre_change_function(x, y);
8858 ChangeDelay[x][y]--;
8860 if (ChangeDelay[x][y] != 0) /* continue element change */
8862 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8864 if (IS_ANIMATED(graphic))
8865 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8867 if (change->change_function)
8868 change->change_function(x, y);
8870 else /* finish element change */
8872 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8874 page = ChangePage[x][y];
8875 ChangePage[x][y] = -1;
8877 change = &ei->change_page[page];
8880 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8882 ChangeDelay[x][y] = 1; /* try change after next move step */
8883 ChangePage[x][y] = page; /* remember page to use for change */
8888 if (ChangeElement(x, y, element, page))
8890 if (change->post_change_function)
8891 change->post_change_function(x, y);
8898 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8899 int trigger_element,
8905 boolean change_done_any = FALSE;
8906 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8909 if (!(trigger_events[trigger_element][trigger_event]))
8912 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8914 int element = EL_CUSTOM_START + i;
8915 boolean change_done = FALSE;
8918 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8919 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8922 for (p = 0; p < element_info[element].num_change_pages; p++)
8924 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8926 if (change->can_change_or_has_action &&
8927 change->has_event[trigger_event] &&
8928 change->trigger_side & trigger_side &&
8929 change->trigger_player & trigger_player &&
8930 change->trigger_page & trigger_page_bits &&
8931 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8933 change->actual_trigger_element = trigger_element;
8934 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8935 change->actual_trigger_side = trigger_side;
8936 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8937 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8939 if ((change->can_change && !change_done) || change->has_action)
8944 SCAN_PLAYFIELD(x, y)
8946 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8949 if (Feld[x][y] == element)
8951 if (change->can_change && !change_done)
8953 ChangeDelay[x][y] = 1;
8954 ChangeEvent[x][y] = trigger_event;
8956 HandleElementChange(x, y, p);
8958 #if USE_NEW_DELAYED_ACTION
8959 else if (change->has_action)
8961 ExecuteCustomElementAction(x, y, element, p);
8962 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8965 if (change->has_action)
8967 ExecuteCustomElementAction(x, y, element, p);
8968 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8974 if (change->can_change)
8977 change_done_any = TRUE;
8984 return change_done_any;
8987 static boolean CheckElementChangeExt(int x, int y,
8989 int trigger_element,
8994 boolean change_done = FALSE;
8997 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8998 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9001 if (Feld[x][y] == EL_BLOCKED)
9003 Blocked2Moving(x, y, &x, &y);
9004 element = Feld[x][y];
9008 /* check if element has already changed */
9009 if (Feld[x][y] != element)
9012 /* check if element has already changed or is about to change after moving */
9013 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9014 Feld[x][y] != element) ||
9016 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9017 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9018 ChangePage[x][y] != -1)))
9022 for (p = 0; p < element_info[element].num_change_pages; p++)
9024 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9026 boolean check_trigger_element =
9027 (trigger_event == CE_TOUCHING_X ||
9028 trigger_event == CE_HITTING_X ||
9029 trigger_event == CE_HIT_BY_X);
9031 if (change->can_change_or_has_action &&
9032 change->has_event[trigger_event] &&
9033 change->trigger_side & trigger_side &&
9034 change->trigger_player & trigger_player &&
9035 (!check_trigger_element ||
9036 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9038 change->actual_trigger_element = trigger_element;
9039 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9040 change->actual_trigger_side = trigger_side;
9041 change->actual_trigger_ce_value = CustomValue[x][y];
9042 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9044 /* special case: trigger element not at (x,y) position for some events */
9045 if (check_trigger_element)
9057 { 0, 0 }, { 0, 0 }, { 0, 0 },
9061 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9062 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9064 change->actual_trigger_ce_value = CustomValue[xx][yy];
9065 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9068 if (change->can_change && !change_done)
9070 ChangeDelay[x][y] = 1;
9071 ChangeEvent[x][y] = trigger_event;
9073 HandleElementChange(x, y, p);
9077 #if USE_NEW_DELAYED_ACTION
9078 else if (change->has_action)
9080 ExecuteCustomElementAction(x, y, element, p);
9081 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9084 if (change->has_action)
9086 ExecuteCustomElementAction(x, y, element, p);
9087 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9096 static void PlayPlayerSound(struct PlayerInfo *player)
9098 int jx = player->jx, jy = player->jy;
9099 int sound_element = player->artwork_element;
9100 int last_action = player->last_action_waiting;
9101 int action = player->action_waiting;
9103 if (player->is_waiting)
9105 if (action != last_action)
9106 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9108 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9112 if (action != last_action)
9113 StopSound(element_info[sound_element].sound[last_action]);
9115 if (last_action == ACTION_SLEEPING)
9116 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9120 static void PlayAllPlayersSound()
9124 for (i = 0; i < MAX_PLAYERS; i++)
9125 if (stored_player[i].active)
9126 PlayPlayerSound(&stored_player[i]);
9129 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9131 boolean last_waiting = player->is_waiting;
9132 int move_dir = player->MovDir;
9134 player->dir_waiting = move_dir;
9135 player->last_action_waiting = player->action_waiting;
9139 if (!last_waiting) /* not waiting -> waiting */
9141 player->is_waiting = TRUE;
9143 player->frame_counter_bored =
9145 game.player_boring_delay_fixed +
9146 SimpleRND(game.player_boring_delay_random);
9147 player->frame_counter_sleeping =
9149 game.player_sleeping_delay_fixed +
9150 SimpleRND(game.player_sleeping_delay_random);
9153 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9155 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9159 if (game.player_sleeping_delay_fixed +
9160 game.player_sleeping_delay_random > 0 &&
9161 player->anim_delay_counter == 0 &&
9162 player->post_delay_counter == 0 &&
9163 FrameCounter >= player->frame_counter_sleeping)
9164 player->is_sleeping = TRUE;
9165 else if (game.player_boring_delay_fixed +
9166 game.player_boring_delay_random > 0 &&
9167 FrameCounter >= player->frame_counter_bored)
9168 player->is_bored = TRUE;
9170 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9171 player->is_bored ? ACTION_BORING :
9175 if (player->is_sleeping && player->use_murphy)
9177 /* special case for sleeping Murphy when leaning against non-free tile */
9179 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9180 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9181 !IS_MOVING(player->jx - 1, player->jy)))
9183 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9184 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9185 !IS_MOVING(player->jx + 1, player->jy)))
9186 move_dir = MV_RIGHT;
9188 player->is_sleeping = FALSE;
9190 player->dir_waiting = move_dir;
9194 if (player->is_sleeping)
9196 if (player->num_special_action_sleeping > 0)
9198 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9200 int last_special_action = player->special_action_sleeping;
9201 int num_special_action = player->num_special_action_sleeping;
9202 int special_action =
9203 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9204 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9205 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9206 last_special_action + 1 : ACTION_SLEEPING);
9207 int special_graphic =
9208 el_act_dir2img(player->artwork_element, special_action, move_dir);
9210 player->anim_delay_counter =
9211 graphic_info[special_graphic].anim_delay_fixed +
9212 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9213 player->post_delay_counter =
9214 graphic_info[special_graphic].post_delay_fixed +
9215 SimpleRND(graphic_info[special_graphic].post_delay_random);
9217 player->special_action_sleeping = special_action;
9220 if (player->anim_delay_counter > 0)
9222 player->action_waiting = player->special_action_sleeping;
9223 player->anim_delay_counter--;
9225 else if (player->post_delay_counter > 0)
9227 player->post_delay_counter--;
9231 else if (player->is_bored)
9233 if (player->num_special_action_bored > 0)
9235 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9237 int special_action =
9238 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9239 int special_graphic =
9240 el_act_dir2img(player->artwork_element, special_action, move_dir);
9242 player->anim_delay_counter =
9243 graphic_info[special_graphic].anim_delay_fixed +
9244 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9245 player->post_delay_counter =
9246 graphic_info[special_graphic].post_delay_fixed +
9247 SimpleRND(graphic_info[special_graphic].post_delay_random);
9249 player->special_action_bored = special_action;
9252 if (player->anim_delay_counter > 0)
9254 player->action_waiting = player->special_action_bored;
9255 player->anim_delay_counter--;
9257 else if (player->post_delay_counter > 0)
9259 player->post_delay_counter--;
9264 else if (last_waiting) /* waiting -> not waiting */
9266 player->is_waiting = FALSE;
9267 player->is_bored = FALSE;
9268 player->is_sleeping = FALSE;
9270 player->frame_counter_bored = -1;
9271 player->frame_counter_sleeping = -1;
9273 player->anim_delay_counter = 0;
9274 player->post_delay_counter = 0;
9276 player->dir_waiting = player->MovDir;
9277 player->action_waiting = ACTION_DEFAULT;
9279 player->special_action_bored = ACTION_DEFAULT;
9280 player->special_action_sleeping = ACTION_DEFAULT;
9284 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9286 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9287 int left = player_action & JOY_LEFT;
9288 int right = player_action & JOY_RIGHT;
9289 int up = player_action & JOY_UP;
9290 int down = player_action & JOY_DOWN;
9291 int button1 = player_action & JOY_BUTTON_1;
9292 int button2 = player_action & JOY_BUTTON_2;
9293 int dx = (left ? -1 : right ? 1 : 0);
9294 int dy = (up ? -1 : down ? 1 : 0);
9296 if (!player->active || tape.pausing)
9302 snapped = SnapField(player, dx, dy);
9306 dropped = DropElement(player);
9308 moved = MovePlayer(player, dx, dy);
9311 if (tape.single_step && tape.recording && !tape.pausing)
9313 if (button1 || (dropped && !moved))
9315 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9316 SnapField(player, 0, 0); /* stop snapping */
9320 SetPlayerWaiting(player, FALSE);
9322 return player_action;
9326 /* no actions for this player (no input at player's configured device) */
9328 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9329 SnapField(player, 0, 0);
9330 CheckGravityMovementWhenNotMoving(player);
9332 if (player->MovPos == 0)
9333 SetPlayerWaiting(player, TRUE);
9335 if (player->MovPos == 0) /* needed for tape.playing */
9336 player->is_moving = FALSE;
9338 player->is_dropping = FALSE;
9339 player->is_dropping_pressed = FALSE;
9340 player->drop_pressed_delay = 0;
9346 static void CheckLevelTime()
9350 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9352 if (level.native_em_level->lev->home == 0) /* all players at home */
9354 local_player->LevelSolved = TRUE;
9355 AllPlayersGone = TRUE;
9357 level.native_em_level->lev->home = -1;
9360 if (level.native_em_level->ply[0]->alive == 0 &&
9361 level.native_em_level->ply[1]->alive == 0 &&
9362 level.native_em_level->ply[2]->alive == 0 &&
9363 level.native_em_level->ply[3]->alive == 0) /* all dead */
9364 AllPlayersGone = TRUE;
9367 if (TimeFrames >= FRAMES_PER_SECOND)
9372 for (i = 0; i < MAX_PLAYERS; i++)
9374 struct PlayerInfo *player = &stored_player[i];
9376 if (SHIELD_ON(player))
9378 player->shield_normal_time_left--;
9380 if (player->shield_deadly_time_left > 0)
9381 player->shield_deadly_time_left--;
9385 if (!level.use_step_counter)
9393 if (TimeLeft <= 10 && setup.time_limit)
9394 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9396 DrawGameValue_Time(TimeLeft);
9398 if (!TimeLeft && setup.time_limit)
9400 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9401 level.native_em_level->lev->killed_out_of_time = TRUE;
9403 for (i = 0; i < MAX_PLAYERS; i++)
9404 KillPlayer(&stored_player[i]);
9407 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9408 DrawGameValue_Time(TimePlayed);
9410 level.native_em_level->lev->time =
9411 (level.time == 0 ? TimePlayed : TimeLeft);
9414 if (tape.recording || tape.playing)
9415 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9419 void AdvanceFrameAndPlayerCounters(int player_nr)
9424 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9425 FrameCounter, FrameCounter + 1);
9428 /* advance frame counters (global frame counter and time frame counter) */
9432 /* advance player counters (counters for move delay, move animation etc.) */
9433 for (i = 0; i < MAX_PLAYERS; i++)
9435 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9436 int move_delay_value = stored_player[i].move_delay_value;
9437 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9439 if (!advance_player_counters) /* not all players may be affected */
9442 #if USE_NEW_PLAYER_ANIM
9443 if (move_frames == 0) /* less than one move per game frame */
9445 int stepsize = TILEX / move_delay_value;
9446 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9447 int count = (stored_player[i].is_moving ?
9448 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9450 if (count % delay == 0)
9455 stored_player[i].Frame += move_frames;
9457 if (stored_player[i].MovPos != 0)
9458 stored_player[i].StepFrame += move_frames;
9460 if (stored_player[i].move_delay > 0)
9461 stored_player[i].move_delay--;
9463 /* due to bugs in previous versions, counter must count up, not down */
9464 if (stored_player[i].push_delay != -1)
9465 stored_player[i].push_delay++;
9467 if (stored_player[i].drop_delay > 0)
9468 stored_player[i].drop_delay--;
9470 if (stored_player[i].is_dropping_pressed)
9471 stored_player[i].drop_pressed_delay++;
9475 void StartGameActions(boolean init_network_game, boolean record_tape,
9478 unsigned long new_random_seed = InitRND(random_seed);
9481 TapeStartRecording(new_random_seed);
9483 #if defined(NETWORK_AVALIABLE)
9484 if (init_network_game)
9486 SendToServer_StartPlaying();
9494 game_status = GAME_MODE_PLAYING;
9501 static unsigned long game_frame_delay = 0;
9502 unsigned long game_frame_delay_value;
9503 byte *recorded_player_action;
9504 byte summarized_player_action = 0;
9505 byte tape_action[MAX_PLAYERS];
9508 if (game.restart_level)
9509 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9511 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9513 if (level.native_em_level->lev->home == 0) /* all players at home */
9515 local_player->LevelSolved = TRUE;
9516 AllPlayersGone = TRUE;
9518 level.native_em_level->lev->home = -1;
9521 if (level.native_em_level->ply[0]->alive == 0 &&
9522 level.native_em_level->ply[1]->alive == 0 &&
9523 level.native_em_level->ply[2]->alive == 0 &&
9524 level.native_em_level->ply[3]->alive == 0) /* all dead */
9525 AllPlayersGone = TRUE;
9528 if (local_player->LevelSolved)
9531 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9534 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9537 game_frame_delay_value =
9538 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9540 if (tape.playing && tape.warp_forward && !tape.pausing)
9541 game_frame_delay_value = 0;
9543 /* ---------- main game synchronization point ---------- */
9545 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9547 if (network_playing && !network_player_action_received)
9549 /* try to get network player actions in time */
9551 #if defined(NETWORK_AVALIABLE)
9552 /* last chance to get network player actions without main loop delay */
9556 /* game was quit by network peer */
9557 if (game_status != GAME_MODE_PLAYING)
9560 if (!network_player_action_received)
9561 return; /* failed to get network player actions in time */
9563 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9569 /* at this point we know that we really continue executing the game */
9572 network_player_action_received = FALSE;
9575 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9577 if (tape.set_centered_player)
9579 game.centered_player_nr_next = tape.centered_player_nr_next;
9580 game.set_centered_player = TRUE;
9583 for (i = 0; i < MAX_PLAYERS; i++)
9585 summarized_player_action |= stored_player[i].action;
9587 if (!network_playing)
9588 stored_player[i].effective_action = stored_player[i].action;
9591 #if defined(NETWORK_AVALIABLE)
9592 if (network_playing)
9593 SendToServer_MovePlayer(summarized_player_action);
9596 if (!options.network && !setup.team_mode)
9597 local_player->effective_action = summarized_player_action;
9599 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9601 for (i = 0; i < MAX_PLAYERS; i++)
9602 stored_player[i].effective_action =
9603 (i == game.centered_player_nr ? summarized_player_action : 0);
9606 if (recorded_player_action != NULL)
9607 for (i = 0; i < MAX_PLAYERS; i++)
9608 stored_player[i].effective_action = recorded_player_action[i];
9610 for (i = 0; i < MAX_PLAYERS; i++)
9612 tape_action[i] = stored_player[i].effective_action;
9614 /* (this can only happen in the R'n'D game engine) */
9615 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9616 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9619 /* only record actions from input devices, but not programmed actions */
9621 TapeRecordAction(tape_action);
9623 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9625 GameActions_EM_Main();
9633 void GameActions_EM_Main()
9635 byte effective_action[MAX_PLAYERS];
9636 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9639 for (i = 0; i < MAX_PLAYERS; i++)
9640 effective_action[i] = stored_player[i].effective_action;
9642 GameActions_EM(effective_action, warp_mode);
9646 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9649 void GameActions_RND()
9651 int magic_wall_x = 0, magic_wall_y = 0;
9652 int i, x, y, element, graphic;
9654 InitPlayfieldScanModeVars();
9656 #if USE_ONE_MORE_CHANGE_PER_FRAME
9657 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9659 SCAN_PLAYFIELD(x, y)
9661 ChangeCount[x][y] = 0;
9662 ChangeEvent[x][y] = -1;
9668 if (game.set_centered_player)
9670 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9672 /* switching to "all players" only possible if all players fit to screen */
9673 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9675 game.centered_player_nr_next = game.centered_player_nr;
9676 game.set_centered_player = FALSE;
9679 /* do not switch focus to non-existing (or non-active) player */
9680 if (game.centered_player_nr_next >= 0 &&
9681 !stored_player[game.centered_player_nr_next].active)
9683 game.centered_player_nr_next = game.centered_player_nr;
9684 game.set_centered_player = FALSE;
9688 if (game.set_centered_player &&
9689 ScreenMovPos == 0) /* screen currently aligned at tile position */
9693 if (game.centered_player_nr_next == -1)
9695 setScreenCenteredToAllPlayers(&sx, &sy);
9699 sx = stored_player[game.centered_player_nr_next].jx;
9700 sy = stored_player[game.centered_player_nr_next].jy;
9703 game.centered_player_nr = game.centered_player_nr_next;
9704 game.set_centered_player = FALSE;
9706 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9707 DrawGameDoorValues();
9711 for (i = 0; i < MAX_PLAYERS; i++)
9713 int actual_player_action = stored_player[i].effective_action;
9716 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9717 - rnd_equinox_tetrachloride 048
9718 - rnd_equinox_tetrachloride_ii 096
9719 - rnd_emanuel_schmieg 002
9720 - doctor_sloan_ww 001, 020
9722 if (stored_player[i].MovPos == 0)
9723 CheckGravityMovement(&stored_player[i]);
9726 /* overwrite programmed action with tape action */
9727 if (stored_player[i].programmed_action)
9728 actual_player_action = stored_player[i].programmed_action;
9731 PlayerActions(&stored_player[i], actual_player_action);
9733 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9735 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9736 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9739 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9743 network_player_action_received = FALSE;
9746 ScrollScreen(NULL, SCROLL_GO_ON);
9748 /* for backwards compatibility, the following code emulates a fixed bug that
9749 occured when pushing elements (causing elements that just made their last
9750 pushing step to already (if possible) make their first falling step in the
9751 same game frame, which is bad); this code is also needed to use the famous
9752 "spring push bug" which is used in older levels and might be wanted to be
9753 used also in newer levels, but in this case the buggy pushing code is only
9754 affecting the "spring" element and no other elements */
9756 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9758 for (i = 0; i < MAX_PLAYERS; i++)
9760 struct PlayerInfo *player = &stored_player[i];
9764 if (player->active && player->is_pushing && player->is_moving &&
9766 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9767 Feld[x][y] == EL_SPRING))
9769 ContinueMoving(x, y);
9771 /* continue moving after pushing (this is actually a bug) */
9772 if (!IS_MOVING(x, y))
9781 SCAN_PLAYFIELD(x, y)
9783 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9786 ChangeCount[x][y] = 0;
9787 ChangeEvent[x][y] = -1;
9789 /* this must be handled before main playfield loop */
9790 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9793 if (MovDelay[x][y] <= 0)
9797 #if USE_NEW_SNAP_DELAY
9798 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9801 if (MovDelay[x][y] <= 0)
9804 DrawLevelField(x, y);
9806 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9812 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9814 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9815 printf("GameActions(): This should never happen!\n");
9817 ChangePage[x][y] = -1;
9822 if (WasJustMoving[x][y] > 0)
9823 WasJustMoving[x][y]--;
9824 if (WasJustFalling[x][y] > 0)
9825 WasJustFalling[x][y]--;
9826 if (CheckCollision[x][y] > 0)
9827 CheckCollision[x][y]--;
9831 /* reset finished pushing action (not done in ContinueMoving() to allow
9832 continuous pushing animation for elements with zero push delay) */
9833 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9835 ResetGfxAnimation(x, y);
9836 DrawLevelField(x, y);
9840 if (IS_BLOCKED(x, y))
9844 Blocked2Moving(x, y, &oldx, &oldy);
9845 if (!IS_MOVING(oldx, oldy))
9847 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9848 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9849 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9850 printf("GameActions(): This should never happen!\n");
9857 SCAN_PLAYFIELD(x, y)
9859 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9862 element = Feld[x][y];
9863 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9866 printf("::: %d,%d\n", x, y);
9868 if (element == EL_ROCK)
9869 printf("::: Yo man! Rocks can fall!\n");
9873 ResetGfxFrame(x, y, TRUE);
9875 if (graphic_info[graphic].anim_global_sync)
9876 GfxFrame[x][y] = FrameCounter;
9877 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9879 int old_gfx_frame = GfxFrame[x][y];
9881 GfxFrame[x][y] = CustomValue[x][y];
9884 if (GfxFrame[x][y] != old_gfx_frame)
9886 DrawLevelGraphicAnimation(x, y, graphic);
9888 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9890 int old_gfx_frame = GfxFrame[x][y];
9892 GfxFrame[x][y] = element_info[element].collect_score;
9895 if (GfxFrame[x][y] != old_gfx_frame)
9897 DrawLevelGraphicAnimation(x, y, graphic);
9899 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9901 int old_gfx_frame = GfxFrame[x][y];
9903 GfxFrame[x][y] = ChangeDelay[x][y];
9906 if (GfxFrame[x][y] != old_gfx_frame)
9908 DrawLevelGraphicAnimation(x, y, graphic);
9912 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9913 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9914 ResetRandomAnimationValue(x, y);
9916 SetRandomAnimationValue(x, y);
9918 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9920 if (IS_INACTIVE(element))
9922 if (IS_ANIMATED(graphic))
9923 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9928 /* this may take place after moving, so 'element' may have changed */
9929 if (IS_CHANGING(x, y) &&
9930 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9932 int page = element_info[element].event_page_nr[CE_DELAY];
9934 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9938 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9942 if (element == EL_CUSTOM_255)
9943 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9947 HandleElementChange(x, y, page);
9949 if (CAN_CHANGE(element))
9950 HandleElementChange(x, y, page);
9952 if (HAS_ACTION(element))
9953 ExecuteCustomElementAction(x, y, element, page);
9958 element = Feld[x][y];
9959 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9962 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9966 element = Feld[x][y];
9967 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9969 if (IS_ANIMATED(graphic) &&
9972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9974 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9975 EdelsteinFunkeln(x, y);
9977 else if ((element == EL_ACID ||
9978 element == EL_EXIT_OPEN ||
9979 element == EL_SP_EXIT_OPEN ||
9980 element == EL_SP_TERMINAL ||
9981 element == EL_SP_TERMINAL_ACTIVE ||
9982 element == EL_EXTRA_TIME ||
9983 element == EL_SHIELD_NORMAL ||
9984 element == EL_SHIELD_DEADLY) &&
9985 IS_ANIMATED(graphic))
9986 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9987 else if (IS_MOVING(x, y))
9988 ContinueMoving(x, y);
9989 else if (IS_ACTIVE_BOMB(element))
9990 CheckDynamite(x, y);
9991 else if (element == EL_AMOEBA_GROWING)
9992 AmoebeWaechst(x, y);
9993 else if (element == EL_AMOEBA_SHRINKING)
9994 AmoebaDisappearing(x, y);
9996 #if !USE_NEW_AMOEBA_CODE
9997 else if (IS_AMOEBALIVE(element))
9998 AmoebeAbleger(x, y);
10001 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10003 else if (element == EL_EXIT_CLOSED)
10005 else if (element == EL_SP_EXIT_CLOSED)
10007 else if (element == EL_EXPANDABLE_WALL_GROWING)
10008 MauerWaechst(x, y);
10009 else if (element == EL_EXPANDABLE_WALL ||
10010 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10011 element == EL_EXPANDABLE_WALL_VERTICAL ||
10012 element == EL_EXPANDABLE_WALL_ANY)
10013 MauerAbleger(x, y);
10014 else if (element == EL_FLAMES)
10015 CheckForDragon(x, y);
10016 else if (element == EL_EXPLOSION)
10017 ; /* drawing of correct explosion animation is handled separately */
10018 else if (element == EL_ELEMENT_SNAPPING ||
10019 element == EL_DIAGONAL_SHRINKING ||
10020 element == EL_DIAGONAL_GROWING)
10023 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10025 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10028 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10029 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10032 if (element == EL_CUSTOM_255 ||
10033 element == EL_CUSTOM_256)
10034 DrawLevelGraphicAnimation(x, y, graphic);
10037 if (IS_BELT_ACTIVE(element))
10038 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10040 if (game.magic_wall_active)
10042 int jx = local_player->jx, jy = local_player->jy;
10044 /* play the element sound at the position nearest to the player */
10045 if ((element == EL_MAGIC_WALL_FULL ||
10046 element == EL_MAGIC_WALL_ACTIVE ||
10047 element == EL_MAGIC_WALL_EMPTYING ||
10048 element == EL_BD_MAGIC_WALL_FULL ||
10049 element == EL_BD_MAGIC_WALL_ACTIVE ||
10050 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10051 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10059 #if USE_NEW_AMOEBA_CODE
10060 /* new experimental amoeba growth stuff */
10061 if (!(FrameCounter % 8))
10063 static unsigned long random = 1684108901;
10065 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10067 x = RND(lev_fieldx);
10068 y = RND(lev_fieldy);
10069 element = Feld[x][y];
10071 if (!IS_PLAYER(x,y) &&
10072 (element == EL_EMPTY ||
10073 CAN_GROW_INTO(element) ||
10074 element == EL_QUICKSAND_EMPTY ||
10075 element == EL_ACID_SPLASH_LEFT ||
10076 element == EL_ACID_SPLASH_RIGHT))
10078 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10079 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10080 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10081 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10082 Feld[x][y] = EL_AMOEBA_DROP;
10085 random = random * 129 + 1;
10091 if (game.explosions_delayed)
10094 game.explosions_delayed = FALSE;
10097 SCAN_PLAYFIELD(x, y)
10099 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10102 element = Feld[x][y];
10104 if (ExplodeField[x][y])
10105 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10106 else if (element == EL_EXPLOSION)
10107 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10109 ExplodeField[x][y] = EX_TYPE_NONE;
10112 game.explosions_delayed = TRUE;
10115 if (game.magic_wall_active)
10117 if (!(game.magic_wall_time_left % 4))
10119 int element = Feld[magic_wall_x][magic_wall_y];
10121 if (element == EL_BD_MAGIC_WALL_FULL ||
10122 element == EL_BD_MAGIC_WALL_ACTIVE ||
10123 element == EL_BD_MAGIC_WALL_EMPTYING)
10124 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10126 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10129 if (game.magic_wall_time_left > 0)
10131 game.magic_wall_time_left--;
10132 if (!game.magic_wall_time_left)
10135 SCAN_PLAYFIELD(x, y)
10137 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10140 element = Feld[x][y];
10142 if (element == EL_MAGIC_WALL_ACTIVE ||
10143 element == EL_MAGIC_WALL_FULL)
10145 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10146 DrawLevelField(x, y);
10148 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10149 element == EL_BD_MAGIC_WALL_FULL)
10151 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10152 DrawLevelField(x, y);
10156 game.magic_wall_active = FALSE;
10161 if (game.light_time_left > 0)
10163 game.light_time_left--;
10165 if (game.light_time_left == 0)
10166 RedrawAllLightSwitchesAndInvisibleElements();
10169 if (game.timegate_time_left > 0)
10171 game.timegate_time_left--;
10173 if (game.timegate_time_left == 0)
10174 CloseAllOpenTimegates();
10177 if (game.lenses_time_left > 0)
10179 game.lenses_time_left--;
10181 if (game.lenses_time_left == 0)
10182 RedrawAllInvisibleElementsForLenses();
10185 if (game.magnify_time_left > 0)
10187 game.magnify_time_left--;
10189 if (game.magnify_time_left == 0)
10190 RedrawAllInvisibleElementsForMagnifier();
10193 for (i = 0; i < MAX_PLAYERS; i++)
10195 struct PlayerInfo *player = &stored_player[i];
10197 if (SHIELD_ON(player))
10199 if (player->shield_deadly_time_left)
10200 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10201 else if (player->shield_normal_time_left)
10202 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10209 PlayAllPlayersSound();
10211 if (options.debug) /* calculate frames per second */
10213 static unsigned long fps_counter = 0;
10214 static int fps_frames = 0;
10215 unsigned long fps_delay_ms = Counter() - fps_counter;
10219 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10221 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10224 fps_counter = Counter();
10227 redraw_mask |= REDRAW_FPS;
10230 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10232 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10234 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10236 local_player->show_envelope = 0;
10239 /* use random number generator in every frame to make it less predictable */
10240 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10244 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10246 int min_x = x, min_y = y, max_x = x, max_y = y;
10249 for (i = 0; i < MAX_PLAYERS; i++)
10251 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10253 if (!stored_player[i].active || &stored_player[i] == player)
10256 min_x = MIN(min_x, jx);
10257 min_y = MIN(min_y, jy);
10258 max_x = MAX(max_x, jx);
10259 max_y = MAX(max_y, jy);
10262 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10265 static boolean AllPlayersInVisibleScreen()
10269 for (i = 0; i < MAX_PLAYERS; i++)
10271 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10273 if (!stored_player[i].active)
10276 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10283 void ScrollLevel(int dx, int dy)
10285 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10288 BlitBitmap(drawto_field, drawto_field,
10289 FX + TILEX * (dx == -1) - softscroll_offset,
10290 FY + TILEY * (dy == -1) - softscroll_offset,
10291 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10292 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10293 FX + TILEX * (dx == 1) - softscroll_offset,
10294 FY + TILEY * (dy == 1) - softscroll_offset);
10298 x = (dx == 1 ? BX1 : BX2);
10299 for (y = BY1; y <= BY2; y++)
10300 DrawScreenField(x, y);
10305 y = (dy == 1 ? BY1 : BY2);
10306 for (x = BX1; x <= BX2; x++)
10307 DrawScreenField(x, y);
10310 redraw_mask |= REDRAW_FIELD;
10313 static boolean canFallDown(struct PlayerInfo *player)
10315 int jx = player->jx, jy = player->jy;
10317 return (IN_LEV_FIELD(jx, jy + 1) &&
10318 (IS_FREE(jx, jy + 1) ||
10319 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10320 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10321 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10324 static boolean canPassField(int x, int y, int move_dir)
10326 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10327 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10328 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10329 int nextx = x + dx;
10330 int nexty = y + dy;
10331 int element = Feld[x][y];
10333 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10334 !CAN_MOVE(element) &&
10335 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10336 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10337 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10340 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10342 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10343 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10344 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10348 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10349 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10350 (IS_DIGGABLE(Feld[newx][newy]) ||
10351 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10352 canPassField(newx, newy, move_dir)));
10355 static void CheckGravityMovement(struct PlayerInfo *player)
10357 #if USE_PLAYER_GRAVITY
10358 if (player->gravity && !player->programmed_action)
10360 if (game.gravity && !player->programmed_action)
10363 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10364 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10365 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10366 int jx = player->jx, jy = player->jy;
10367 boolean player_is_moving_to_valid_field =
10368 (!player_is_snapping &&
10369 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10370 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10371 boolean player_can_fall_down = canFallDown(player);
10373 if (player_can_fall_down &&
10374 !player_is_moving_to_valid_field)
10375 player->programmed_action = MV_DOWN;
10379 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10381 return CheckGravityMovement(player);
10383 #if USE_PLAYER_GRAVITY
10384 if (player->gravity && !player->programmed_action)
10386 if (game.gravity && !player->programmed_action)
10389 int jx = player->jx, jy = player->jy;
10390 boolean field_under_player_is_free =
10391 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10392 boolean player_is_standing_on_valid_field =
10393 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10394 (IS_WALKABLE(Feld[jx][jy]) &&
10395 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10397 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10398 player->programmed_action = MV_DOWN;
10403 MovePlayerOneStep()
10404 -----------------------------------------------------------------------------
10405 dx, dy: direction (non-diagonal) to try to move the player to
10406 real_dx, real_dy: direction as read from input device (can be diagonal)
10409 boolean MovePlayerOneStep(struct PlayerInfo *player,
10410 int dx, int dy, int real_dx, int real_dy)
10412 int jx = player->jx, jy = player->jy;
10413 int new_jx = jx + dx, new_jy = jy + dy;
10414 #if !USE_FIXED_DONT_RUN_INTO
10418 boolean player_can_move = !player->cannot_move;
10420 if (!player->active || (!dx && !dy))
10421 return MP_NO_ACTION;
10423 player->MovDir = (dx < 0 ? MV_LEFT :
10424 dx > 0 ? MV_RIGHT :
10426 dy > 0 ? MV_DOWN : MV_NONE);
10428 if (!IN_LEV_FIELD(new_jx, new_jy))
10429 return MP_NO_ACTION;
10431 if (!player_can_move)
10434 if (player->MovPos == 0)
10436 player->is_moving = FALSE;
10437 player->is_digging = FALSE;
10438 player->is_collecting = FALSE;
10439 player->is_snapping = FALSE;
10440 player->is_pushing = FALSE;
10443 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10444 SnapField(player, 0, 0);
10448 return MP_NO_ACTION;
10453 if (!options.network && game.centered_player_nr == -1 &&
10454 !AllPlayersInSight(player, new_jx, new_jy))
10455 return MP_NO_ACTION;
10457 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10458 return MP_NO_ACTION;
10461 #if !USE_FIXED_DONT_RUN_INTO
10462 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10464 /* (moved to DigField()) */
10465 if (player_can_move && DONT_RUN_INTO(element))
10467 if (element == EL_ACID && dx == 0 && dy == 1)
10469 SplashAcid(new_jx, new_jy);
10470 Feld[jx][jy] = EL_PLAYER_1;
10471 InitMovingField(jx, jy, MV_DOWN);
10472 Store[jx][jy] = EL_ACID;
10473 ContinueMoving(jx, jy);
10474 BuryPlayer(player);
10477 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10483 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10485 #if USE_FIXED_DONT_RUN_INTO
10486 if (can_move == MP_DONT_RUN_INTO)
10490 if (can_move != MP_MOVING)
10493 #if USE_FIXED_DONT_RUN_INTO
10496 /* check if DigField() has caused relocation of the player */
10497 if (player->jx != jx || player->jy != jy)
10498 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10500 StorePlayer[jx][jy] = 0;
10501 player->last_jx = jx;
10502 player->last_jy = jy;
10503 player->jx = new_jx;
10504 player->jy = new_jy;
10505 StorePlayer[new_jx][new_jy] = player->element_nr;
10507 if (player->move_delay_value_next != -1)
10509 player->move_delay_value = player->move_delay_value_next;
10510 player->move_delay_value_next = -1;
10514 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10516 player->step_counter++;
10518 PlayerVisit[jx][jy] = FrameCounter;
10520 ScrollPlayer(player, SCROLL_INIT);
10525 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10527 int jx = player->jx, jy = player->jy;
10528 int old_jx = jx, old_jy = jy;
10529 int moved = MP_NO_ACTION;
10531 if (!player->active)
10536 if (player->MovPos == 0)
10538 player->is_moving = FALSE;
10539 player->is_digging = FALSE;
10540 player->is_collecting = FALSE;
10541 player->is_snapping = FALSE;
10542 player->is_pushing = FALSE;
10548 if (player->move_delay > 0)
10551 player->move_delay = -1; /* set to "uninitialized" value */
10553 /* store if player is automatically moved to next field */
10554 player->is_auto_moving = (player->programmed_action != MV_NONE);
10556 /* remove the last programmed player action */
10557 player->programmed_action = 0;
10559 if (player->MovPos)
10561 /* should only happen if pre-1.2 tape recordings are played */
10562 /* this is only for backward compatibility */
10564 int original_move_delay_value = player->move_delay_value;
10567 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10571 /* scroll remaining steps with finest movement resolution */
10572 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10574 while (player->MovPos)
10576 ScrollPlayer(player, SCROLL_GO_ON);
10577 ScrollScreen(NULL, SCROLL_GO_ON);
10579 AdvanceFrameAndPlayerCounters(player->index_nr);
10585 player->move_delay_value = original_move_delay_value;
10588 if (player->last_move_dir & MV_HORIZONTAL)
10590 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10591 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10595 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10596 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10603 if (moved & MP_MOVING && !ScreenMovPos &&
10604 (player->index_nr == game.centered_player_nr ||
10605 game.centered_player_nr == -1))
10607 if (moved & MP_MOVING && !ScreenMovPos &&
10608 (player == local_player || !options.network))
10611 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10612 int offset = (setup.scroll_delay ? 3 : 0);
10614 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10616 /* actual player has left the screen -- scroll in that direction */
10617 if (jx != old_jx) /* player has moved horizontally */
10618 scroll_x += (jx - old_jx);
10619 else /* player has moved vertically */
10620 scroll_y += (jy - old_jy);
10624 if (jx != old_jx) /* player has moved horizontally */
10626 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10627 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10628 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10630 /* don't scroll over playfield boundaries */
10631 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10632 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10634 /* don't scroll more than one field at a time */
10635 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10637 /* don't scroll against the player's moving direction */
10638 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10639 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10640 scroll_x = old_scroll_x;
10642 else /* player has moved vertically */
10644 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10645 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10646 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10648 /* don't scroll over playfield boundaries */
10649 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10650 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10652 /* don't scroll more than one field at a time */
10653 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10655 /* don't scroll against the player's moving direction */
10656 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10657 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10658 scroll_y = old_scroll_y;
10662 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10665 if (!options.network && game.centered_player_nr == -1 &&
10666 !AllPlayersInVisibleScreen())
10668 scroll_x = old_scroll_x;
10669 scroll_y = old_scroll_y;
10673 if (!options.network && !AllPlayersInVisibleScreen())
10675 scroll_x = old_scroll_x;
10676 scroll_y = old_scroll_y;
10681 ScrollScreen(player, SCROLL_INIT);
10682 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10687 player->StepFrame = 0;
10689 if (moved & MP_MOVING)
10691 if (old_jx != jx && old_jy == jy)
10692 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10693 else if (old_jx == jx && old_jy != jy)
10694 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10696 DrawLevelField(jx, jy); /* for "crumbled sand" */
10698 player->last_move_dir = player->MovDir;
10699 player->is_moving = TRUE;
10700 player->is_snapping = FALSE;
10701 player->is_switching = FALSE;
10702 player->is_dropping = FALSE;
10703 player->is_dropping_pressed = FALSE;
10704 player->drop_pressed_delay = 0;
10708 CheckGravityMovementWhenNotMoving(player);
10710 player->is_moving = FALSE;
10712 /* at this point, the player is allowed to move, but cannot move right now
10713 (e.g. because of something blocking the way) -- ensure that the player
10714 is also allowed to move in the next frame (in old versions before 3.1.1,
10715 the player was forced to wait again for eight frames before next try) */
10717 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10718 player->move_delay = 0; /* allow direct movement in the next frame */
10721 if (player->move_delay == -1) /* not yet initialized by DigField() */
10722 player->move_delay = player->move_delay_value;
10724 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10726 TestIfPlayerTouchesBadThing(jx, jy);
10727 TestIfPlayerTouchesCustomElement(jx, jy);
10730 if (!player->active)
10731 RemovePlayer(player);
10736 void ScrollPlayer(struct PlayerInfo *player, int mode)
10738 int jx = player->jx, jy = player->jy;
10739 int last_jx = player->last_jx, last_jy = player->last_jy;
10740 int move_stepsize = TILEX / player->move_delay_value;
10742 #if USE_NEW_PLAYER_SPEED
10743 if (!player->active)
10746 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10749 if (!player->active || player->MovPos == 0)
10753 if (mode == SCROLL_INIT)
10755 player->actual_frame_counter = FrameCounter;
10756 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10758 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10759 Feld[last_jx][last_jy] == EL_EMPTY)
10761 int last_field_block_delay = 0; /* start with no blocking at all */
10762 int block_delay_adjustment = player->block_delay_adjustment;
10764 /* if player blocks last field, add delay for exactly one move */
10765 if (player->block_last_field)
10767 last_field_block_delay += player->move_delay_value;
10769 /* when blocking enabled, prevent moving up despite gravity */
10770 #if USE_PLAYER_GRAVITY
10771 if (player->gravity && player->MovDir == MV_UP)
10772 block_delay_adjustment = -1;
10774 if (game.gravity && player->MovDir == MV_UP)
10775 block_delay_adjustment = -1;
10779 /* add block delay adjustment (also possible when not blocking) */
10780 last_field_block_delay += block_delay_adjustment;
10782 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10783 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10786 #if USE_NEW_PLAYER_SPEED
10787 if (player->MovPos != 0) /* player has not yet reached destination */
10793 else if (!FrameReached(&player->actual_frame_counter, 1))
10797 printf("::: player->MovPos: %d -> %d\n",
10799 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10802 #if USE_NEW_PLAYER_SPEED
10803 if (player->MovPos != 0)
10805 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10806 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10808 /* before DrawPlayer() to draw correct player graphic for this case */
10809 if (player->MovPos == 0)
10810 CheckGravityMovement(player);
10813 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10814 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10816 /* before DrawPlayer() to draw correct player graphic for this case */
10817 if (player->MovPos == 0)
10818 CheckGravityMovement(player);
10821 if (player->MovPos == 0) /* player reached destination field */
10824 printf("::: player reached destination field\n");
10827 if (player->move_delay_reset_counter > 0)
10829 player->move_delay_reset_counter--;
10831 if (player->move_delay_reset_counter == 0)
10833 /* continue with normal speed after quickly moving through gate */
10834 HALVE_PLAYER_SPEED(player);
10836 /* be able to make the next move without delay */
10837 player->move_delay = 0;
10841 player->last_jx = jx;
10842 player->last_jy = jy;
10844 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10845 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10846 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10848 DrawPlayer(player); /* needed here only to cleanup last field */
10849 RemovePlayer(player);
10851 if (local_player->friends_still_needed == 0 ||
10852 IS_SP_ELEMENT(Feld[jx][jy]))
10853 player->LevelSolved = player->GameOver = TRUE;
10856 /* this breaks one level: "machine", level 000 */
10858 int move_direction = player->MovDir;
10859 int enter_side = MV_DIR_OPPOSITE(move_direction);
10860 int leave_side = move_direction;
10861 int old_jx = last_jx;
10862 int old_jy = last_jy;
10863 int old_element = Feld[old_jx][old_jy];
10864 int new_element = Feld[jx][jy];
10866 if (IS_CUSTOM_ELEMENT(old_element))
10867 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10869 player->index_bit, leave_side);
10871 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10872 CE_PLAYER_LEAVES_X,
10873 player->index_bit, leave_side);
10875 if (IS_CUSTOM_ELEMENT(new_element))
10876 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10877 player->index_bit, enter_side);
10879 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10880 CE_PLAYER_ENTERS_X,
10881 player->index_bit, enter_side);
10883 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10884 CE_MOVE_OF_X, move_direction);
10887 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10889 TestIfPlayerTouchesBadThing(jx, jy);
10890 TestIfPlayerTouchesCustomElement(jx, jy);
10892 /* needed because pushed element has not yet reached its destination,
10893 so it would trigger a change event at its previous field location */
10894 if (!player->is_pushing)
10895 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10897 if (!player->active)
10898 RemovePlayer(player);
10901 if (level.use_step_counter)
10911 if (TimeLeft <= 10 && setup.time_limit)
10912 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10914 DrawGameValue_Time(TimeLeft);
10916 if (!TimeLeft && setup.time_limit)
10917 for (i = 0; i < MAX_PLAYERS; i++)
10918 KillPlayer(&stored_player[i]);
10920 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10921 DrawGameValue_Time(TimePlayed);
10924 if (tape.single_step && tape.recording && !tape.pausing &&
10925 !player->programmed_action)
10926 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10930 void ScrollScreen(struct PlayerInfo *player, int mode)
10932 static unsigned long screen_frame_counter = 0;
10934 if (mode == SCROLL_INIT)
10936 /* set scrolling step size according to actual player's moving speed */
10937 ScrollStepSize = TILEX / player->move_delay_value;
10939 screen_frame_counter = FrameCounter;
10940 ScreenMovDir = player->MovDir;
10941 ScreenMovPos = player->MovPos;
10942 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10945 else if (!FrameReached(&screen_frame_counter, 1))
10950 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10951 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10952 redraw_mask |= REDRAW_FIELD;
10955 ScreenMovDir = MV_NONE;
10958 void TestIfPlayerTouchesCustomElement(int x, int y)
10960 static int xy[4][2] =
10967 static int trigger_sides[4][2] =
10969 /* center side border side */
10970 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10971 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10972 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10973 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10975 static int touch_dir[4] =
10977 MV_LEFT | MV_RIGHT,
10982 int center_element = Feld[x][y]; /* should always be non-moving! */
10985 for (i = 0; i < NUM_DIRECTIONS; i++)
10987 int xx = x + xy[i][0];
10988 int yy = y + xy[i][1];
10989 int center_side = trigger_sides[i][0];
10990 int border_side = trigger_sides[i][1];
10991 int border_element;
10993 if (!IN_LEV_FIELD(xx, yy))
10996 if (IS_PLAYER(x, y))
10998 struct PlayerInfo *player = PLAYERINFO(x, y);
11000 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11001 border_element = Feld[xx][yy]; /* may be moving! */
11002 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11003 border_element = Feld[xx][yy];
11004 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11005 border_element = MovingOrBlocked2Element(xx, yy);
11007 continue; /* center and border element do not touch */
11009 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11010 player->index_bit, border_side);
11011 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11012 CE_PLAYER_TOUCHES_X,
11013 player->index_bit, border_side);
11015 else if (IS_PLAYER(xx, yy))
11017 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11019 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11021 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11022 continue; /* center and border element do not touch */
11025 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11026 player->index_bit, center_side);
11027 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11028 CE_PLAYER_TOUCHES_X,
11029 player->index_bit, center_side);
11035 #if USE_ELEMENT_TOUCHING_BUGFIX
11037 void TestIfElementTouchesCustomElement(int x, int y)
11039 static int xy[4][2] =
11046 static int trigger_sides[4][2] =
11048 /* center side border side */
11049 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11050 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11051 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11052 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11054 static int touch_dir[4] =
11056 MV_LEFT | MV_RIGHT,
11061 boolean change_center_element = FALSE;
11062 int center_element = Feld[x][y]; /* should always be non-moving! */
11063 int border_element_old[NUM_DIRECTIONS];
11066 for (i = 0; i < NUM_DIRECTIONS; i++)
11068 int xx = x + xy[i][0];
11069 int yy = y + xy[i][1];
11070 int border_element;
11072 border_element_old[i] = -1;
11074 if (!IN_LEV_FIELD(xx, yy))
11077 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11078 border_element = Feld[xx][yy]; /* may be moving! */
11079 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11080 border_element = Feld[xx][yy];
11081 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11082 border_element = MovingOrBlocked2Element(xx, yy);
11084 continue; /* center and border element do not touch */
11086 border_element_old[i] = border_element;
11089 for (i = 0; i < NUM_DIRECTIONS; i++)
11091 int xx = x + xy[i][0];
11092 int yy = y + xy[i][1];
11093 int center_side = trigger_sides[i][0];
11094 int border_element = border_element_old[i];
11096 if (border_element == -1)
11099 /* check for change of border element */
11100 CheckElementChangeBySide(xx, yy, border_element, center_element,
11101 CE_TOUCHING_X, center_side);
11104 for (i = 0; i < NUM_DIRECTIONS; i++)
11106 int border_side = trigger_sides[i][1];
11107 int border_element = border_element_old[i];
11109 if (border_element == -1)
11112 /* check for change of center element (but change it only once) */
11113 if (!change_center_element)
11114 change_center_element =
11115 CheckElementChangeBySide(x, y, center_element, border_element,
11116 CE_TOUCHING_X, border_side);
11122 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11124 static int xy[4][2] =
11131 static int trigger_sides[4][2] =
11133 /* center side border side */
11134 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11135 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11136 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11137 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11139 static int touch_dir[4] =
11141 MV_LEFT | MV_RIGHT,
11146 boolean change_center_element = FALSE;
11147 int center_element = Feld[x][y]; /* should always be non-moving! */
11150 for (i = 0; i < NUM_DIRECTIONS; i++)
11152 int xx = x + xy[i][0];
11153 int yy = y + xy[i][1];
11154 int center_side = trigger_sides[i][0];
11155 int border_side = trigger_sides[i][1];
11156 int border_element;
11158 if (!IN_LEV_FIELD(xx, yy))
11161 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11162 border_element = Feld[xx][yy]; /* may be moving! */
11163 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11164 border_element = Feld[xx][yy];
11165 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11166 border_element = MovingOrBlocked2Element(xx, yy);
11168 continue; /* center and border element do not touch */
11170 /* check for change of center element (but change it only once) */
11171 if (!change_center_element)
11172 change_center_element =
11173 CheckElementChangeBySide(x, y, center_element, border_element,
11174 CE_TOUCHING_X, border_side);
11176 /* check for change of border element */
11177 CheckElementChangeBySide(xx, yy, border_element, center_element,
11178 CE_TOUCHING_X, center_side);
11184 void TestIfElementHitsCustomElement(int x, int y, int direction)
11186 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11187 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11188 int hitx = x + dx, hity = y + dy;
11189 int hitting_element = Feld[x][y];
11190 int touched_element;
11192 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11195 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11196 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11198 if (IN_LEV_FIELD(hitx, hity))
11200 int opposite_direction = MV_DIR_OPPOSITE(direction);
11201 int hitting_side = direction;
11202 int touched_side = opposite_direction;
11203 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11204 MovDir[hitx][hity] != direction ||
11205 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11211 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11212 CE_HITTING_X, touched_side);
11214 CheckElementChangeBySide(hitx, hity, touched_element,
11215 hitting_element, CE_HIT_BY_X, hitting_side);
11217 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11218 CE_HIT_BY_SOMETHING, opposite_direction);
11222 /* "hitting something" is also true when hitting the playfield border */
11223 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11224 CE_HITTING_SOMETHING, direction);
11228 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11230 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11231 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11232 int hitx = x + dx, hity = y + dy;
11233 int hitting_element = Feld[x][y];
11234 int touched_element;
11236 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11237 !IS_FREE(hitx, hity) &&
11238 (!IS_MOVING(hitx, hity) ||
11239 MovDir[hitx][hity] != direction ||
11240 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11243 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11247 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11251 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11252 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11254 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11255 EP_CAN_SMASH_EVERYTHING, direction);
11257 if (IN_LEV_FIELD(hitx, hity))
11259 int opposite_direction = MV_DIR_OPPOSITE(direction);
11260 int hitting_side = direction;
11261 int touched_side = opposite_direction;
11263 int touched_element = MovingOrBlocked2Element(hitx, hity);
11266 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11267 MovDir[hitx][hity] != direction ||
11268 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11277 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11278 CE_SMASHED_BY_SOMETHING, opposite_direction);
11280 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11281 CE_OTHER_IS_SMASHING, touched_side);
11283 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11284 CE_OTHER_GETS_SMASHED, hitting_side);
11290 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11292 int i, kill_x = -1, kill_y = -1;
11294 int bad_element = -1;
11295 static int test_xy[4][2] =
11302 static int test_dir[4] =
11310 for (i = 0; i < NUM_DIRECTIONS; i++)
11312 int test_x, test_y, test_move_dir, test_element;
11314 test_x = good_x + test_xy[i][0];
11315 test_y = good_y + test_xy[i][1];
11317 if (!IN_LEV_FIELD(test_x, test_y))
11321 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11323 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11325 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11326 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11328 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11329 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11333 bad_element = test_element;
11339 if (kill_x != -1 || kill_y != -1)
11341 if (IS_PLAYER(good_x, good_y))
11343 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11345 if (player->shield_deadly_time_left > 0 &&
11346 !IS_INDESTRUCTIBLE(bad_element))
11347 Bang(kill_x, kill_y);
11348 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11349 KillPlayer(player);
11352 Bang(good_x, good_y);
11356 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11358 int i, kill_x = -1, kill_y = -1;
11359 int bad_element = Feld[bad_x][bad_y];
11360 static int test_xy[4][2] =
11367 static int touch_dir[4] =
11369 MV_LEFT | MV_RIGHT,
11374 static int test_dir[4] =
11382 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11385 for (i = 0; i < NUM_DIRECTIONS; i++)
11387 int test_x, test_y, test_move_dir, test_element;
11389 test_x = bad_x + test_xy[i][0];
11390 test_y = bad_y + test_xy[i][1];
11391 if (!IN_LEV_FIELD(test_x, test_y))
11395 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11397 test_element = Feld[test_x][test_y];
11399 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11400 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11402 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11403 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11405 /* good thing is player or penguin that does not move away */
11406 if (IS_PLAYER(test_x, test_y))
11408 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11410 if (bad_element == EL_ROBOT && player->is_moving)
11411 continue; /* robot does not kill player if he is moving */
11413 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11415 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11416 continue; /* center and border element do not touch */
11423 else if (test_element == EL_PENGUIN)
11432 if (kill_x != -1 || kill_y != -1)
11434 if (IS_PLAYER(kill_x, kill_y))
11436 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11438 if (player->shield_deadly_time_left > 0 &&
11439 !IS_INDESTRUCTIBLE(bad_element))
11440 Bang(bad_x, bad_y);
11441 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11442 KillPlayer(player);
11445 Bang(kill_x, kill_y);
11449 void TestIfPlayerTouchesBadThing(int x, int y)
11451 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11454 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11456 TestIfGoodThingHitsBadThing(x, y, move_dir);
11459 void TestIfBadThingTouchesPlayer(int x, int y)
11461 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11464 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11466 TestIfBadThingHitsGoodThing(x, y, move_dir);
11469 void TestIfFriendTouchesBadThing(int x, int y)
11471 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11474 void TestIfBadThingTouchesFriend(int x, int y)
11476 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11479 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11481 int i, kill_x = bad_x, kill_y = bad_y;
11482 static int xy[4][2] =
11490 for (i = 0; i < NUM_DIRECTIONS; i++)
11494 x = bad_x + xy[i][0];
11495 y = bad_y + xy[i][1];
11496 if (!IN_LEV_FIELD(x, y))
11499 element = Feld[x][y];
11500 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11501 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11509 if (kill_x != bad_x || kill_y != bad_y)
11510 Bang(bad_x, bad_y);
11513 void KillPlayer(struct PlayerInfo *player)
11515 int jx = player->jx, jy = player->jy;
11517 if (!player->active)
11520 /* remove accessible field at the player's position */
11521 Feld[jx][jy] = EL_EMPTY;
11523 /* deactivate shield (else Bang()/Explode() would not work right) */
11524 player->shield_normal_time_left = 0;
11525 player->shield_deadly_time_left = 0;
11528 BuryPlayer(player);
11531 static void KillPlayerUnlessEnemyProtected(int x, int y)
11533 if (!PLAYER_ENEMY_PROTECTED(x, y))
11534 KillPlayer(PLAYERINFO(x, y));
11537 static void KillPlayerUnlessExplosionProtected(int x, int y)
11539 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11540 KillPlayer(PLAYERINFO(x, y));
11543 void BuryPlayer(struct PlayerInfo *player)
11545 int jx = player->jx, jy = player->jy;
11547 if (!player->active)
11550 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11551 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11553 player->GameOver = TRUE;
11554 RemovePlayer(player);
11557 void RemovePlayer(struct PlayerInfo *player)
11559 int jx = player->jx, jy = player->jy;
11560 int i, found = FALSE;
11562 player->present = FALSE;
11563 player->active = FALSE;
11565 if (!ExplodeField[jx][jy])
11566 StorePlayer[jx][jy] = 0;
11568 if (player->is_moving)
11569 DrawLevelField(player->last_jx, player->last_jy);
11571 for (i = 0; i < MAX_PLAYERS; i++)
11572 if (stored_player[i].active)
11576 AllPlayersGone = TRUE;
11582 #if USE_NEW_SNAP_DELAY
11583 static void setFieldForSnapping(int x, int y, int element, int direction)
11585 struct ElementInfo *ei = &element_info[element];
11586 int direction_bit = MV_DIR_TO_BIT(direction);
11587 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11588 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11589 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11591 Feld[x][y] = EL_ELEMENT_SNAPPING;
11592 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11594 ResetGfxAnimation(x, y);
11596 GfxElement[x][y] = element;
11597 GfxAction[x][y] = action;
11598 GfxDir[x][y] = direction;
11599 GfxFrame[x][y] = -1;
11604 =============================================================================
11605 checkDiagonalPushing()
11606 -----------------------------------------------------------------------------
11607 check if diagonal input device direction results in pushing of object
11608 (by checking if the alternative direction is walkable, diggable, ...)
11609 =============================================================================
11612 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11613 int x, int y, int real_dx, int real_dy)
11615 int jx, jy, dx, dy, xx, yy;
11617 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11620 /* diagonal direction: check alternative direction */
11625 xx = jx + (dx == 0 ? real_dx : 0);
11626 yy = jy + (dy == 0 ? real_dy : 0);
11628 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11632 =============================================================================
11634 -----------------------------------------------------------------------------
11635 x, y: field next to player (non-diagonal) to try to dig to
11636 real_dx, real_dy: direction as read from input device (can be diagonal)
11637 =============================================================================
11640 int DigField(struct PlayerInfo *player,
11641 int oldx, int oldy, int x, int y,
11642 int real_dx, int real_dy, int mode)
11644 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11645 boolean player_was_pushing = player->is_pushing;
11646 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11647 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11648 int jx = oldx, jy = oldy;
11649 int dx = x - jx, dy = y - jy;
11650 int nextx = x + dx, nexty = y + dy;
11651 int move_direction = (dx == -1 ? MV_LEFT :
11652 dx == +1 ? MV_RIGHT :
11654 dy == +1 ? MV_DOWN : MV_NONE);
11655 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11656 int dig_side = MV_DIR_OPPOSITE(move_direction);
11657 int old_element = Feld[jx][jy];
11658 #if USE_FIXED_DONT_RUN_INTO
11659 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11665 if (is_player) /* function can also be called by EL_PENGUIN */
11667 if (player->MovPos == 0)
11669 player->is_digging = FALSE;
11670 player->is_collecting = FALSE;
11673 if (player->MovPos == 0) /* last pushing move finished */
11674 player->is_pushing = FALSE;
11676 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11678 player->is_switching = FALSE;
11679 player->push_delay = -1;
11681 return MP_NO_ACTION;
11685 #if !USE_FIXED_DONT_RUN_INTO
11686 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11687 return MP_NO_ACTION;
11690 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11691 old_element = Back[jx][jy];
11693 /* in case of element dropped at player position, check background */
11694 else if (Back[jx][jy] != EL_EMPTY &&
11695 game.engine_version >= VERSION_IDENT(2,2,0,0))
11696 old_element = Back[jx][jy];
11698 /* checking here causes player to move into acid even if the current field
11699 cannot be left to that direction */
11701 #if USE_FIXED_DONT_RUN_INTO
11702 if (player_can_move && DONT_RUN_INTO(element))
11704 if (element == EL_ACID && dx == 0 && dy == 1)
11707 Feld[jx][jy] = EL_PLAYER_1;
11708 InitMovingField(jx, jy, MV_DOWN);
11709 Store[jx][jy] = EL_ACID;
11710 ContinueMoving(jx, jy);
11711 BuryPlayer(player);
11714 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11716 return MP_DONT_RUN_INTO;
11721 #if 1 /* ------------------------------ NEW ------------------------------ */
11723 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11724 return MP_NO_ACTION; /* field has no opening in this direction */
11726 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11727 return MP_NO_ACTION; /* field has no opening in this direction */
11729 #if USE_FIXED_DONT_RUN_INTO
11730 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11733 Feld[jx][jy] = EL_PLAYER_1;
11734 InitMovingField(jx, jy, MV_DOWN);
11735 Store[jx][jy] = EL_ACID;
11736 ContinueMoving(jx, jy);
11737 BuryPlayer(player);
11739 return MP_DONT_RUN_INTO;
11743 #if USE_FIXED_DONT_RUN_INTO
11744 if (player_can_move && DONT_RUN_INTO(element))
11746 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11748 return MP_DONT_RUN_INTO;
11752 #else /* ------------------------------ OLD ------------------------------ */
11755 #if USE_FIXED_DONT_RUN_INTO
11756 if (player_can_move && DONT_RUN_INTO(element))
11758 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11760 return MP_DONT_RUN_INTO;
11765 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11766 return MP_NO_ACTION; /* field has no opening in this direction */
11768 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11769 return MP_NO_ACTION; /* field has no opening in this direction */
11771 /* checking here causes player to explode when moving into acid */
11773 #if USE_FIXED_DONT_RUN_INTO
11774 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11777 Feld[jx][jy] = EL_PLAYER_1;
11778 InitMovingField(jx, jy, MV_DOWN);
11779 Store[jx][jy] = EL_ACID;
11780 ContinueMoving(jx, jy);
11781 BuryPlayer(player);
11783 return MP_DONT_RUN_INTO;
11788 #endif /* ------------------------------ END ------------------------------ */
11791 #if USE_FIXED_DONT_RUN_INTO
11792 if (player_can_move && DONT_RUN_INTO(element))
11794 if (element == EL_ACID && dx == 0 && dy == 1)
11797 Feld[jx][jy] = EL_PLAYER_1;
11798 InitMovingField(jx, jy, MV_DOWN);
11799 Store[jx][jy] = EL_ACID;
11800 ContinueMoving(jx, jy);
11801 BuryPlayer(player);
11804 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11806 return MP_DONT_RUN_INTO;
11811 #if USE_FIXED_DONT_RUN_INTO
11812 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11813 return MP_NO_ACTION;
11816 #if !USE_FIXED_DONT_RUN_INTO
11817 element = Feld[x][y];
11820 collect_count = element_info[element].collect_count_initial;
11822 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11823 return MP_NO_ACTION;
11825 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11826 player_can_move = player_can_move_or_snap;
11828 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11829 game.engine_version >= VERSION_IDENT(2,2,0,0))
11831 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11832 player->index_bit, dig_side);
11833 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11834 player->index_bit, dig_side);
11836 if (Feld[x][y] != element) /* field changed by snapping */
11839 return MP_NO_ACTION;
11842 #if USE_PLAYER_GRAVITY
11843 if (player->gravity && is_player && !player->is_auto_moving &&
11844 canFallDown(player) && move_direction != MV_DOWN &&
11845 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11846 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11848 if (game.gravity && is_player && !player->is_auto_moving &&
11849 canFallDown(player) && move_direction != MV_DOWN &&
11850 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11851 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11854 if (player_can_move &&
11855 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11857 int sound_element = SND_ELEMENT(element);
11858 int sound_action = ACTION_WALKING;
11860 if (IS_RND_GATE(element))
11862 if (!player->key[RND_GATE_NR(element)])
11863 return MP_NO_ACTION;
11865 else if (IS_RND_GATE_GRAY(element))
11867 if (!player->key[RND_GATE_GRAY_NR(element)])
11868 return MP_NO_ACTION;
11870 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11872 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11873 return MP_NO_ACTION;
11875 else if (element == EL_EXIT_OPEN ||
11876 element == EL_SP_EXIT_OPEN ||
11877 element == EL_SP_EXIT_OPENING)
11879 sound_action = ACTION_PASSING; /* player is passing exit */
11881 else if (element == EL_EMPTY)
11883 sound_action = ACTION_MOVING; /* nothing to walk on */
11886 /* play sound from background or player, whatever is available */
11887 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11888 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11890 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11892 else if (player_can_move &&
11893 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11895 if (!ACCESS_FROM(element, opposite_direction))
11896 return MP_NO_ACTION; /* field not accessible from this direction */
11898 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11899 return MP_NO_ACTION;
11901 if (IS_EM_GATE(element))
11903 if (!player->key[EM_GATE_NR(element)])
11904 return MP_NO_ACTION;
11906 else if (IS_EM_GATE_GRAY(element))
11908 if (!player->key[EM_GATE_GRAY_NR(element)])
11909 return MP_NO_ACTION;
11911 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11913 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11914 return MP_NO_ACTION;
11916 else if (IS_EMC_GATE(element))
11918 if (!player->key[EMC_GATE_NR(element)])
11919 return MP_NO_ACTION;
11921 else if (IS_EMC_GATE_GRAY(element))
11923 if (!player->key[EMC_GATE_GRAY_NR(element)])
11924 return MP_NO_ACTION;
11926 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11928 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11929 return MP_NO_ACTION;
11931 else if (IS_SP_PORT(element))
11933 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11934 element == EL_SP_GRAVITY_PORT_RIGHT ||
11935 element == EL_SP_GRAVITY_PORT_UP ||
11936 element == EL_SP_GRAVITY_PORT_DOWN)
11937 #if USE_PLAYER_GRAVITY
11938 player->gravity = !player->gravity;
11940 game.gravity = !game.gravity;
11942 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11943 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11944 element == EL_SP_GRAVITY_ON_PORT_UP ||
11945 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11946 #if USE_PLAYER_GRAVITY
11947 player->gravity = TRUE;
11949 game.gravity = TRUE;
11951 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11952 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11953 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11954 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11955 #if USE_PLAYER_GRAVITY
11956 player->gravity = FALSE;
11958 game.gravity = FALSE;
11962 /* automatically move to the next field with double speed */
11963 player->programmed_action = move_direction;
11965 if (player->move_delay_reset_counter == 0)
11967 player->move_delay_reset_counter = 2; /* two double speed steps */
11969 DOUBLE_PLAYER_SPEED(player);
11972 PlayLevelSoundAction(x, y, ACTION_PASSING);
11974 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11978 if (mode != DF_SNAP)
11980 GfxElement[x][y] = GFX_ELEMENT(element);
11981 player->is_digging = TRUE;
11984 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11986 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11987 player->index_bit, dig_side);
11989 if (mode == DF_SNAP)
11991 #if USE_NEW_SNAP_DELAY
11992 if (level.block_snap_field)
11993 setFieldForSnapping(x, y, element, move_direction);
11995 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11997 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12000 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12001 player->index_bit, dig_side);
12004 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12008 if (is_player && mode != DF_SNAP)
12010 GfxElement[x][y] = element;
12011 player->is_collecting = TRUE;
12014 if (element == EL_SPEED_PILL)
12016 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12018 else if (element == EL_EXTRA_TIME && level.time > 0)
12020 TimeLeft += level.extra_time;
12021 DrawGameValue_Time(TimeLeft);
12023 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12025 player->shield_normal_time_left += level.shield_normal_time;
12026 if (element == EL_SHIELD_DEADLY)
12027 player->shield_deadly_time_left += level.shield_deadly_time;
12029 else if (element == EL_DYNAMITE ||
12030 element == EL_EM_DYNAMITE ||
12031 element == EL_SP_DISK_RED)
12033 if (player->inventory_size < MAX_INVENTORY_SIZE)
12034 player->inventory_element[player->inventory_size++] = element;
12037 DrawGameDoorValues();
12039 DrawGameValue_Dynamite(local_player->inventory_size);
12042 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12044 player->dynabomb_count++;
12045 player->dynabombs_left++;
12047 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12049 player->dynabomb_size++;
12051 else if (element == EL_DYNABOMB_INCREASE_POWER)
12053 player->dynabomb_xl = TRUE;
12055 else if (IS_KEY(element))
12057 player->key[KEY_NR(element)] = TRUE;
12060 DrawGameDoorValues();
12062 DrawGameValue_Keys(player->key);
12065 redraw_mask |= REDRAW_DOOR_1;
12067 else if (IS_ENVELOPE(element))
12069 player->show_envelope = element;
12071 else if (element == EL_EMC_LENSES)
12073 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12075 RedrawAllInvisibleElementsForLenses();
12077 else if (element == EL_EMC_MAGNIFIER)
12079 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12081 RedrawAllInvisibleElementsForMagnifier();
12083 else if (IS_DROPPABLE(element) ||
12084 IS_THROWABLE(element)) /* can be collected and dropped */
12088 if (collect_count == 0)
12089 player->inventory_infinite_element = element;
12091 for (i = 0; i < collect_count; i++)
12092 if (player->inventory_size < MAX_INVENTORY_SIZE)
12093 player->inventory_element[player->inventory_size++] = element;
12096 DrawGameDoorValues();
12098 DrawGameValue_Dynamite(local_player->inventory_size);
12101 else if (collect_count > 0)
12103 local_player->gems_still_needed -= collect_count;
12104 if (local_player->gems_still_needed < 0)
12105 local_player->gems_still_needed = 0;
12107 DrawGameValue_Emeralds(local_player->gems_still_needed);
12110 RaiseScoreElement(element);
12111 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12114 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12115 player->index_bit, dig_side);
12117 if (mode == DF_SNAP)
12119 #if USE_NEW_SNAP_DELAY
12120 if (level.block_snap_field)
12121 setFieldForSnapping(x, y, element, move_direction);
12123 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12125 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12128 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12129 player->index_bit, dig_side);
12132 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12134 if (mode == DF_SNAP && element != EL_BD_ROCK)
12135 return MP_NO_ACTION;
12137 if (CAN_FALL(element) && dy)
12138 return MP_NO_ACTION;
12140 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12141 !(element == EL_SPRING && level.use_spring_bug))
12142 return MP_NO_ACTION;
12144 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12145 ((move_direction & MV_VERTICAL &&
12146 ((element_info[element].move_pattern & MV_LEFT &&
12147 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12148 (element_info[element].move_pattern & MV_RIGHT &&
12149 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12150 (move_direction & MV_HORIZONTAL &&
12151 ((element_info[element].move_pattern & MV_UP &&
12152 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12153 (element_info[element].move_pattern & MV_DOWN &&
12154 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12155 return MP_NO_ACTION;
12157 /* do not push elements already moving away faster than player */
12158 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12159 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12160 return MP_NO_ACTION;
12162 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12164 if (player->push_delay_value == -1 || !player_was_pushing)
12165 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12167 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12169 if (player->push_delay_value == -1)
12170 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12172 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12174 if (!player->is_pushing)
12175 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12178 player->is_pushing = TRUE;
12180 if (!(IN_LEV_FIELD(nextx, nexty) &&
12181 (IS_FREE(nextx, nexty) ||
12182 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12183 IS_SB_ELEMENT(element)))))
12184 return MP_NO_ACTION;
12186 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12187 return MP_NO_ACTION;
12189 if (player->push_delay == -1) /* new pushing; restart delay */
12190 player->push_delay = 0;
12192 if (player->push_delay < player->push_delay_value &&
12193 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12194 element != EL_SPRING && element != EL_BALLOON)
12196 /* make sure that there is no move delay before next try to push */
12197 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12198 player->move_delay = 0;
12200 return MP_NO_ACTION;
12203 if (IS_SB_ELEMENT(element))
12205 if (element == EL_SOKOBAN_FIELD_FULL)
12207 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12208 local_player->sokobanfields_still_needed++;
12211 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12213 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12214 local_player->sokobanfields_still_needed--;
12217 Feld[x][y] = EL_SOKOBAN_OBJECT;
12219 if (Back[x][y] == Back[nextx][nexty])
12220 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12221 else if (Back[x][y] != 0)
12222 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12225 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12228 if (local_player->sokobanfields_still_needed == 0 &&
12229 game.emulation == EMU_SOKOBAN)
12231 player->LevelSolved = player->GameOver = TRUE;
12232 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12236 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12238 InitMovingField(x, y, move_direction);
12239 GfxAction[x][y] = ACTION_PUSHING;
12241 if (mode == DF_SNAP)
12242 ContinueMoving(x, y);
12244 MovPos[x][y] = (dx != 0 ? dx : dy);
12246 Pushed[x][y] = TRUE;
12247 Pushed[nextx][nexty] = TRUE;
12249 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12250 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12252 player->push_delay_value = -1; /* get new value later */
12254 /* check for element change _after_ element has been pushed */
12255 if (game.use_change_when_pushing_bug)
12257 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12258 player->index_bit, dig_side);
12259 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12260 player->index_bit, dig_side);
12263 else if (IS_SWITCHABLE(element))
12265 if (PLAYER_SWITCHING(player, x, y))
12267 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12268 player->index_bit, dig_side);
12273 player->is_switching = TRUE;
12274 player->switch_x = x;
12275 player->switch_y = y;
12277 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12279 if (element == EL_ROBOT_WHEEL)
12281 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12285 DrawLevelField(x, y);
12287 else if (element == EL_SP_TERMINAL)
12292 SCAN_PLAYFIELD(xx, yy)
12294 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12297 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12299 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12300 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12303 else if (IS_BELT_SWITCH(element))
12305 ToggleBeltSwitch(x, y);
12307 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12308 element == EL_SWITCHGATE_SWITCH_DOWN)
12310 ToggleSwitchgateSwitch(x, y);
12312 else if (element == EL_LIGHT_SWITCH ||
12313 element == EL_LIGHT_SWITCH_ACTIVE)
12315 ToggleLightSwitch(x, y);
12317 else if (element == EL_TIMEGATE_SWITCH)
12319 ActivateTimegateSwitch(x, y);
12321 else if (element == EL_BALLOON_SWITCH_LEFT ||
12322 element == EL_BALLOON_SWITCH_RIGHT ||
12323 element == EL_BALLOON_SWITCH_UP ||
12324 element == EL_BALLOON_SWITCH_DOWN ||
12325 element == EL_BALLOON_SWITCH_NONE ||
12326 element == EL_BALLOON_SWITCH_ANY)
12328 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12329 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12330 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12331 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12332 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12335 else if (element == EL_LAMP)
12337 Feld[x][y] = EL_LAMP_ACTIVE;
12338 local_player->lights_still_needed--;
12340 ResetGfxAnimation(x, y);
12341 DrawLevelField(x, y);
12343 else if (element == EL_TIME_ORB_FULL)
12345 Feld[x][y] = EL_TIME_ORB_EMPTY;
12347 if (level.time > 0 || level.use_time_orb_bug)
12349 TimeLeft += level.time_orb_time;
12350 DrawGameValue_Time(TimeLeft);
12353 ResetGfxAnimation(x, y);
12354 DrawLevelField(x, y);
12356 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12357 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12361 game.ball_state = !game.ball_state;
12364 SCAN_PLAYFIELD(xx, yy)
12366 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12369 int e = Feld[xx][yy];
12371 if (game.ball_state)
12373 if (e == EL_EMC_MAGIC_BALL)
12374 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12375 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12376 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12380 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12381 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12382 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12383 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12388 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12389 player->index_bit, dig_side);
12391 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12392 player->index_bit, dig_side);
12394 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12395 player->index_bit, dig_side);
12401 if (!PLAYER_SWITCHING(player, x, y))
12403 player->is_switching = TRUE;
12404 player->switch_x = x;
12405 player->switch_y = y;
12407 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12408 player->index_bit, dig_side);
12409 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12410 player->index_bit, dig_side);
12412 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12413 player->index_bit, dig_side);
12414 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12415 player->index_bit, dig_side);
12418 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12419 player->index_bit, dig_side);
12420 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12421 player->index_bit, dig_side);
12423 return MP_NO_ACTION;
12426 player->push_delay = -1;
12428 if (is_player) /* function can also be called by EL_PENGUIN */
12430 if (Feld[x][y] != element) /* really digged/collected something */
12431 player->is_collecting = !player->is_digging;
12437 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12439 int jx = player->jx, jy = player->jy;
12440 int x = jx + dx, y = jy + dy;
12441 int snap_direction = (dx == -1 ? MV_LEFT :
12442 dx == +1 ? MV_RIGHT :
12444 dy == +1 ? MV_DOWN : MV_NONE);
12445 boolean can_continue_snapping = (level.continuous_snapping &&
12446 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12448 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12451 if (!player->active || !IN_LEV_FIELD(x, y))
12459 if (player->MovPos == 0)
12460 player->is_pushing = FALSE;
12462 player->is_snapping = FALSE;
12464 if (player->MovPos == 0)
12466 player->is_moving = FALSE;
12467 player->is_digging = FALSE;
12468 player->is_collecting = FALSE;
12474 #if USE_NEW_CONTINUOUS_SNAPPING
12475 /* prevent snapping with already pressed snap key when not allowed */
12476 if (player->is_snapping && !can_continue_snapping)
12479 if (player->is_snapping)
12483 player->MovDir = snap_direction;
12485 if (player->MovPos == 0)
12487 player->is_moving = FALSE;
12488 player->is_digging = FALSE;
12489 player->is_collecting = FALSE;
12492 player->is_dropping = FALSE;
12493 player->is_dropping_pressed = FALSE;
12494 player->drop_pressed_delay = 0;
12496 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12499 player->is_snapping = TRUE;
12501 if (player->MovPos == 0)
12503 player->is_moving = FALSE;
12504 player->is_digging = FALSE;
12505 player->is_collecting = FALSE;
12508 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12509 DrawLevelField(player->last_jx, player->last_jy);
12511 DrawLevelField(x, y);
12516 boolean DropElement(struct PlayerInfo *player)
12518 int old_element, new_element;
12519 int dropx = player->jx, dropy = player->jy;
12520 int drop_direction = player->MovDir;
12521 int drop_side = drop_direction;
12522 int drop_element = (player->inventory_size > 0 ?
12523 player->inventory_element[player->inventory_size - 1] :
12524 player->inventory_infinite_element != EL_UNDEFINED ?
12525 player->inventory_infinite_element :
12526 player->dynabombs_left > 0 ?
12527 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12530 player->is_dropping_pressed = TRUE;
12532 /* do not drop an element on top of another element; when holding drop key
12533 pressed without moving, dropped element must move away before the next
12534 element can be dropped (this is especially important if the next element
12535 is dynamite, which can be placed on background for historical reasons) */
12536 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12539 if (IS_THROWABLE(drop_element))
12541 dropx += GET_DX_FROM_DIR(drop_direction);
12542 dropy += GET_DY_FROM_DIR(drop_direction);
12544 if (!IN_LEV_FIELD(dropx, dropy))
12548 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12549 new_element = drop_element; /* default: no change when dropping */
12551 /* check if player is active, not moving and ready to drop */
12552 if (!player->active || player->MovPos || player->drop_delay > 0)
12555 /* check if player has anything that can be dropped */
12556 if (new_element == EL_UNDEFINED)
12559 /* check if drop key was pressed long enough for EM style dynamite */
12560 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12563 /* check if anything can be dropped at the current position */
12564 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12567 /* collected custom elements can only be dropped on empty fields */
12568 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12571 if (old_element != EL_EMPTY)
12572 Back[dropx][dropy] = old_element; /* store old element on this field */
12574 ResetGfxAnimation(dropx, dropy);
12575 ResetRandomAnimationValue(dropx, dropy);
12577 if (player->inventory_size > 0 ||
12578 player->inventory_infinite_element != EL_UNDEFINED)
12580 if (player->inventory_size > 0)
12582 player->inventory_size--;
12585 DrawGameDoorValues();
12587 DrawGameValue_Dynamite(local_player->inventory_size);
12590 if (new_element == EL_DYNAMITE)
12591 new_element = EL_DYNAMITE_ACTIVE;
12592 else if (new_element == EL_EM_DYNAMITE)
12593 new_element = EL_EM_DYNAMITE_ACTIVE;
12594 else if (new_element == EL_SP_DISK_RED)
12595 new_element = EL_SP_DISK_RED_ACTIVE;
12598 Feld[dropx][dropy] = new_element;
12600 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12601 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12602 el2img(Feld[dropx][dropy]), 0);
12604 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12606 /* needed if previous element just changed to "empty" in the last frame */
12607 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12609 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12610 player->index_bit, drop_side);
12611 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12613 player->index_bit, drop_side);
12615 TestIfElementTouchesCustomElement(dropx, dropy);
12617 else /* player is dropping a dyna bomb */
12619 player->dynabombs_left--;
12621 Feld[dropx][dropy] = new_element;
12623 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12624 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12625 el2img(Feld[dropx][dropy]), 0);
12627 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12630 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12631 InitField_WithBug1(dropx, dropy, FALSE);
12633 new_element = Feld[dropx][dropy]; /* element might have changed */
12635 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12636 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12638 int move_direction, nextx, nexty;
12640 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12641 MovDir[dropx][dropy] = drop_direction;
12643 move_direction = MovDir[dropx][dropy];
12644 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12645 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12647 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12648 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12651 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12652 player->is_dropping = TRUE;
12654 player->drop_pressed_delay = 0;
12655 player->is_dropping_pressed = FALSE;
12657 player->drop_x = dropx;
12658 player->drop_y = dropy;
12663 /* ------------------------------------------------------------------------- */
12664 /* game sound playing functions */
12665 /* ------------------------------------------------------------------------- */
12667 static int *loop_sound_frame = NULL;
12668 static int *loop_sound_volume = NULL;
12670 void InitPlayLevelSound()
12672 int num_sounds = getSoundListSize();
12674 checked_free(loop_sound_frame);
12675 checked_free(loop_sound_volume);
12677 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12678 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12681 static void PlayLevelSound(int x, int y, int nr)
12683 int sx = SCREENX(x), sy = SCREENY(y);
12684 int volume, stereo_position;
12685 int max_distance = 8;
12686 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12688 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12689 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12692 if (!IN_LEV_FIELD(x, y) ||
12693 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12694 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12697 volume = SOUND_MAX_VOLUME;
12699 if (!IN_SCR_FIELD(sx, sy))
12701 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12702 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12704 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12707 stereo_position = (SOUND_MAX_LEFT +
12708 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12709 (SCR_FIELDX + 2 * max_distance));
12711 if (IS_LOOP_SOUND(nr))
12713 /* This assures that quieter loop sounds do not overwrite louder ones,
12714 while restarting sound volume comparison with each new game frame. */
12716 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12719 loop_sound_volume[nr] = volume;
12720 loop_sound_frame[nr] = FrameCounter;
12723 PlaySoundExt(nr, volume, stereo_position, type);
12726 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12728 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12729 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12730 y < LEVELY(BY1) ? LEVELY(BY1) :
12731 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12735 static void PlayLevelSoundAction(int x, int y, int action)
12737 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12740 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12742 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12744 if (sound_effect != SND_UNDEFINED)
12745 PlayLevelSound(x, y, sound_effect);
12748 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12751 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12753 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12754 PlayLevelSound(x, y, sound_effect);
12757 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12759 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12761 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12762 PlayLevelSound(x, y, sound_effect);
12765 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12767 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12769 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12770 StopSound(sound_effect);
12773 static void PlayLevelMusic()
12775 if (levelset.music[level_nr] != MUS_UNDEFINED)
12776 PlayMusic(levelset.music[level_nr]); /* from config file */
12778 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12781 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12783 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12788 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12792 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12796 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12800 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12804 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12808 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12812 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12815 case SAMPLE_android_clone:
12816 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12819 case SAMPLE_android_move:
12820 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12823 case SAMPLE_spring:
12824 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12828 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12832 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12835 case SAMPLE_eater_eat:
12836 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12840 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12843 case SAMPLE_collect:
12844 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12847 case SAMPLE_diamond:
12848 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12851 case SAMPLE_squash:
12852 /* !!! CHECK THIS !!! */
12854 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12856 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12860 case SAMPLE_wonderfall:
12861 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12865 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12869 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12873 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12877 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12881 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12885 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12888 case SAMPLE_wonder:
12889 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12893 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12896 case SAMPLE_exit_open:
12897 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12900 case SAMPLE_exit_leave:
12901 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12904 case SAMPLE_dynamite:
12905 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12909 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12913 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12917 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12921 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12925 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12929 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12933 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12938 void RaiseScore(int value)
12940 local_player->score += value;
12942 DrawGameValue_Score(local_player->score);
12945 void RaiseScoreElement(int element)
12950 case EL_BD_DIAMOND:
12951 case EL_EMERALD_YELLOW:
12952 case EL_EMERALD_RED:
12953 case EL_EMERALD_PURPLE:
12954 case EL_SP_INFOTRON:
12955 RaiseScore(level.score[SC_EMERALD]);
12958 RaiseScore(level.score[SC_DIAMOND]);
12961 RaiseScore(level.score[SC_CRYSTAL]);
12964 RaiseScore(level.score[SC_PEARL]);
12967 case EL_BD_BUTTERFLY:
12968 case EL_SP_ELECTRON:
12969 RaiseScore(level.score[SC_BUG]);
12972 case EL_BD_FIREFLY:
12973 case EL_SP_SNIKSNAK:
12974 RaiseScore(level.score[SC_SPACESHIP]);
12977 case EL_DARK_YAMYAM:
12978 RaiseScore(level.score[SC_YAMYAM]);
12981 RaiseScore(level.score[SC_ROBOT]);
12984 RaiseScore(level.score[SC_PACMAN]);
12987 RaiseScore(level.score[SC_NUT]);
12990 case EL_EM_DYNAMITE:
12991 case EL_SP_DISK_RED:
12992 case EL_DYNABOMB_INCREASE_NUMBER:
12993 case EL_DYNABOMB_INCREASE_SIZE:
12994 case EL_DYNABOMB_INCREASE_POWER:
12995 RaiseScore(level.score[SC_DYNAMITE]);
12997 case EL_SHIELD_NORMAL:
12998 case EL_SHIELD_DEADLY:
12999 RaiseScore(level.score[SC_SHIELD]);
13001 case EL_EXTRA_TIME:
13002 RaiseScore(level.extra_time_score);
13016 RaiseScore(level.score[SC_KEY]);
13019 RaiseScore(element_info[element].collect_score);
13024 void RequestQuitGame(boolean ask_if_really_quit)
13026 if (AllPlayersGone ||
13027 !ask_if_really_quit ||
13028 level_editor_test_game ||
13029 Request("Do you really want to quit the game ?",
13030 REQ_ASK | REQ_STAY_CLOSED))
13032 #if defined(NETWORK_AVALIABLE)
13033 if (options.network)
13034 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13038 game_status = GAME_MODE_MAIN;
13044 if (tape.playing && tape.deactivate_display)
13045 TapeDeactivateDisplayOff(TRUE);
13047 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13049 if (tape.playing && tape.deactivate_display)
13050 TapeDeactivateDisplayOn();
13055 /* ---------- new game button stuff ---------------------------------------- */
13057 /* graphic position values for game buttons */
13058 #define GAME_BUTTON_XSIZE 30
13059 #define GAME_BUTTON_YSIZE 30
13060 #define GAME_BUTTON_XPOS 5
13061 #define GAME_BUTTON_YPOS 215
13062 #define SOUND_BUTTON_XPOS 5
13063 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13065 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13066 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13067 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13068 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13069 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13070 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13077 } gamebutton_info[NUM_GAME_BUTTONS] =
13080 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13085 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13086 GAME_CTRL_ID_PAUSE,
13090 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13095 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13096 SOUND_CTRL_ID_MUSIC,
13097 "background music on/off"
13100 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13101 SOUND_CTRL_ID_LOOPS,
13102 "sound loops on/off"
13105 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13106 SOUND_CTRL_ID_SIMPLE,
13107 "normal sounds on/off"
13111 void CreateGameButtons()
13115 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13117 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13118 struct GadgetInfo *gi;
13121 unsigned long event_mask;
13122 int gd_xoffset, gd_yoffset;
13123 int gd_x1, gd_x2, gd_y1, gd_y2;
13126 gd_xoffset = gamebutton_info[i].x;
13127 gd_yoffset = gamebutton_info[i].y;
13128 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13129 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13131 if (id == GAME_CTRL_ID_STOP ||
13132 id == GAME_CTRL_ID_PAUSE ||
13133 id == GAME_CTRL_ID_PLAY)
13135 button_type = GD_TYPE_NORMAL_BUTTON;
13137 event_mask = GD_EVENT_RELEASED;
13138 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13139 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13143 button_type = GD_TYPE_CHECK_BUTTON;
13145 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13146 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13147 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13148 event_mask = GD_EVENT_PRESSED;
13149 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13150 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13153 gi = CreateGadget(GDI_CUSTOM_ID, id,
13154 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13155 GDI_X, DX + gd_xoffset,
13156 GDI_Y, DY + gd_yoffset,
13157 GDI_WIDTH, GAME_BUTTON_XSIZE,
13158 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13159 GDI_TYPE, button_type,
13160 GDI_STATE, GD_BUTTON_UNPRESSED,
13161 GDI_CHECKED, checked,
13162 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13163 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13164 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13165 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13166 GDI_EVENT_MASK, event_mask,
13167 GDI_CALLBACK_ACTION, HandleGameButtons,
13171 Error(ERR_EXIT, "cannot create gadget");
13173 game_gadget[id] = gi;
13177 void FreeGameButtons()
13181 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13182 FreeGadget(game_gadget[i]);
13185 static void MapGameButtons()
13189 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13190 MapGadget(game_gadget[i]);
13193 void UnmapGameButtons()
13197 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13198 UnmapGadget(game_gadget[i]);
13201 static void HandleGameButtons(struct GadgetInfo *gi)
13203 int id = gi->custom_id;
13205 if (game_status != GAME_MODE_PLAYING)
13210 case GAME_CTRL_ID_STOP:
13214 RequestQuitGame(TRUE);
13217 case GAME_CTRL_ID_PAUSE:
13218 if (options.network)
13220 #if defined(NETWORK_AVALIABLE)
13222 SendToServer_ContinuePlaying();
13224 SendToServer_PausePlaying();
13228 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13231 case GAME_CTRL_ID_PLAY:
13234 #if defined(NETWORK_AVALIABLE)
13235 if (options.network)
13236 SendToServer_ContinuePlaying();
13240 tape.pausing = FALSE;
13241 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13246 case SOUND_CTRL_ID_MUSIC:
13247 if (setup.sound_music)
13249 setup.sound_music = FALSE;
13252 else if (audio.music_available)
13254 setup.sound = setup.sound_music = TRUE;
13256 SetAudioMode(setup.sound);
13262 case SOUND_CTRL_ID_LOOPS:
13263 if (setup.sound_loops)
13264 setup.sound_loops = FALSE;
13265 else if (audio.loops_available)
13267 setup.sound = setup.sound_loops = TRUE;
13268 SetAudioMode(setup.sound);
13272 case SOUND_CTRL_ID_SIMPLE:
13273 if (setup.sound_simple)
13274 setup.sound_simple = FALSE;
13275 else if (audio.sound_available)
13277 setup.sound = setup.sound_simple = TRUE;
13278 SetAudioMode(setup.sound);