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))
1770 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1771 element_info[i].push_delay_random = game.default_push_delay_random;
1775 /* set push delay value for certain elements from pre-defined list */
1776 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1778 int e = push_delay_list[i].element;
1780 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1781 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1784 /* set push delay value for Supaplex elements for newer engine versions */
1785 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1787 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1789 if (IS_SP_ELEMENT(i))
1791 /* set SP push delay to just enough to push under a falling zonk */
1792 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1794 element_info[i].push_delay_fixed = delay;
1795 element_info[i].push_delay_random = 0;
1800 /* ---------- initialize move stepsize ----------------------------------- */
1802 /* initialize move stepsize values to default */
1803 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1804 if (!IS_CUSTOM_ELEMENT(i))
1805 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1807 /* set move stepsize value for certain elements from pre-defined list */
1808 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1810 int e = move_stepsize_list[i].element;
1812 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1815 /* ---------- initialize collect score ----------------------------------- */
1817 /* initialize collect score values for custom elements from initial value */
1818 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1819 if (IS_CUSTOM_ELEMENT(i))
1820 element_info[i].collect_score = element_info[i].collect_score_initial;
1822 /* ---------- initialize collect count ----------------------------------- */
1824 /* initialize collect count values for non-custom elements */
1825 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1826 if (!IS_CUSTOM_ELEMENT(i))
1827 element_info[i].collect_count_initial = 0;
1829 /* add collect count values for all elements from pre-defined list */
1830 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1831 element_info[collect_count_list[i].element].collect_count_initial =
1832 collect_count_list[i].count;
1834 /* ---------- initialize access direction -------------------------------- */
1836 /* initialize access direction values to default (access from every side) */
1837 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1838 if (!IS_CUSTOM_ELEMENT(i))
1839 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1841 /* set access direction value for certain elements from pre-defined list */
1842 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1843 element_info[access_direction_list[i].element].access_direction =
1844 access_direction_list[i].direction;
1846 /* ---------- initialize explosion content ------------------------------- */
1847 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1849 if (IS_CUSTOM_ELEMENT(i))
1852 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1854 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1856 element_info[i].content.e[x][y] =
1857 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1858 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1859 i == EL_PLAYER_3 ? EL_EMERALD :
1860 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1861 i == EL_MOLE ? EL_EMERALD_RED :
1862 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1863 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1864 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1865 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1866 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1867 i == EL_WALL_EMERALD ? EL_EMERALD :
1868 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1869 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1870 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1871 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1872 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1873 i == EL_WALL_PEARL ? EL_PEARL :
1874 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1880 int get_num_special_action(int element, int action_first, int action_last)
1882 int num_special_action = 0;
1885 for (i = action_first; i <= action_last; i++)
1887 boolean found = FALSE;
1889 for (j = 0; j < NUM_DIRECTIONS; j++)
1890 if (el_act_dir2img(element, i, j) !=
1891 el_act_dir2img(element, ACTION_DEFAULT, j))
1895 num_special_action++;
1901 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1904 return num_special_action;
1908 =============================================================================
1910 -----------------------------------------------------------------------------
1911 initialize and start new game
1912 =============================================================================
1917 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1918 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1919 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1924 /* don't play tapes over network */
1925 network_playing = (options.network && !tape.playing);
1927 for (i = 0; i < MAX_PLAYERS; i++)
1929 struct PlayerInfo *player = &stored_player[i];
1931 player->index_nr = i;
1932 player->index_bit = (1 << i);
1933 player->element_nr = EL_PLAYER_1 + i;
1935 player->present = FALSE;
1936 player->active = FALSE;
1939 player->effective_action = 0;
1940 player->programmed_action = 0;
1943 player->gems_still_needed = level.gems_needed;
1944 player->sokobanfields_still_needed = 0;
1945 player->lights_still_needed = 0;
1946 player->friends_still_needed = 0;
1948 for (j = 0; j < MAX_NUM_KEYS; j++)
1949 player->key[j] = FALSE;
1951 player->dynabomb_count = 0;
1952 player->dynabomb_size = 1;
1953 player->dynabombs_left = 0;
1954 player->dynabomb_xl = FALSE;
1956 player->MovDir = MV_NONE;
1959 player->GfxDir = MV_NONE;
1960 player->GfxAction = ACTION_DEFAULT;
1962 player->StepFrame = 0;
1964 player->use_murphy = FALSE;
1965 player->artwork_element =
1966 (level.use_artwork_element[i] ? level.artwork_element[i] :
1967 player->element_nr);
1969 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1970 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1972 player->gravity = level.initial_player_gravity[i];
1974 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1976 player->actual_frame_counter = 0;
1978 player->step_counter = 0;
1980 player->last_move_dir = MV_NONE;
1982 player->is_waiting = FALSE;
1983 player->is_moving = FALSE;
1984 player->is_auto_moving = FALSE;
1985 player->is_digging = FALSE;
1986 player->is_snapping = FALSE;
1987 player->is_collecting = FALSE;
1988 player->is_pushing = FALSE;
1989 player->is_switching = FALSE;
1990 player->is_dropping = FALSE;
1991 player->is_dropping_pressed = FALSE;
1993 player->is_bored = FALSE;
1994 player->is_sleeping = FALSE;
1996 player->frame_counter_bored = -1;
1997 player->frame_counter_sleeping = -1;
1999 player->anim_delay_counter = 0;
2000 player->post_delay_counter = 0;
2002 player->dir_waiting = MV_NONE;
2003 player->action_waiting = ACTION_DEFAULT;
2004 player->last_action_waiting = ACTION_DEFAULT;
2005 player->special_action_bored = ACTION_DEFAULT;
2006 player->special_action_sleeping = ACTION_DEFAULT;
2009 /* cannot be set here -- could be modified in Init[Player]Field() below */
2011 /* set number of special actions for bored and sleeping animation */
2012 player->num_special_action_bored =
2013 get_num_special_action(player->artwork_element,
2014 ACTION_BORING_1, ACTION_BORING_LAST);
2015 player->num_special_action_sleeping =
2016 get_num_special_action(player->artwork_element,
2017 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2020 player->switch_x = -1;
2021 player->switch_y = -1;
2023 player->drop_x = -1;
2024 player->drop_y = -1;
2026 player->show_envelope = 0;
2029 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2031 player->move_delay = game.initial_move_delay;
2032 player->move_delay_value = game.initial_move_delay_value;
2034 player->move_delay_value_next = -1;
2036 player->move_delay_reset_counter = 0;
2038 player->cannot_move = FALSE;
2041 player->push_delay = -1; /* initialized when pushing starts */
2042 player->push_delay_value = game.initial_push_delay_value;
2044 player->drop_delay = 0;
2045 player->drop_pressed_delay = 0;
2047 player->last_jx = player->last_jy = 0;
2048 player->jx = player->jy = 0;
2050 player->shield_normal_time_left = 0;
2051 player->shield_deadly_time_left = 0;
2053 player->inventory_infinite_element = EL_UNDEFINED;
2054 player->inventory_size = 0;
2056 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2057 SnapField(player, 0, 0);
2059 player->LevelSolved = FALSE;
2060 player->GameOver = FALSE;
2063 network_player_action_received = FALSE;
2065 #if defined(NETWORK_AVALIABLE)
2066 /* initial null action */
2067 if (network_playing)
2068 SendToServer_MovePlayer(MV_NONE);
2077 TimeLeft = level.time;
2080 ScreenMovDir = MV_NONE;
2084 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2086 AllPlayersGone = FALSE;
2088 game.yamyam_content_nr = 0;
2089 game.magic_wall_active = FALSE;
2090 game.magic_wall_time_left = 0;
2091 game.light_time_left = 0;
2092 game.timegate_time_left = 0;
2093 game.switchgate_pos = 0;
2094 game.wind_direction = level.wind_direction_initial;
2096 #if !USE_PLAYER_GRAVITY
2098 game.gravity = FALSE;
2100 game.gravity = level.initial_gravity;
2102 game.explosions_delayed = TRUE;
2105 game.lenses_time_left = 0;
2106 game.magnify_time_left = 0;
2108 game.ball_state = level.ball_state_initial;
2109 game.ball_content_nr = 0;
2111 game.envelope_active = FALSE;
2113 /* set focus to local player for network games, else to all players */
2114 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2115 game.centered_player_nr_next = game.centered_player_nr;
2116 game.set_centered_player = FALSE;
2118 if (network_playing && tape.recording)
2120 /* store client dependent player focus when recording network games */
2121 tape.centered_player_nr_next = game.centered_player_nr_next;
2122 tape.set_centered_player = TRUE;
2126 printf("::: focus set to player %d [%d]\n",
2127 game.centered_player_nr, local_player->index_nr);
2130 for (i = 0; i < NUM_BELTS; i++)
2132 game.belt_dir[i] = MV_NONE;
2133 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2136 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2137 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2140 SCAN_PLAYFIELD(x, y)
2142 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2145 Feld[x][y] = level.field[x][y];
2146 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2147 ChangeDelay[x][y] = 0;
2148 ChangePage[x][y] = -1;
2149 #if USE_NEW_CUSTOM_VALUE
2150 CustomValue[x][y] = 0; /* initialized in InitField() */
2152 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2154 WasJustMoving[x][y] = 0;
2155 WasJustFalling[x][y] = 0;
2156 CheckCollision[x][y] = 0;
2158 Pushed[x][y] = FALSE;
2160 ChangeCount[x][y] = 0;
2161 ChangeEvent[x][y] = -1;
2163 ExplodePhase[x][y] = 0;
2164 ExplodeDelay[x][y] = 0;
2165 ExplodeField[x][y] = EX_TYPE_NONE;
2167 RunnerVisit[x][y] = 0;
2168 PlayerVisit[x][y] = 0;
2171 GfxRandom[x][y] = INIT_GFX_RANDOM();
2172 GfxElement[x][y] = EL_UNDEFINED;
2173 GfxAction[x][y] = ACTION_DEFAULT;
2174 GfxDir[x][y] = MV_NONE;
2178 SCAN_PLAYFIELD(x, y)
2180 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2183 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2185 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2187 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2190 InitField(x, y, TRUE);
2195 for (i = 0; i < MAX_PLAYERS; i++)
2197 struct PlayerInfo *player = &stored_player[i];
2200 /* set number of special actions for bored and sleeping animation */
2201 player->num_special_action_bored =
2202 get_num_special_action(player->artwork_element,
2203 ACTION_BORING_1, ACTION_BORING_LAST);
2204 player->num_special_action_sleeping =
2205 get_num_special_action(player->artwork_element,
2206 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2211 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2212 emulate_sb ? EMU_SOKOBAN :
2213 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2215 #if USE_NEW_ALL_SLIPPERY
2216 /* initialize type of slippery elements */
2217 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2219 if (!IS_CUSTOM_ELEMENT(i))
2221 /* default: elements slip down either to the left or right randomly */
2222 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2224 /* SP style elements prefer to slip down on the left side */
2225 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2226 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2228 /* BD style elements prefer to slip down on the left side */
2229 if (game.emulation == EMU_BOULDERDASH)
2230 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2235 /* initialize explosion and ignition delay */
2236 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2238 if (!IS_CUSTOM_ELEMENT(i))
2241 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2242 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2243 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2244 int last_phase = (num_phase + 1) * delay;
2245 int half_phase = (num_phase / 2) * delay;
2247 element_info[i].explosion_delay = last_phase - 1;
2248 element_info[i].ignition_delay = half_phase;
2250 if (i == EL_BLACK_ORB)
2251 element_info[i].ignition_delay = 1;
2255 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2256 element_info[i].explosion_delay = 1;
2258 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2259 element_info[i].ignition_delay = 1;
2263 /* correct non-moving belts to start moving left */
2264 for (i = 0; i < NUM_BELTS; i++)
2265 if (game.belt_dir[i] == MV_NONE)
2266 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2268 /* check if any connected player was not found in playfield */
2269 for (i = 0; i < MAX_PLAYERS; i++)
2271 struct PlayerInfo *player = &stored_player[i];
2273 if (player->connected && !player->present)
2275 for (j = 0; j < MAX_PLAYERS; j++)
2277 struct PlayerInfo *some_player = &stored_player[j];
2278 int jx = some_player->jx, jy = some_player->jy;
2280 /* assign first free player found that is present in the playfield */
2281 if (some_player->present && !some_player->connected)
2283 player->present = TRUE;
2284 player->active = TRUE;
2286 some_player->present = FALSE;
2287 some_player->active = FALSE;
2290 player->element_nr = some_player->element_nr;
2293 player->artwork_element = some_player->artwork_element;
2295 player->block_last_field = some_player->block_last_field;
2296 player->block_delay_adjustment = some_player->block_delay_adjustment;
2298 StorePlayer[jx][jy] = player->element_nr;
2299 player->jx = player->last_jx = jx;
2300 player->jy = player->last_jy = jy;
2310 /* when playing a tape, eliminate all players who do not participate */
2312 for (i = 0; i < MAX_PLAYERS; i++)
2314 if (stored_player[i].active && !tape.player_participates[i])
2316 struct PlayerInfo *player = &stored_player[i];
2317 int jx = player->jx, jy = player->jy;
2319 player->active = FALSE;
2320 StorePlayer[jx][jy] = 0;
2321 Feld[jx][jy] = EL_EMPTY;
2325 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2327 /* when in single player mode, eliminate all but the first active player */
2329 for (i = 0; i < MAX_PLAYERS; i++)
2331 if (stored_player[i].active)
2333 for (j = i + 1; j < MAX_PLAYERS; j++)
2335 if (stored_player[j].active)
2337 struct PlayerInfo *player = &stored_player[j];
2338 int jx = player->jx, jy = player->jy;
2340 player->active = FALSE;
2341 player->present = FALSE;
2343 StorePlayer[jx][jy] = 0;
2344 Feld[jx][jy] = EL_EMPTY;
2351 /* when recording the game, store which players take part in the game */
2354 for (i = 0; i < MAX_PLAYERS; i++)
2355 if (stored_player[i].active)
2356 tape.player_participates[i] = TRUE;
2361 for (i = 0; i < MAX_PLAYERS; i++)
2363 struct PlayerInfo *player = &stored_player[i];
2365 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2370 if (local_player == player)
2371 printf("Player %d is local player.\n", i+1);
2375 if (BorderElement == EL_EMPTY)
2378 SBX_Right = lev_fieldx - SCR_FIELDX;
2380 SBY_Lower = lev_fieldy - SCR_FIELDY;
2385 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2387 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2390 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2391 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2393 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2394 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2396 /* if local player not found, look for custom element that might create
2397 the player (make some assumptions about the right custom element) */
2398 if (!local_player->present)
2400 int start_x = 0, start_y = 0;
2401 int found_rating = 0;
2402 int found_element = EL_UNDEFINED;
2403 int player_nr = local_player->index_nr;
2406 SCAN_PLAYFIELD(x, y)
2408 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2411 int element = Feld[x][y];
2416 if (level.use_start_element[player_nr] &&
2417 level.start_element[player_nr] == element &&
2424 found_element = element;
2427 if (!IS_CUSTOM_ELEMENT(element))
2430 if (CAN_CHANGE(element))
2432 for (i = 0; i < element_info[element].num_change_pages; i++)
2434 /* check for player created from custom element as single target */
2435 content = element_info[element].change_page[i].target_element;
2436 is_player = ELEM_IS_PLAYER(content);
2438 if (is_player && (found_rating < 3 || element < found_element))
2444 found_element = element;
2449 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2451 /* check for player created from custom element as explosion content */
2452 content = element_info[element].content.e[xx][yy];
2453 is_player = ELEM_IS_PLAYER(content);
2455 if (is_player && (found_rating < 2 || element < found_element))
2457 start_x = x + xx - 1;
2458 start_y = y + yy - 1;
2461 found_element = element;
2464 if (!CAN_CHANGE(element))
2467 for (i = 0; i < element_info[element].num_change_pages; i++)
2469 /* check for player created from custom element as extended target */
2471 element_info[element].change_page[i].target_content.e[xx][yy];
2473 is_player = ELEM_IS_PLAYER(content);
2475 if (is_player && (found_rating < 1 || element < found_element))
2477 start_x = x + xx - 1;
2478 start_y = y + yy - 1;
2481 found_element = element;
2487 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2488 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2491 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2492 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2497 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2498 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2499 local_player->jx - MIDPOSX);
2501 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2502 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2503 local_player->jy - MIDPOSY);
2506 if (!game.restart_level)
2507 CloseDoor(DOOR_CLOSE_1);
2509 /* !!! FIX THIS (START) !!! */
2510 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2512 InitGameEngine_EM();
2519 /* after drawing the level, correct some elements */
2520 if (game.timegate_time_left == 0)
2521 CloseAllOpenTimegates();
2523 if (setup.soft_scrolling)
2524 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2526 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2529 /* !!! FIX THIS (END) !!! */
2531 if (!game.restart_level)
2533 /* copy default game door content to main double buffer */
2534 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2535 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2538 DrawGameDoorValues();
2540 if (!game.restart_level)
2544 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2545 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2546 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2550 /* copy actual game door content to door double buffer for OpenDoor() */
2551 BlitBitmap(drawto, bitmap_db_door,
2552 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2554 OpenDoor(DOOR_OPEN_ALL);
2556 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2558 if (setup.sound_music)
2561 KeyboardAutoRepeatOffUnlessAutoplay();
2565 for (i = 0; i < MAX_PLAYERS; i++)
2566 printf("Player %d %sactive.\n",
2567 i + 1, (stored_player[i].active ? "" : "not "));
2571 game.restart_level = FALSE;
2574 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2576 /* this is used for non-R'n'D game engines to update certain engine values */
2578 /* needed to determine if sounds are played within the visible screen area */
2579 scroll_x = actual_scroll_x;
2580 scroll_y = actual_scroll_y;
2583 void InitMovDir(int x, int y)
2585 int i, element = Feld[x][y];
2586 static int xy[4][2] =
2593 static int direction[3][4] =
2595 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2596 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2597 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2606 Feld[x][y] = EL_BUG;
2607 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2610 case EL_SPACESHIP_RIGHT:
2611 case EL_SPACESHIP_UP:
2612 case EL_SPACESHIP_LEFT:
2613 case EL_SPACESHIP_DOWN:
2614 Feld[x][y] = EL_SPACESHIP;
2615 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2618 case EL_BD_BUTTERFLY_RIGHT:
2619 case EL_BD_BUTTERFLY_UP:
2620 case EL_BD_BUTTERFLY_LEFT:
2621 case EL_BD_BUTTERFLY_DOWN:
2622 Feld[x][y] = EL_BD_BUTTERFLY;
2623 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2626 case EL_BD_FIREFLY_RIGHT:
2627 case EL_BD_FIREFLY_UP:
2628 case EL_BD_FIREFLY_LEFT:
2629 case EL_BD_FIREFLY_DOWN:
2630 Feld[x][y] = EL_BD_FIREFLY;
2631 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2634 case EL_PACMAN_RIGHT:
2636 case EL_PACMAN_LEFT:
2637 case EL_PACMAN_DOWN:
2638 Feld[x][y] = EL_PACMAN;
2639 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2642 case EL_YAMYAM_LEFT:
2643 case EL_YAMYAM_RIGHT:
2645 case EL_YAMYAM_DOWN:
2646 Feld[x][y] = EL_YAMYAM;
2647 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2650 case EL_SP_SNIKSNAK:
2651 MovDir[x][y] = MV_UP;
2654 case EL_SP_ELECTRON:
2655 MovDir[x][y] = MV_LEFT;
2662 Feld[x][y] = EL_MOLE;
2663 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2667 if (IS_CUSTOM_ELEMENT(element))
2669 struct ElementInfo *ei = &element_info[element];
2670 int move_direction_initial = ei->move_direction_initial;
2671 int move_pattern = ei->move_pattern;
2673 if (move_direction_initial == MV_START_PREVIOUS)
2675 if (MovDir[x][y] != MV_NONE)
2678 move_direction_initial = MV_START_AUTOMATIC;
2681 if (move_direction_initial == MV_START_RANDOM)
2682 MovDir[x][y] = 1 << RND(4);
2683 else if (move_direction_initial & MV_ANY_DIRECTION)
2684 MovDir[x][y] = move_direction_initial;
2685 else if (move_pattern == MV_ALL_DIRECTIONS ||
2686 move_pattern == MV_TURNING_LEFT ||
2687 move_pattern == MV_TURNING_RIGHT ||
2688 move_pattern == MV_TURNING_LEFT_RIGHT ||
2689 move_pattern == MV_TURNING_RIGHT_LEFT ||
2690 move_pattern == MV_TURNING_RANDOM)
2691 MovDir[x][y] = 1 << RND(4);
2692 else if (move_pattern == MV_HORIZONTAL)
2693 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2694 else if (move_pattern == MV_VERTICAL)
2695 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2696 else if (move_pattern & MV_ANY_DIRECTION)
2697 MovDir[x][y] = element_info[element].move_pattern;
2698 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2699 move_pattern == MV_ALONG_RIGHT_SIDE)
2701 /* use random direction as default start direction */
2702 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2703 MovDir[x][y] = 1 << RND(4);
2705 for (i = 0; i < NUM_DIRECTIONS; i++)
2707 int x1 = x + xy[i][0];
2708 int y1 = y + xy[i][1];
2710 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2712 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2713 MovDir[x][y] = direction[0][i];
2715 MovDir[x][y] = direction[1][i];
2724 MovDir[x][y] = 1 << RND(4);
2726 if (element != EL_BUG &&
2727 element != EL_SPACESHIP &&
2728 element != EL_BD_BUTTERFLY &&
2729 element != EL_BD_FIREFLY)
2732 for (i = 0; i < NUM_DIRECTIONS; i++)
2734 int x1 = x + xy[i][0];
2735 int y1 = y + xy[i][1];
2737 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2739 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2741 MovDir[x][y] = direction[0][i];
2744 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2745 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2747 MovDir[x][y] = direction[1][i];
2756 GfxDir[x][y] = MovDir[x][y];
2759 void InitAmoebaNr(int x, int y)
2762 int group_nr = AmoebeNachbarNr(x, y);
2766 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2768 if (AmoebaCnt[i] == 0)
2776 AmoebaNr[x][y] = group_nr;
2777 AmoebaCnt[group_nr]++;
2778 AmoebaCnt2[group_nr]++;
2784 boolean raise_level = FALSE;
2786 if (local_player->MovPos)
2789 if (tape.auto_play) /* tape might already be stopped here */
2790 tape.auto_play_level_solved = TRUE;
2792 local_player->LevelSolved = FALSE;
2794 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2798 if (!tape.playing && setup.sound_loops)
2799 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2800 SND_CTRL_PLAY_LOOP);
2802 while (TimeLeft > 0)
2804 if (!tape.playing && !setup.sound_loops)
2805 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2807 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2810 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2815 RaiseScore(level.score[SC_TIME_BONUS]);
2818 DrawGameValue_Time(TimeLeft);
2826 if (!tape.playing && setup.sound_loops)
2827 StopSound(SND_GAME_LEVELTIME_BONUS);
2829 else if (level.time == 0) /* level without time limit */
2831 if (!tape.playing && setup.sound_loops)
2832 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2833 SND_CTRL_PLAY_LOOP);
2835 while (TimePlayed < 999)
2837 if (!tape.playing && !setup.sound_loops)
2838 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2840 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2843 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2848 RaiseScore(level.score[SC_TIME_BONUS]);
2851 DrawGameValue_Time(TimePlayed);
2859 if (!tape.playing && setup.sound_loops)
2860 StopSound(SND_GAME_LEVELTIME_BONUS);
2863 /* close exit door after last player */
2864 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2865 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2866 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2868 int element = Feld[ExitX][ExitY];
2870 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2871 EL_SP_EXIT_CLOSING);
2873 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2876 /* player disappears */
2877 if (ExitX >= 0 && ExitY >= 0)
2878 DrawLevelField(ExitX, ExitY);
2884 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2886 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2892 CloseDoor(DOOR_CLOSE_1);
2897 SaveTape(tape.level_nr); /* Ask to save tape */
2900 if (level_nr == leveldir_current->handicap_level)
2902 leveldir_current->handicap_level++;
2903 SaveLevelSetup_SeriesInfo();
2906 if (level_editor_test_game)
2907 local_player->score = -1; /* no highscore when playing from editor */
2908 else if (level_nr < leveldir_current->last_level)
2909 raise_level = TRUE; /* advance to next level */
2911 if ((hi_pos = NewHiScore()) >= 0)
2913 game_status = GAME_MODE_SCORES;
2914 DrawHallOfFame(hi_pos);
2923 game_status = GAME_MODE_MAIN;
2940 LoadScore(level_nr);
2942 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2943 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2946 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2948 if (local_player->score > highscore[k].Score)
2950 /* player has made it to the hall of fame */
2952 if (k < MAX_SCORE_ENTRIES - 1)
2954 int m = MAX_SCORE_ENTRIES - 1;
2957 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2958 if (strEqual(setup.player_name, highscore[l].Name))
2960 if (m == k) /* player's new highscore overwrites his old one */
2964 for (l = m; l > k; l--)
2966 strcpy(highscore[l].Name, highscore[l - 1].Name);
2967 highscore[l].Score = highscore[l - 1].Score;
2974 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2975 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2976 highscore[k].Score = local_player->score;
2982 else if (!strncmp(setup.player_name, highscore[k].Name,
2983 MAX_PLAYER_NAME_LEN))
2984 break; /* player already there with a higher score */
2990 SaveScore(level_nr);
2995 inline static int getElementMoveStepsize(int x, int y)
2997 int element = Feld[x][y];
2998 int direction = MovDir[x][y];
2999 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3000 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3001 int horiz_move = (dx != 0);
3002 int sign = (horiz_move ? dx : dy);
3003 int step = sign * element_info[element].move_stepsize;
3005 /* special values for move stepsize for spring and things on conveyor belt */
3009 if (element == EL_SPRING)
3010 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3011 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3012 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3013 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3015 if (CAN_FALL(element) &&
3016 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3017 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3018 else if (element == EL_SPRING)
3019 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3026 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3028 if (player->GfxAction != action || player->GfxDir != dir)
3031 printf("Player frame reset! (%d => %d, %d => %d)\n",
3032 player->GfxAction, action, player->GfxDir, dir);
3035 player->GfxAction = action;
3036 player->GfxDir = dir;
3038 player->StepFrame = 0;
3042 #if USE_GFX_RESET_GFX_ANIMATION
3043 static void ResetGfxFrame(int x, int y, boolean redraw)
3045 int element = Feld[x][y];
3046 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3047 int last_gfx_frame = GfxFrame[x][y];
3049 if (graphic_info[graphic].anim_global_sync)
3050 GfxFrame[x][y] = FrameCounter;
3051 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3052 GfxFrame[x][y] = CustomValue[x][y];
3053 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3054 GfxFrame[x][y] = element_info[element].collect_score;
3055 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3056 GfxFrame[x][y] = ChangeDelay[x][y];
3058 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3059 DrawLevelGraphicAnimation(x, y, graphic);
3063 static void ResetGfxAnimation(int x, int y)
3066 int element, graphic;
3069 GfxAction[x][y] = ACTION_DEFAULT;
3070 GfxDir[x][y] = MovDir[x][y];
3074 element = Feld[x][y];
3075 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3077 if (graphic_info[graphic].anim_global_sync)
3078 GfxFrame[x][y] = FrameCounter;
3079 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3080 GfxFrame[x][y] = CustomValue[x][y];
3081 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3082 GfxFrame[x][y] = element_info[element].collect_score;
3083 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3084 GfxFrame[x][y] = ChangeDelay[x][y];
3087 #if USE_GFX_RESET_GFX_ANIMATION
3088 ResetGfxFrame(x, y, FALSE);
3092 static void ResetRandomAnimationValue(int x, int y)
3094 GfxRandom[x][y] = INIT_GFX_RANDOM();
3097 void InitMovingField(int x, int y, int direction)
3099 int element = Feld[x][y];
3103 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3104 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3108 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3109 ResetGfxAnimation(x, y);
3111 MovDir[x][y] = direction;
3112 GfxDir[x][y] = direction;
3113 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3114 ACTION_FALLING : ACTION_MOVING);
3117 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3119 if (graphic_info[graphic].anim_global_sync)
3120 GfxFrame[x][y] = FrameCounter;
3121 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3122 GfxFrame[x][y] = CustomValue[x][y];
3123 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3124 GfxFrame[x][y] = element_info[element].collect_score;
3125 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3126 GfxFrame[x][y] = ChangeDelay[x][y];
3129 /* this is needed for CEs with property "can move" / "not moving" */
3131 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3133 if (Feld[newx][newy] == EL_EMPTY)
3134 Feld[newx][newy] = EL_BLOCKED;
3136 MovDir[newx][newy] = MovDir[x][y];
3138 #if USE_NEW_CUSTOM_VALUE
3139 CustomValue[newx][newy] = CustomValue[x][y];
3142 GfxFrame[newx][newy] = GfxFrame[x][y];
3143 GfxRandom[newx][newy] = GfxRandom[x][y];
3144 GfxAction[newx][newy] = GfxAction[x][y];
3145 GfxDir[newx][newy] = GfxDir[x][y];
3149 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3151 int direction = MovDir[x][y];
3153 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3154 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3156 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3157 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3164 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3166 int oldx = x, oldy = y;
3167 int direction = MovDir[x][y];
3169 if (direction == MV_LEFT)
3171 else if (direction == MV_RIGHT)
3173 else if (direction == MV_UP)
3175 else if (direction == MV_DOWN)
3178 *comes_from_x = oldx;
3179 *comes_from_y = oldy;
3182 int MovingOrBlocked2Element(int x, int y)
3184 int element = Feld[x][y];
3186 if (element == EL_BLOCKED)
3190 Blocked2Moving(x, y, &oldx, &oldy);
3191 return Feld[oldx][oldy];
3197 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3199 /* like MovingOrBlocked2Element(), but if element is moving
3200 and (x,y) is the field the moving element is just leaving,
3201 return EL_BLOCKED instead of the element value */
3202 int element = Feld[x][y];
3204 if (IS_MOVING(x, y))
3206 if (element == EL_BLOCKED)
3210 Blocked2Moving(x, y, &oldx, &oldy);
3211 return Feld[oldx][oldy];
3220 static void RemoveField(int x, int y)
3222 Feld[x][y] = EL_EMPTY;
3228 #if USE_NEW_CUSTOM_VALUE
3229 CustomValue[x][y] = 0;
3233 ChangeDelay[x][y] = 0;
3234 ChangePage[x][y] = -1;
3235 Pushed[x][y] = FALSE;
3238 ExplodeField[x][y] = EX_TYPE_NONE;
3241 GfxElement[x][y] = EL_UNDEFINED;
3242 GfxAction[x][y] = ACTION_DEFAULT;
3243 GfxDir[x][y] = MV_NONE;
3246 void RemoveMovingField(int x, int y)
3248 int oldx = x, oldy = y, newx = x, newy = y;
3249 int element = Feld[x][y];
3250 int next_element = EL_UNDEFINED;
3252 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3255 if (IS_MOVING(x, y))
3257 Moving2Blocked(x, y, &newx, &newy);
3259 if (Feld[newx][newy] != EL_BLOCKED)
3261 /* element is moving, but target field is not free (blocked), but
3262 already occupied by something different (example: acid pool);
3263 in this case, only remove the moving field, but not the target */
3265 RemoveField(oldx, oldy);
3267 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3269 DrawLevelField(oldx, oldy);
3274 else if (element == EL_BLOCKED)
3276 Blocked2Moving(x, y, &oldx, &oldy);
3277 if (!IS_MOVING(oldx, oldy))
3281 if (element == EL_BLOCKED &&
3282 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3283 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3284 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3285 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3286 next_element = get_next_element(Feld[oldx][oldy]);
3288 RemoveField(oldx, oldy);
3289 RemoveField(newx, newy);
3291 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3293 if (next_element != EL_UNDEFINED)
3294 Feld[oldx][oldy] = next_element;
3296 DrawLevelField(oldx, oldy);
3297 DrawLevelField(newx, newy);
3300 void DrawDynamite(int x, int y)
3302 int sx = SCREENX(x), sy = SCREENY(y);
3303 int graphic = el2img(Feld[x][y]);
3306 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3309 if (IS_WALKABLE_INSIDE(Back[x][y]))
3313 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3314 else if (Store[x][y])
3315 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3317 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3319 if (Back[x][y] || Store[x][y])
3320 DrawGraphicThruMask(sx, sy, graphic, frame);
3322 DrawGraphic(sx, sy, graphic, frame);
3325 void CheckDynamite(int x, int y)
3327 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3331 if (MovDelay[x][y] != 0)
3334 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3340 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3347 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3349 boolean num_checked_players = 0;
3352 for (i = 0; i < MAX_PLAYERS; i++)
3354 if (stored_player[i].active)
3356 int sx = stored_player[i].jx;
3357 int sy = stored_player[i].jy;
3359 if (num_checked_players == 0)
3366 *sx1 = MIN(*sx1, sx);
3367 *sy1 = MIN(*sy1, sy);
3368 *sx2 = MAX(*sx2, sx);
3369 *sy2 = MAX(*sy2, sy);
3372 num_checked_players++;
3377 static boolean checkIfAllPlayersFitToScreen_RND()
3379 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3381 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3383 return (sx2 - sx1 < SCR_FIELDX &&
3384 sy2 - sy1 < SCR_FIELDY);
3387 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3389 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3391 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3393 *sx = (sx1 + sx2) / 2;
3394 *sy = (sy1 + sy2) / 2;
3398 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3399 int center_x, int center_y)
3401 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3403 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3405 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3406 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3409 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3413 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3415 return (max_dx <= SCR_FIELDX / 2 &&
3416 max_dy <= SCR_FIELDY / 2);
3424 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3425 boolean quick_relocation)
3427 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3428 boolean no_delay = (tape.warp_forward);
3429 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3430 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3432 if (quick_relocation)
3434 int offset = (setup.scroll_delay ? 3 : 0);
3441 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3443 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3444 x > SBX_Right + MIDPOSX ? SBX_Right :
3447 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3448 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3453 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3454 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3455 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3457 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3458 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3459 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3461 /* don't scroll over playfield boundaries */
3462 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3463 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3465 /* don't scroll over playfield boundaries */
3466 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3467 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3470 RedrawPlayfield(TRUE, 0,0,0,0);
3474 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3475 x > SBX_Right + MIDPOSX ? SBX_Right :
3478 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3479 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3482 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3484 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3487 int fx = FX, fy = FY;
3489 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3490 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3492 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3498 fx += dx * TILEX / 2;
3499 fy += dy * TILEY / 2;
3501 ScrollLevel(dx, dy);
3504 /* scroll in two steps of half tile size to make things smoother */
3505 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3507 Delay(wait_delay_value);
3509 /* scroll second step to align at full tile size */
3511 Delay(wait_delay_value);
3516 Delay(wait_delay_value);
3522 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3524 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3525 boolean no_delay = (tape.warp_forward);
3526 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3527 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3528 int jx = player->jx;
3529 int jy = player->jy;
3531 if (quick_relocation)
3533 int offset = (setup.scroll_delay ? 3 : 0);
3535 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3537 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3538 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3539 player->jx - MIDPOSX);
3541 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3542 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3543 player->jy - MIDPOSY);
3547 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3548 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3549 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3551 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3552 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3553 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3555 /* don't scroll over playfield boundaries */
3556 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3557 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3559 /* don't scroll over playfield boundaries */
3560 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3561 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3564 RedrawPlayfield(TRUE, 0,0,0,0);
3568 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3569 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3570 player->jx - MIDPOSX);
3572 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3573 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3574 player->jy - MIDPOSY);
3576 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3578 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3581 int fx = FX, fy = FY;
3583 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3584 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3586 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3592 fx += dx * TILEX / 2;
3593 fy += dy * TILEY / 2;
3595 ScrollLevel(dx, dy);
3598 /* scroll in two steps of half tile size to make things smoother */
3599 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3601 Delay(wait_delay_value);
3603 /* scroll second step to align at full tile size */
3605 Delay(wait_delay_value);
3610 Delay(wait_delay_value);
3616 void RelocatePlayer(int jx, int jy, int el_player_raw)
3618 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3619 int player_nr = GET_PLAYER_NR(el_player);
3620 struct PlayerInfo *player = &stored_player[player_nr];
3621 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3622 boolean no_delay = (tape.warp_forward);
3623 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3624 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3625 int old_jx = player->jx;
3626 int old_jy = player->jy;
3627 int old_element = Feld[old_jx][old_jy];
3628 int element = Feld[jx][jy];
3629 boolean player_relocated = (old_jx != jx || old_jy != jy);
3631 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3632 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3633 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3634 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3635 int leave_side_horiz = move_dir_horiz;
3636 int leave_side_vert = move_dir_vert;
3637 int enter_side = enter_side_horiz | enter_side_vert;
3638 int leave_side = leave_side_horiz | leave_side_vert;
3640 if (player->GameOver) /* do not reanimate dead player */
3643 if (!player_relocated) /* no need to relocate the player */
3646 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3648 RemoveField(jx, jy); /* temporarily remove newly placed player */
3649 DrawLevelField(jx, jy);
3652 if (player->present)
3654 while (player->MovPos)
3656 ScrollPlayer(player, SCROLL_GO_ON);
3657 ScrollScreen(NULL, SCROLL_GO_ON);
3659 AdvanceFrameAndPlayerCounters(player->index_nr);
3664 Delay(wait_delay_value);
3667 DrawPlayer(player); /* needed here only to cleanup last field */
3668 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3670 player->is_moving = FALSE;
3673 if (IS_CUSTOM_ELEMENT(old_element))
3674 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3676 player->index_bit, leave_side);
3678 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3680 player->index_bit, leave_side);
3682 Feld[jx][jy] = el_player;
3683 InitPlayerField(jx, jy, el_player, TRUE);
3685 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3687 Feld[jx][jy] = element;
3688 InitField(jx, jy, FALSE);
3692 /* only visually relocate centered player */
3694 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3695 level.instant_relocation);
3697 if (player->index_nr == game.centered_player_nr)
3698 DrawRelocatePlayer(player, level.instant_relocation);
3701 if (player == local_player) /* only visually relocate local player */
3702 DrawRelocatePlayer(player, level.instant_relocation);
3705 TestIfPlayerTouchesBadThing(jx, jy);
3706 TestIfPlayerTouchesCustomElement(jx, jy);
3708 if (IS_CUSTOM_ELEMENT(element))
3709 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3710 player->index_bit, enter_side);
3712 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3713 player->index_bit, enter_side);
3716 void Explode(int ex, int ey, int phase, int mode)
3722 /* !!! eliminate this variable !!! */
3723 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3725 if (game.explosions_delayed)
3727 ExplodeField[ex][ey] = mode;
3731 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3733 int center_element = Feld[ex][ey];
3734 int artwork_element, explosion_element; /* set these values later */
3737 /* --- This is only really needed (and now handled) in "Impact()". --- */
3738 /* do not explode moving elements that left the explode field in time */
3739 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3740 center_element == EL_EMPTY &&
3741 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3746 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3747 if (mode == EX_TYPE_NORMAL ||
3748 mode == EX_TYPE_CENTER ||
3749 mode == EX_TYPE_CROSS)
3750 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3753 /* remove things displayed in background while burning dynamite */
3754 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3757 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3759 /* put moving element to center field (and let it explode there) */
3760 center_element = MovingOrBlocked2Element(ex, ey);
3761 RemoveMovingField(ex, ey);
3762 Feld[ex][ey] = center_element;
3765 /* now "center_element" is finally determined -- set related values now */
3766 artwork_element = center_element; /* for custom player artwork */
3767 explosion_element = center_element; /* for custom player artwork */
3769 if (IS_PLAYER(ex, ey))
3771 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3773 artwork_element = stored_player[player_nr].artwork_element;
3775 if (level.use_explosion_element[player_nr])
3777 explosion_element = level.explosion_element[player_nr];
3778 artwork_element = explosion_element;
3783 if (mode == EX_TYPE_NORMAL ||
3784 mode == EX_TYPE_CENTER ||
3785 mode == EX_TYPE_CROSS)
3786 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3790 last_phase = element_info[explosion_element].explosion_delay + 1;
3792 last_phase = element_info[center_element].explosion_delay + 1;
3795 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3797 int xx = x - ex + 1;
3798 int yy = y - ey + 1;
3801 if (!IN_LEV_FIELD(x, y) ||
3802 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3803 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3806 element = Feld[x][y];
3808 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3810 element = MovingOrBlocked2Element(x, y);
3812 if (!IS_EXPLOSION_PROOF(element))
3813 RemoveMovingField(x, y);
3816 /* indestructible elements can only explode in center (but not flames) */
3817 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3818 mode == EX_TYPE_BORDER)) ||
3819 element == EL_FLAMES)
3822 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3823 behaviour, for example when touching a yamyam that explodes to rocks
3824 with active deadly shield, a rock is created under the player !!! */
3825 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3827 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3828 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3829 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3831 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3834 if (IS_ACTIVE_BOMB(element))
3836 /* re-activate things under the bomb like gate or penguin */
3837 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3844 /* save walkable background elements while explosion on same tile */
3845 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3846 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3847 Back[x][y] = element;
3849 /* ignite explodable elements reached by other explosion */
3850 if (element == EL_EXPLOSION)
3851 element = Store2[x][y];
3853 if (AmoebaNr[x][y] &&
3854 (element == EL_AMOEBA_FULL ||
3855 element == EL_BD_AMOEBA ||
3856 element == EL_AMOEBA_GROWING))
3858 AmoebaCnt[AmoebaNr[x][y]]--;
3859 AmoebaCnt2[AmoebaNr[x][y]]--;
3864 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3867 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3869 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3871 switch(StorePlayer[ex][ey])
3874 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3877 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3880 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3884 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3889 if (PLAYERINFO(ex, ey)->use_murphy)
3890 Store[x][y] = EL_EMPTY;
3893 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3894 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3895 else if (ELEM_IS_PLAYER(center_element))
3896 Store[x][y] = EL_EMPTY;
3897 else if (center_element == EL_YAMYAM)
3898 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3899 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3900 Store[x][y] = element_info[center_element].content.e[xx][yy];
3902 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3903 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3904 otherwise) -- FIX THIS !!! */
3905 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3906 Store[x][y] = element_info[element].content.e[1][1];
3908 else if (!CAN_EXPLODE(element))
3909 Store[x][y] = element_info[element].content.e[1][1];
3912 Store[x][y] = EL_EMPTY;
3914 else if (center_element == EL_MOLE)
3915 Store[x][y] = EL_EMERALD_RED;
3916 else if (center_element == EL_PENGUIN)
3917 Store[x][y] = EL_EMERALD_PURPLE;
3918 else if (center_element == EL_BUG)
3919 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3920 else if (center_element == EL_BD_BUTTERFLY)
3921 Store[x][y] = EL_BD_DIAMOND;
3922 else if (center_element == EL_SP_ELECTRON)
3923 Store[x][y] = EL_SP_INFOTRON;
3924 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3925 Store[x][y] = level.amoeba_content;
3926 else if (center_element == EL_YAMYAM)
3927 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3928 else if (IS_CUSTOM_ELEMENT(center_element) &&
3929 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3930 Store[x][y] = element_info[center_element].content.e[xx][yy];
3931 else if (element == EL_WALL_EMERALD)
3932 Store[x][y] = EL_EMERALD;
3933 else if (element == EL_WALL_DIAMOND)
3934 Store[x][y] = EL_DIAMOND;
3935 else if (element == EL_WALL_BD_DIAMOND)
3936 Store[x][y] = EL_BD_DIAMOND;
3937 else if (element == EL_WALL_EMERALD_YELLOW)
3938 Store[x][y] = EL_EMERALD_YELLOW;
3939 else if (element == EL_WALL_EMERALD_RED)
3940 Store[x][y] = EL_EMERALD_RED;
3941 else if (element == EL_WALL_EMERALD_PURPLE)
3942 Store[x][y] = EL_EMERALD_PURPLE;
3943 else if (element == EL_WALL_PEARL)
3944 Store[x][y] = EL_PEARL;
3945 else if (element == EL_WALL_CRYSTAL)
3946 Store[x][y] = EL_CRYSTAL;
3947 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3948 Store[x][y] = element_info[element].content.e[1][1];
3950 Store[x][y] = EL_EMPTY;
3953 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3954 center_element == EL_AMOEBA_TO_DIAMOND)
3955 Store2[x][y] = element;
3957 Feld[x][y] = EL_EXPLOSION;
3958 GfxElement[x][y] = artwork_element;
3961 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3962 x, y, artwork_element, EL_NAME(artwork_element));
3965 ExplodePhase[x][y] = 1;
3966 ExplodeDelay[x][y] = last_phase;
3971 if (center_element == EL_YAMYAM)
3972 game.yamyam_content_nr =
3973 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3985 GfxFrame[x][y] = 0; /* restart explosion animation */
3987 last_phase = ExplodeDelay[x][y];
3989 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3993 /* activate this even in non-DEBUG version until cause for crash in
3994 getGraphicAnimationFrame() (see below) is found and eliminated */
4000 /* this can happen if the player leaves an explosion just in time */
4001 if (GfxElement[x][y] == EL_UNDEFINED)
4002 GfxElement[x][y] = EL_EMPTY;
4004 if (GfxElement[x][y] == EL_UNDEFINED)
4007 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4008 printf("Explode(): This should never happen!\n");
4011 GfxElement[x][y] = EL_EMPTY;
4017 border_element = Store2[x][y];
4018 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4019 border_element = StorePlayer[x][y];
4021 if (phase == element_info[border_element].ignition_delay ||
4022 phase == last_phase)
4024 boolean border_explosion = FALSE;
4026 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4027 !PLAYER_EXPLOSION_PROTECTED(x, y))
4029 KillPlayerUnlessExplosionProtected(x, y);
4030 border_explosion = TRUE;
4032 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4034 Feld[x][y] = Store2[x][y];
4037 border_explosion = TRUE;
4039 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4041 AmoebeUmwandeln(x, y);
4043 border_explosion = TRUE;
4046 /* if an element just explodes due to another explosion (chain-reaction),
4047 do not immediately end the new explosion when it was the last frame of
4048 the explosion (as it would be done in the following "if"-statement!) */
4049 if (border_explosion && phase == last_phase)
4053 if (phase == last_phase)
4057 element = Feld[x][y] = Store[x][y];
4058 Store[x][y] = Store2[x][y] = 0;
4059 GfxElement[x][y] = EL_UNDEFINED;
4061 /* player can escape from explosions and might therefore be still alive */
4062 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4063 element <= EL_PLAYER_IS_EXPLODING_4)
4065 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4066 int explosion_element = EL_PLAYER_1 + player_nr;
4067 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4068 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4070 if (level.use_explosion_element[player_nr])
4071 explosion_element = level.explosion_element[player_nr];
4073 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4074 element_info[explosion_element].content.e[xx][yy]);
4077 /* restore probably existing indestructible background element */
4078 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4079 element = Feld[x][y] = Back[x][y];
4082 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4083 GfxDir[x][y] = MV_NONE;
4084 ChangeDelay[x][y] = 0;
4085 ChangePage[x][y] = -1;
4087 #if USE_NEW_CUSTOM_VALUE
4088 CustomValue[x][y] = 0;
4091 InitField_WithBug2(x, y, FALSE);
4093 DrawLevelField(x, y);
4095 TestIfElementTouchesCustomElement(x, y);
4097 if (GFX_CRUMBLED(element))
4098 DrawLevelFieldCrumbledSandNeighbours(x, y);
4100 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4101 StorePlayer[x][y] = 0;
4103 if (ELEM_IS_PLAYER(element))
4104 RelocatePlayer(x, y, element);
4106 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4108 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4109 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4112 DrawLevelFieldCrumbledSand(x, y);
4114 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4116 DrawLevelElement(x, y, Back[x][y]);
4117 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4119 else if (IS_WALKABLE_UNDER(Back[x][y]))
4121 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4122 DrawLevelElementThruMask(x, y, Back[x][y]);
4124 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4125 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4129 void DynaExplode(int ex, int ey)
4132 int dynabomb_element = Feld[ex][ey];
4133 int dynabomb_size = 1;
4134 boolean dynabomb_xl = FALSE;
4135 struct PlayerInfo *player;
4136 static int xy[4][2] =
4144 if (IS_ACTIVE_BOMB(dynabomb_element))
4146 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4147 dynabomb_size = player->dynabomb_size;
4148 dynabomb_xl = player->dynabomb_xl;
4149 player->dynabombs_left++;
4152 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4154 for (i = 0; i < NUM_DIRECTIONS; i++)
4156 for (j = 1; j <= dynabomb_size; j++)
4158 int x = ex + j * xy[i][0];
4159 int y = ey + j * xy[i][1];
4162 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4165 element = Feld[x][y];
4167 /* do not restart explosions of fields with active bombs */
4168 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4171 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4173 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4174 !IS_DIGGABLE(element) && !dynabomb_xl)
4180 void Bang(int x, int y)
4182 int element = MovingOrBlocked2Element(x, y);
4183 int explosion_type = EX_TYPE_NORMAL;
4185 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4187 struct PlayerInfo *player = PLAYERINFO(x, y);
4189 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4190 player->element_nr);
4192 if (level.use_explosion_element[player->index_nr])
4194 int explosion_element = level.explosion_element[player->index_nr];
4196 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4197 explosion_type = EX_TYPE_CROSS;
4198 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4199 explosion_type = EX_TYPE_CENTER;
4207 case EL_BD_BUTTERFLY:
4210 case EL_DARK_YAMYAM:
4214 RaiseScoreElement(element);
4217 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4218 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4219 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4220 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4221 case EL_DYNABOMB_INCREASE_NUMBER:
4222 case EL_DYNABOMB_INCREASE_SIZE:
4223 case EL_DYNABOMB_INCREASE_POWER:
4224 explosion_type = EX_TYPE_DYNA;
4229 case EL_LAMP_ACTIVE:
4230 case EL_AMOEBA_TO_DIAMOND:
4231 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4232 explosion_type = EX_TYPE_CENTER;
4236 if (element_info[element].explosion_type == EXPLODES_CROSS)
4237 explosion_type = EX_TYPE_CROSS;
4238 else if (element_info[element].explosion_type == EXPLODES_1X1)
4239 explosion_type = EX_TYPE_CENTER;
4243 if (explosion_type == EX_TYPE_DYNA)
4246 Explode(x, y, EX_PHASE_START, explosion_type);
4248 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4251 void SplashAcid(int x, int y)
4253 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4254 (!IN_LEV_FIELD(x - 1, y - 2) ||
4255 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4256 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4258 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4259 (!IN_LEV_FIELD(x + 1, y - 2) ||
4260 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4261 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4263 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4266 static void InitBeltMovement()
4268 static int belt_base_element[4] =
4270 EL_CONVEYOR_BELT_1_LEFT,
4271 EL_CONVEYOR_BELT_2_LEFT,
4272 EL_CONVEYOR_BELT_3_LEFT,
4273 EL_CONVEYOR_BELT_4_LEFT
4275 static int belt_base_active_element[4] =
4277 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4278 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4279 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4280 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4285 /* set frame order for belt animation graphic according to belt direction */
4286 for (i = 0; i < NUM_BELTS; i++)
4290 for (j = 0; j < NUM_BELT_PARTS; j++)
4292 int element = belt_base_active_element[belt_nr] + j;
4293 int graphic = el2img(element);
4295 if (game.belt_dir[i] == MV_LEFT)
4296 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4298 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4303 SCAN_PLAYFIELD(x, y)
4305 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4308 int element = Feld[x][y];
4310 for (i = 0; i < NUM_BELTS; i++)
4312 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4314 int e_belt_nr = getBeltNrFromBeltElement(element);
4317 if (e_belt_nr == belt_nr)
4319 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4321 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4328 static void ToggleBeltSwitch(int x, int y)
4330 static int belt_base_element[4] =
4332 EL_CONVEYOR_BELT_1_LEFT,
4333 EL_CONVEYOR_BELT_2_LEFT,
4334 EL_CONVEYOR_BELT_3_LEFT,
4335 EL_CONVEYOR_BELT_4_LEFT
4337 static int belt_base_active_element[4] =
4339 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4340 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4341 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4342 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4344 static int belt_base_switch_element[4] =
4346 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4347 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4348 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4349 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4351 static int belt_move_dir[4] =
4359 int element = Feld[x][y];
4360 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4361 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4362 int belt_dir = belt_move_dir[belt_dir_nr];
4365 if (!IS_BELT_SWITCH(element))
4368 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4369 game.belt_dir[belt_nr] = belt_dir;
4371 if (belt_dir_nr == 3)
4374 /* set frame order for belt animation graphic according to belt direction */
4375 for (i = 0; i < NUM_BELT_PARTS; i++)
4377 int element = belt_base_active_element[belt_nr] + i;
4378 int graphic = el2img(element);
4380 if (belt_dir == MV_LEFT)
4381 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4383 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4387 SCAN_PLAYFIELD(xx, yy)
4389 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4392 int element = Feld[xx][yy];
4394 if (IS_BELT_SWITCH(element))
4396 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4398 if (e_belt_nr == belt_nr)
4400 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4401 DrawLevelField(xx, yy);
4404 else if (IS_BELT(element) && belt_dir != MV_NONE)
4406 int e_belt_nr = getBeltNrFromBeltElement(element);
4408 if (e_belt_nr == belt_nr)
4410 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4412 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4413 DrawLevelField(xx, yy);
4416 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4418 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4420 if (e_belt_nr == belt_nr)
4422 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4424 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4425 DrawLevelField(xx, yy);
4431 static void ToggleSwitchgateSwitch(int x, int y)
4435 game.switchgate_pos = !game.switchgate_pos;
4438 SCAN_PLAYFIELD(xx, yy)
4440 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4443 int element = Feld[xx][yy];
4445 #if !USE_BOTH_SWITCHGATE_SWITCHES
4446 if (element == EL_SWITCHGATE_SWITCH_UP ||
4447 element == EL_SWITCHGATE_SWITCH_DOWN)
4449 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4450 DrawLevelField(xx, yy);
4453 if (element == EL_SWITCHGATE_SWITCH_UP)
4455 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4456 DrawLevelField(xx, yy);
4458 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4460 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4461 DrawLevelField(xx, yy);
4464 else if (element == EL_SWITCHGATE_OPEN ||
4465 element == EL_SWITCHGATE_OPENING)
4467 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4469 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4471 else if (element == EL_SWITCHGATE_CLOSED ||
4472 element == EL_SWITCHGATE_CLOSING)
4474 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4476 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4481 static int getInvisibleActiveFromInvisibleElement(int element)
4483 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4484 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4485 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4489 static int getInvisibleFromInvisibleActiveElement(int element)
4491 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4492 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4493 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4497 static void RedrawAllLightSwitchesAndInvisibleElements()
4502 SCAN_PLAYFIELD(x, y)
4504 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4507 int element = Feld[x][y];
4509 if (element == EL_LIGHT_SWITCH &&
4510 game.light_time_left > 0)
4512 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4513 DrawLevelField(x, y);
4515 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4516 game.light_time_left == 0)
4518 Feld[x][y] = EL_LIGHT_SWITCH;
4519 DrawLevelField(x, y);
4521 else if (element == EL_EMC_DRIPPER &&
4522 game.light_time_left > 0)
4524 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4525 DrawLevelField(x, y);
4527 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4528 game.light_time_left == 0)
4530 Feld[x][y] = EL_EMC_DRIPPER;
4531 DrawLevelField(x, y);
4533 else if (element == EL_INVISIBLE_STEELWALL ||
4534 element == EL_INVISIBLE_WALL ||
4535 element == EL_INVISIBLE_SAND)
4537 if (game.light_time_left > 0)
4538 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4540 DrawLevelField(x, y);
4542 /* uncrumble neighbour fields, if needed */
4543 if (element == EL_INVISIBLE_SAND)
4544 DrawLevelFieldCrumbledSandNeighbours(x, y);
4546 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4547 element == EL_INVISIBLE_WALL_ACTIVE ||
4548 element == EL_INVISIBLE_SAND_ACTIVE)
4550 if (game.light_time_left == 0)
4551 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4553 DrawLevelField(x, y);
4555 /* re-crumble neighbour fields, if needed */
4556 if (element == EL_INVISIBLE_SAND)
4557 DrawLevelFieldCrumbledSandNeighbours(x, y);
4562 static void RedrawAllInvisibleElementsForLenses()
4567 SCAN_PLAYFIELD(x, y)
4569 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4572 int element = Feld[x][y];
4574 if (element == EL_EMC_DRIPPER &&
4575 game.lenses_time_left > 0)
4577 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4578 DrawLevelField(x, y);
4580 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4581 game.lenses_time_left == 0)
4583 Feld[x][y] = EL_EMC_DRIPPER;
4584 DrawLevelField(x, y);
4586 else if (element == EL_INVISIBLE_STEELWALL ||
4587 element == EL_INVISIBLE_WALL ||
4588 element == EL_INVISIBLE_SAND)
4590 if (game.lenses_time_left > 0)
4591 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4593 DrawLevelField(x, y);
4595 /* uncrumble neighbour fields, if needed */
4596 if (element == EL_INVISIBLE_SAND)
4597 DrawLevelFieldCrumbledSandNeighbours(x, y);
4599 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4600 element == EL_INVISIBLE_WALL_ACTIVE ||
4601 element == EL_INVISIBLE_SAND_ACTIVE)
4603 if (game.lenses_time_left == 0)
4604 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4606 DrawLevelField(x, y);
4608 /* re-crumble neighbour fields, if needed */
4609 if (element == EL_INVISIBLE_SAND)
4610 DrawLevelFieldCrumbledSandNeighbours(x, y);
4615 static void RedrawAllInvisibleElementsForMagnifier()
4620 SCAN_PLAYFIELD(x, y)
4622 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4625 int element = Feld[x][y];
4627 if (element == EL_EMC_FAKE_GRASS &&
4628 game.magnify_time_left > 0)
4630 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4631 DrawLevelField(x, y);
4633 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4634 game.magnify_time_left == 0)
4636 Feld[x][y] = EL_EMC_FAKE_GRASS;
4637 DrawLevelField(x, y);
4639 else if (IS_GATE_GRAY(element) &&
4640 game.magnify_time_left > 0)
4642 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4643 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4644 IS_EM_GATE_GRAY(element) ?
4645 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4646 IS_EMC_GATE_GRAY(element) ?
4647 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4649 DrawLevelField(x, y);
4651 else if (IS_GATE_GRAY_ACTIVE(element) &&
4652 game.magnify_time_left == 0)
4654 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4655 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4656 IS_EM_GATE_GRAY_ACTIVE(element) ?
4657 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4658 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4659 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4661 DrawLevelField(x, y);
4666 static void ToggleLightSwitch(int x, int y)
4668 int element = Feld[x][y];
4670 game.light_time_left =
4671 (element == EL_LIGHT_SWITCH ?
4672 level.time_light * FRAMES_PER_SECOND : 0);
4674 RedrawAllLightSwitchesAndInvisibleElements();
4677 static void ActivateTimegateSwitch(int x, int y)
4681 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4684 SCAN_PLAYFIELD(xx, yy)
4686 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4689 int element = Feld[xx][yy];
4691 if (element == EL_TIMEGATE_CLOSED ||
4692 element == EL_TIMEGATE_CLOSING)
4694 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4695 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4699 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4701 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4702 DrawLevelField(xx, yy);
4708 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4711 void Impact(int x, int y)
4713 boolean last_line = (y == lev_fieldy - 1);
4714 boolean object_hit = FALSE;
4715 boolean impact = (last_line || object_hit);
4716 int element = Feld[x][y];
4717 int smashed = EL_STEELWALL;
4719 if (!last_line) /* check if element below was hit */
4721 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4724 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4725 MovDir[x][y + 1] != MV_DOWN ||
4726 MovPos[x][y + 1] <= TILEY / 2));
4728 /* do not smash moving elements that left the smashed field in time */
4729 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4730 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4733 #if USE_QUICKSAND_IMPACT_BUGFIX
4734 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4736 RemoveMovingField(x, y + 1);
4737 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4738 Feld[x][y + 2] = EL_ROCK;
4739 DrawLevelField(x, y + 2);
4746 smashed = MovingOrBlocked2Element(x, y + 1);
4748 impact = (last_line || object_hit);
4751 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4753 SplashAcid(x, y + 1);
4757 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4758 /* only reset graphic animation if graphic really changes after impact */
4760 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4762 ResetGfxAnimation(x, y);
4763 DrawLevelField(x, y);
4766 if (impact && CAN_EXPLODE_IMPACT(element))
4771 else if (impact && element == EL_PEARL)
4773 ResetGfxAnimation(x, y);
4775 Feld[x][y] = EL_PEARL_BREAKING;
4776 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4779 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4781 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4786 if (impact && element == EL_AMOEBA_DROP)
4788 if (object_hit && IS_PLAYER(x, y + 1))
4789 KillPlayerUnlessEnemyProtected(x, y + 1);
4790 else if (object_hit && smashed == EL_PENGUIN)
4794 Feld[x][y] = EL_AMOEBA_GROWING;
4795 Store[x][y] = EL_AMOEBA_WET;
4797 ResetRandomAnimationValue(x, y);
4802 if (object_hit) /* check which object was hit */
4804 if (CAN_PASS_MAGIC_WALL(element) &&
4805 (smashed == EL_MAGIC_WALL ||
4806 smashed == EL_BD_MAGIC_WALL))
4809 int activated_magic_wall =
4810 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4811 EL_BD_MAGIC_WALL_ACTIVE);
4813 /* activate magic wall / mill */
4815 SCAN_PLAYFIELD(xx, yy)
4817 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4819 if (Feld[xx][yy] == smashed)
4820 Feld[xx][yy] = activated_magic_wall;
4822 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4823 game.magic_wall_active = TRUE;
4825 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4826 SND_MAGIC_WALL_ACTIVATING :
4827 SND_BD_MAGIC_WALL_ACTIVATING));
4830 if (IS_PLAYER(x, y + 1))
4832 if (CAN_SMASH_PLAYER(element))
4834 KillPlayerUnlessEnemyProtected(x, y + 1);
4838 else if (smashed == EL_PENGUIN)
4840 if (CAN_SMASH_PLAYER(element))
4846 else if (element == EL_BD_DIAMOND)
4848 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4854 else if (((element == EL_SP_INFOTRON ||
4855 element == EL_SP_ZONK) &&
4856 (smashed == EL_SP_SNIKSNAK ||
4857 smashed == EL_SP_ELECTRON ||
4858 smashed == EL_SP_DISK_ORANGE)) ||
4859 (element == EL_SP_INFOTRON &&
4860 smashed == EL_SP_DISK_YELLOW))
4865 else if (CAN_SMASH_EVERYTHING(element))
4867 if (IS_CLASSIC_ENEMY(smashed) ||
4868 CAN_EXPLODE_SMASHED(smashed))
4873 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4875 if (smashed == EL_LAMP ||
4876 smashed == EL_LAMP_ACTIVE)
4881 else if (smashed == EL_NUT)
4883 Feld[x][y + 1] = EL_NUT_BREAKING;
4884 PlayLevelSound(x, y, SND_NUT_BREAKING);
4885 RaiseScoreElement(EL_NUT);
4888 else if (smashed == EL_PEARL)
4890 ResetGfxAnimation(x, y);
4892 Feld[x][y + 1] = EL_PEARL_BREAKING;
4893 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4896 else if (smashed == EL_DIAMOND)
4898 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4899 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4902 else if (IS_BELT_SWITCH(smashed))
4904 ToggleBeltSwitch(x, y + 1);
4906 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4907 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4909 ToggleSwitchgateSwitch(x, y + 1);
4911 else if (smashed == EL_LIGHT_SWITCH ||
4912 smashed == EL_LIGHT_SWITCH_ACTIVE)
4914 ToggleLightSwitch(x, y + 1);
4919 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4922 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4924 CheckElementChangeBySide(x, y + 1, smashed, element,
4925 CE_SWITCHED, CH_SIDE_TOP);
4926 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4932 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4937 /* play sound of magic wall / mill */
4939 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4940 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4942 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4943 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4944 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4945 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4950 /* play sound of object that hits the ground */
4951 if (last_line || object_hit)
4952 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4955 inline static void TurnRoundExt(int x, int y)
4967 { 0, 0 }, { 0, 0 }, { 0, 0 },
4972 int left, right, back;
4976 { MV_DOWN, MV_UP, MV_RIGHT },
4977 { MV_UP, MV_DOWN, MV_LEFT },
4979 { MV_LEFT, MV_RIGHT, MV_DOWN },
4983 { MV_RIGHT, MV_LEFT, MV_UP }
4986 int element = Feld[x][y];
4987 int move_pattern = element_info[element].move_pattern;
4989 int old_move_dir = MovDir[x][y];
4990 int left_dir = turn[old_move_dir].left;
4991 int right_dir = turn[old_move_dir].right;
4992 int back_dir = turn[old_move_dir].back;
4994 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4995 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4996 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4997 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4999 int left_x = x + left_dx, left_y = y + left_dy;
5000 int right_x = x + right_dx, right_y = y + right_dy;
5001 int move_x = x + move_dx, move_y = y + move_dy;
5005 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5007 TestIfBadThingTouchesOtherBadThing(x, y);
5009 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5010 MovDir[x][y] = right_dir;
5011 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5012 MovDir[x][y] = left_dir;
5014 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5016 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5019 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5021 TestIfBadThingTouchesOtherBadThing(x, y);
5023 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5024 MovDir[x][y] = left_dir;
5025 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5026 MovDir[x][y] = right_dir;
5028 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5030 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5033 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5035 TestIfBadThingTouchesOtherBadThing(x, y);
5037 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5038 MovDir[x][y] = left_dir;
5039 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5040 MovDir[x][y] = right_dir;
5042 if (MovDir[x][y] != old_move_dir)
5045 else if (element == EL_YAMYAM)
5047 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5048 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5050 if (can_turn_left && can_turn_right)
5051 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5052 else if (can_turn_left)
5053 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5054 else if (can_turn_right)
5055 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5057 MovDir[x][y] = back_dir;
5059 MovDelay[x][y] = 16 + 16 * RND(3);
5061 else if (element == EL_DARK_YAMYAM)
5063 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5065 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5068 if (can_turn_left && can_turn_right)
5069 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5070 else if (can_turn_left)
5071 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5072 else if (can_turn_right)
5073 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5075 MovDir[x][y] = back_dir;
5077 MovDelay[x][y] = 16 + 16 * RND(3);
5079 else if (element == EL_PACMAN)
5081 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5082 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5084 if (can_turn_left && can_turn_right)
5085 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5086 else if (can_turn_left)
5087 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5088 else if (can_turn_right)
5089 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5091 MovDir[x][y] = back_dir;
5093 MovDelay[x][y] = 6 + RND(40);
5095 else if (element == EL_PIG)
5097 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5098 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5099 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5100 boolean should_turn_left, should_turn_right, should_move_on;
5102 int rnd = RND(rnd_value);
5104 should_turn_left = (can_turn_left &&
5106 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5107 y + back_dy + left_dy)));
5108 should_turn_right = (can_turn_right &&
5110 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5111 y + back_dy + right_dy)));
5112 should_move_on = (can_move_on &&
5115 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5116 y + move_dy + left_dy) ||
5117 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5118 y + move_dy + right_dy)));
5120 if (should_turn_left || should_turn_right || should_move_on)
5122 if (should_turn_left && should_turn_right && should_move_on)
5123 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5124 rnd < 2 * rnd_value / 3 ? right_dir :
5126 else if (should_turn_left && should_turn_right)
5127 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5128 else if (should_turn_left && should_move_on)
5129 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5130 else if (should_turn_right && should_move_on)
5131 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5132 else if (should_turn_left)
5133 MovDir[x][y] = left_dir;
5134 else if (should_turn_right)
5135 MovDir[x][y] = right_dir;
5136 else if (should_move_on)
5137 MovDir[x][y] = old_move_dir;
5139 else if (can_move_on && rnd > rnd_value / 8)
5140 MovDir[x][y] = old_move_dir;
5141 else if (can_turn_left && can_turn_right)
5142 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5143 else if (can_turn_left && rnd > rnd_value / 8)
5144 MovDir[x][y] = left_dir;
5145 else if (can_turn_right && rnd > rnd_value/8)
5146 MovDir[x][y] = right_dir;
5148 MovDir[x][y] = back_dir;
5150 xx = x + move_xy[MovDir[x][y]].dx;
5151 yy = y + move_xy[MovDir[x][y]].dy;
5153 if (!IN_LEV_FIELD(xx, yy) ||
5154 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5155 MovDir[x][y] = old_move_dir;
5159 else if (element == EL_DRAGON)
5161 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5162 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5163 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5165 int rnd = RND(rnd_value);
5167 if (can_move_on && rnd > rnd_value / 8)
5168 MovDir[x][y] = old_move_dir;
5169 else if (can_turn_left && can_turn_right)
5170 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5171 else if (can_turn_left && rnd > rnd_value / 8)
5172 MovDir[x][y] = left_dir;
5173 else if (can_turn_right && rnd > rnd_value / 8)
5174 MovDir[x][y] = right_dir;
5176 MovDir[x][y] = back_dir;
5178 xx = x + move_xy[MovDir[x][y]].dx;
5179 yy = y + move_xy[MovDir[x][y]].dy;
5181 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5182 MovDir[x][y] = old_move_dir;
5186 else if (element == EL_MOLE)
5188 boolean can_move_on =
5189 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5190 IS_AMOEBOID(Feld[move_x][move_y]) ||
5191 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5194 boolean can_turn_left =
5195 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5196 IS_AMOEBOID(Feld[left_x][left_y])));
5198 boolean can_turn_right =
5199 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5200 IS_AMOEBOID(Feld[right_x][right_y])));
5202 if (can_turn_left && can_turn_right)
5203 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5204 else if (can_turn_left)
5205 MovDir[x][y] = left_dir;
5207 MovDir[x][y] = right_dir;
5210 if (MovDir[x][y] != old_move_dir)
5213 else if (element == EL_BALLOON)
5215 MovDir[x][y] = game.wind_direction;
5218 else if (element == EL_SPRING)
5220 #if USE_NEW_SPRING_BUMPER
5221 if (MovDir[x][y] & MV_HORIZONTAL)
5223 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5224 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5226 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5227 ResetGfxAnimation(move_x, move_y);
5228 DrawLevelField(move_x, move_y);
5230 MovDir[x][y] = back_dir;
5232 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5233 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5234 MovDir[x][y] = MV_NONE;
5237 if (MovDir[x][y] & MV_HORIZONTAL &&
5238 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5239 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5240 MovDir[x][y] = MV_NONE;
5245 else if (element == EL_ROBOT ||
5246 element == EL_SATELLITE ||
5247 element == EL_PENGUIN ||
5248 element == EL_EMC_ANDROID)
5250 int attr_x = -1, attr_y = -1;
5261 for (i = 0; i < MAX_PLAYERS; i++)
5263 struct PlayerInfo *player = &stored_player[i];
5264 int jx = player->jx, jy = player->jy;
5266 if (!player->active)
5270 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5278 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5279 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5280 game.engine_version < VERSION_IDENT(3,1,0,0)))
5286 if (element == EL_PENGUIN)
5289 static int xy[4][2] =
5297 for (i = 0; i < NUM_DIRECTIONS; i++)
5299 int ex = x + xy[i][0];
5300 int ey = y + xy[i][1];
5302 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5311 MovDir[x][y] = MV_NONE;
5313 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5314 else if (attr_x > x)
5315 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5317 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5318 else if (attr_y > y)
5319 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5321 if (element == EL_ROBOT)
5325 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5326 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5327 Moving2Blocked(x, y, &newx, &newy);
5329 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5330 MovDelay[x][y] = 8 + 8 * !RND(3);
5332 MovDelay[x][y] = 16;
5334 else if (element == EL_PENGUIN)
5340 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5342 boolean first_horiz = RND(2);
5343 int new_move_dir = MovDir[x][y];
5346 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5347 Moving2Blocked(x, y, &newx, &newy);
5349 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5353 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5354 Moving2Blocked(x, y, &newx, &newy);
5356 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5359 MovDir[x][y] = old_move_dir;
5363 else if (element == EL_SATELLITE)
5369 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5371 boolean first_horiz = RND(2);
5372 int new_move_dir = MovDir[x][y];
5375 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5376 Moving2Blocked(x, y, &newx, &newy);
5378 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5382 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5383 Moving2Blocked(x, y, &newx, &newy);
5385 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5388 MovDir[x][y] = old_move_dir;
5392 else if (element == EL_EMC_ANDROID)
5394 static int check_pos[16] =
5396 -1, /* 0 => (invalid) */
5397 7, /* 1 => MV_LEFT */
5398 3, /* 2 => MV_RIGHT */
5399 -1, /* 3 => (invalid) */
5401 0, /* 5 => MV_LEFT | MV_UP */
5402 2, /* 6 => MV_RIGHT | MV_UP */
5403 -1, /* 7 => (invalid) */
5404 5, /* 8 => MV_DOWN */
5405 6, /* 9 => MV_LEFT | MV_DOWN */
5406 4, /* 10 => MV_RIGHT | MV_DOWN */
5407 -1, /* 11 => (invalid) */
5408 -1, /* 12 => (invalid) */
5409 -1, /* 13 => (invalid) */
5410 -1, /* 14 => (invalid) */
5411 -1, /* 15 => (invalid) */
5419 { -1, -1, MV_LEFT | MV_UP },
5421 { +1, -1, MV_RIGHT | MV_UP },
5422 { +1, 0, MV_RIGHT },
5423 { +1, +1, MV_RIGHT | MV_DOWN },
5425 { -1, +1, MV_LEFT | MV_DOWN },
5428 int start_pos, check_order;
5429 boolean can_clone = FALSE;
5432 /* check if there is any free field around current position */
5433 for (i = 0; i < 8; i++)
5435 int newx = x + check_xy[i].dx;
5436 int newy = y + check_xy[i].dy;
5438 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5446 if (can_clone) /* randomly find an element to clone */
5450 start_pos = check_pos[RND(8)];
5451 check_order = (RND(2) ? -1 : +1);
5453 for (i = 0; i < 8; i++)
5455 int pos_raw = start_pos + i * check_order;
5456 int pos = (pos_raw + 8) % 8;
5457 int newx = x + check_xy[pos].dx;
5458 int newy = y + check_xy[pos].dy;
5460 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5462 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5463 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5465 Store[x][y] = Feld[newx][newy];
5474 if (can_clone) /* randomly find a direction to move */
5478 start_pos = check_pos[RND(8)];
5479 check_order = (RND(2) ? -1 : +1);
5481 for (i = 0; i < 8; i++)
5483 int pos_raw = start_pos + i * check_order;
5484 int pos = (pos_raw + 8) % 8;
5485 int newx = x + check_xy[pos].dx;
5486 int newy = y + check_xy[pos].dy;
5487 int new_move_dir = check_xy[pos].dir;
5489 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5491 MovDir[x][y] = new_move_dir;
5492 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5501 if (can_clone) /* cloning and moving successful */
5504 /* cannot clone -- try to move towards player */
5506 start_pos = check_pos[MovDir[x][y] & 0x0f];
5507 check_order = (RND(2) ? -1 : +1);
5509 for (i = 0; i < 3; i++)
5511 /* first check start_pos, then previous/next or (next/previous) pos */
5512 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5513 int pos = (pos_raw + 8) % 8;
5514 int newx = x + check_xy[pos].dx;
5515 int newy = y + check_xy[pos].dy;
5516 int new_move_dir = check_xy[pos].dir;
5518 if (IS_PLAYER(newx, newy))
5521 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5523 MovDir[x][y] = new_move_dir;
5524 MovDelay[x][y] = level.android_move_time * 8 + 1;
5531 else if (move_pattern == MV_TURNING_LEFT ||
5532 move_pattern == MV_TURNING_RIGHT ||
5533 move_pattern == MV_TURNING_LEFT_RIGHT ||
5534 move_pattern == MV_TURNING_RIGHT_LEFT ||
5535 move_pattern == MV_TURNING_RANDOM ||
5536 move_pattern == MV_ALL_DIRECTIONS)
5538 boolean can_turn_left =
5539 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5540 boolean can_turn_right =
5541 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5543 if (element_info[element].move_stepsize == 0) /* "not moving" */
5546 if (move_pattern == MV_TURNING_LEFT)
5547 MovDir[x][y] = left_dir;
5548 else if (move_pattern == MV_TURNING_RIGHT)
5549 MovDir[x][y] = right_dir;
5550 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5551 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5552 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5553 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5554 else if (move_pattern == MV_TURNING_RANDOM)
5555 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5556 can_turn_right && !can_turn_left ? right_dir :
5557 RND(2) ? left_dir : right_dir);
5558 else if (can_turn_left && can_turn_right)
5559 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5560 else if (can_turn_left)
5561 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5562 else if (can_turn_right)
5563 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5565 MovDir[x][y] = back_dir;
5567 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5569 else if (move_pattern == MV_HORIZONTAL ||
5570 move_pattern == MV_VERTICAL)
5572 if (move_pattern & old_move_dir)
5573 MovDir[x][y] = back_dir;
5574 else if (move_pattern == MV_HORIZONTAL)
5575 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5576 else if (move_pattern == MV_VERTICAL)
5577 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5579 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5581 else if (move_pattern & MV_ANY_DIRECTION)
5583 MovDir[x][y] = move_pattern;
5584 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5586 else if (move_pattern & MV_WIND_DIRECTION)
5588 MovDir[x][y] = game.wind_direction;
5589 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5591 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5593 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5594 MovDir[x][y] = left_dir;
5595 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5596 MovDir[x][y] = right_dir;
5598 if (MovDir[x][y] != old_move_dir)
5599 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5601 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5603 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5604 MovDir[x][y] = right_dir;
5605 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5606 MovDir[x][y] = left_dir;
5608 if (MovDir[x][y] != old_move_dir)
5609 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5611 else if (move_pattern == MV_TOWARDS_PLAYER ||
5612 move_pattern == MV_AWAY_FROM_PLAYER)
5614 int attr_x = -1, attr_y = -1;
5616 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5627 for (i = 0; i < MAX_PLAYERS; i++)
5629 struct PlayerInfo *player = &stored_player[i];
5630 int jx = player->jx, jy = player->jy;
5632 if (!player->active)
5636 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5644 MovDir[x][y] = MV_NONE;
5646 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5647 else if (attr_x > x)
5648 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5650 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5651 else if (attr_y > y)
5652 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5654 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5656 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5658 boolean first_horiz = RND(2);
5659 int new_move_dir = MovDir[x][y];
5661 if (element_info[element].move_stepsize == 0) /* "not moving" */
5663 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5664 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5670 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5671 Moving2Blocked(x, y, &newx, &newy);
5673 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5677 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5678 Moving2Blocked(x, y, &newx, &newy);
5680 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5683 MovDir[x][y] = old_move_dir;
5686 else if (move_pattern == MV_WHEN_PUSHED ||
5687 move_pattern == MV_WHEN_DROPPED)
5689 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5690 MovDir[x][y] = MV_NONE;
5694 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5696 static int test_xy[7][2] =
5706 static int test_dir[7] =
5716 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5717 int move_preference = -1000000; /* start with very low preference */
5718 int new_move_dir = MV_NONE;
5719 int start_test = RND(4);
5722 for (i = 0; i < NUM_DIRECTIONS; i++)
5724 int move_dir = test_dir[start_test + i];
5725 int move_dir_preference;
5727 xx = x + test_xy[start_test + i][0];
5728 yy = y + test_xy[start_test + i][1];
5730 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5731 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5733 new_move_dir = move_dir;
5738 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5741 move_dir_preference = -1 * RunnerVisit[xx][yy];
5742 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5743 move_dir_preference = PlayerVisit[xx][yy];
5745 if (move_dir_preference > move_preference)
5747 /* prefer field that has not been visited for the longest time */
5748 move_preference = move_dir_preference;
5749 new_move_dir = move_dir;
5751 else if (move_dir_preference == move_preference &&
5752 move_dir == old_move_dir)
5754 /* prefer last direction when all directions are preferred equally */
5755 move_preference = move_dir_preference;
5756 new_move_dir = move_dir;
5760 MovDir[x][y] = new_move_dir;
5761 if (old_move_dir != new_move_dir)
5762 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5766 static void TurnRound(int x, int y)
5768 int direction = MovDir[x][y];
5770 int element, graphic;
5775 GfxDir[x][y] = MovDir[x][y];
5777 if (direction != MovDir[x][y])
5781 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5784 ResetGfxFrame(x, y, FALSE);
5786 element = Feld[x][y];
5787 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5789 if (graphic_info[graphic].anim_global_sync)
5790 GfxFrame[x][y] = FrameCounter;
5791 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5792 GfxFrame[x][y] = CustomValue[x][y];
5793 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5794 GfxFrame[x][y] = element_info[element].collect_score;
5795 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5796 GfxFrame[x][y] = ChangeDelay[x][y];
5800 static boolean JustBeingPushed(int x, int y)
5804 for (i = 0; i < MAX_PLAYERS; i++)
5806 struct PlayerInfo *player = &stored_player[i];
5808 if (player->active && player->is_pushing && player->MovPos)
5810 int next_jx = player->jx + (player->jx - player->last_jx);
5811 int next_jy = player->jy + (player->jy - player->last_jy);
5813 if (x == next_jx && y == next_jy)
5821 void StartMoving(int x, int y)
5823 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5824 int element = Feld[x][y];
5829 if (MovDelay[x][y] == 0)
5830 GfxAction[x][y] = ACTION_DEFAULT;
5832 if (CAN_FALL(element) && y < lev_fieldy - 1)
5834 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5835 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5836 if (JustBeingPushed(x, y))
5839 if (element == EL_QUICKSAND_FULL)
5841 if (IS_FREE(x, y + 1))
5843 InitMovingField(x, y, MV_DOWN);
5844 started_moving = TRUE;
5846 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5847 Store[x][y] = EL_ROCK;
5849 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5851 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5853 if (!MovDelay[x][y])
5854 MovDelay[x][y] = TILEY + 1;
5863 Feld[x][y] = EL_QUICKSAND_EMPTY;
5864 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5865 Store[x][y + 1] = Store[x][y];
5868 PlayLevelSoundAction(x, y, ACTION_FILLING);
5871 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5872 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5874 InitMovingField(x, y, MV_DOWN);
5875 started_moving = TRUE;
5877 Feld[x][y] = EL_QUICKSAND_FILLING;
5878 Store[x][y] = element;
5880 PlayLevelSoundAction(x, y, ACTION_FILLING);
5882 else if (element == EL_MAGIC_WALL_FULL)
5884 if (IS_FREE(x, y + 1))
5886 InitMovingField(x, y, MV_DOWN);
5887 started_moving = TRUE;
5889 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5890 Store[x][y] = EL_CHANGED(Store[x][y]);
5892 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5894 if (!MovDelay[x][y])
5895 MovDelay[x][y] = TILEY/4 + 1;
5904 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5905 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5906 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5910 else if (element == EL_BD_MAGIC_WALL_FULL)
5912 if (IS_FREE(x, y + 1))
5914 InitMovingField(x, y, MV_DOWN);
5915 started_moving = TRUE;
5917 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5918 Store[x][y] = EL_CHANGED2(Store[x][y]);
5920 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5922 if (!MovDelay[x][y])
5923 MovDelay[x][y] = TILEY/4 + 1;
5932 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5933 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5934 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5938 else if (CAN_PASS_MAGIC_WALL(element) &&
5939 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5940 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5942 InitMovingField(x, y, MV_DOWN);
5943 started_moving = TRUE;
5946 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5947 EL_BD_MAGIC_WALL_FILLING);
5948 Store[x][y] = element;
5950 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5952 SplashAcid(x, y + 1);
5954 InitMovingField(x, y, MV_DOWN);
5955 started_moving = TRUE;
5957 Store[x][y] = EL_ACID;
5959 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5960 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5962 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5963 CAN_FALL(element) && WasJustFalling[x][y] &&
5964 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5966 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5967 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5968 (Feld[x][y + 1] == EL_BLOCKED)))
5970 /* this is needed for a special case not covered by calling "Impact()"
5971 from "ContinueMoving()": if an element moves to a tile directly below
5972 another element which was just falling on that tile (which was empty
5973 in the previous frame), the falling element above would just stop
5974 instead of smashing the element below (in previous version, the above
5975 element was just checked for "moving" instead of "falling", resulting
5976 in incorrect smashes caused by horizontal movement of the above
5977 element; also, the case of the player being the element to smash was
5978 simply not covered here... :-/ ) */
5980 CheckCollision[x][y] = 0;
5984 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5986 if (MovDir[x][y] == MV_NONE)
5988 InitMovingField(x, y, MV_DOWN);
5989 started_moving = TRUE;
5992 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5994 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5995 MovDir[x][y] = MV_DOWN;
5997 InitMovingField(x, y, MV_DOWN);
5998 started_moving = TRUE;
6000 else if (element == EL_AMOEBA_DROP)
6002 Feld[x][y] = EL_AMOEBA_GROWING;
6003 Store[x][y] = EL_AMOEBA_WET;
6005 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6006 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6007 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6008 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6010 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6011 (IS_FREE(x - 1, y + 1) ||
6012 Feld[x - 1][y + 1] == EL_ACID));
6013 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6014 (IS_FREE(x + 1, y + 1) ||
6015 Feld[x + 1][y + 1] == EL_ACID));
6016 boolean can_fall_any = (can_fall_left || can_fall_right);
6017 boolean can_fall_both = (can_fall_left && can_fall_right);
6018 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6020 #if USE_NEW_ALL_SLIPPERY
6021 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6023 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6024 can_fall_right = FALSE;
6025 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6026 can_fall_left = FALSE;
6027 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6028 can_fall_right = FALSE;
6029 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6030 can_fall_left = FALSE;
6032 can_fall_any = (can_fall_left || can_fall_right);
6033 can_fall_both = FALSE;
6036 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6038 if (slippery_type == SLIPPERY_ONLY_LEFT)
6039 can_fall_right = FALSE;
6040 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6041 can_fall_left = FALSE;
6042 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6043 can_fall_right = FALSE;
6044 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6045 can_fall_left = FALSE;
6047 can_fall_any = (can_fall_left || can_fall_right);
6048 can_fall_both = (can_fall_left && can_fall_right);
6052 #if USE_NEW_ALL_SLIPPERY
6054 #if USE_NEW_SP_SLIPPERY
6055 /* !!! better use the same properties as for custom elements here !!! */
6056 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6057 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6059 can_fall_right = FALSE; /* slip down on left side */
6060 can_fall_both = FALSE;
6065 #if USE_NEW_ALL_SLIPPERY
6068 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6069 can_fall_right = FALSE; /* slip down on left side */
6071 can_fall_left = !(can_fall_right = RND(2));
6073 can_fall_both = FALSE;
6078 if (game.emulation == EMU_BOULDERDASH ||
6079 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6080 can_fall_right = FALSE; /* slip down on left side */
6082 can_fall_left = !(can_fall_right = RND(2));
6084 can_fall_both = FALSE;
6090 /* if not determined otherwise, prefer left side for slipping down */
6091 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6092 started_moving = TRUE;
6096 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6098 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6101 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6102 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6103 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6104 int belt_dir = game.belt_dir[belt_nr];
6106 if ((belt_dir == MV_LEFT && left_is_free) ||
6107 (belt_dir == MV_RIGHT && right_is_free))
6109 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6111 InitMovingField(x, y, belt_dir);
6112 started_moving = TRUE;
6114 Pushed[x][y] = TRUE;
6115 Pushed[nextx][y] = TRUE;
6117 GfxAction[x][y] = ACTION_DEFAULT;
6121 MovDir[x][y] = 0; /* if element was moving, stop it */
6126 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6128 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6130 if (CAN_MOVE(element) && !started_moving)
6133 int move_pattern = element_info[element].move_pattern;
6138 if (MovDir[x][y] == MV_NONE)
6140 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6141 x, y, element, element_info[element].token_name);
6142 printf("StartMoving(): This should never happen!\n");
6147 Moving2Blocked(x, y, &newx, &newy);
6149 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6152 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6153 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6155 WasJustMoving[x][y] = 0;
6156 CheckCollision[x][y] = 0;
6158 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6160 if (Feld[x][y] != element) /* element has changed */
6164 if (!MovDelay[x][y]) /* start new movement phase */
6166 /* all objects that can change their move direction after each step
6167 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6169 if (element != EL_YAMYAM &&
6170 element != EL_DARK_YAMYAM &&
6171 element != EL_PACMAN &&
6172 !(move_pattern & MV_ANY_DIRECTION) &&
6173 move_pattern != MV_TURNING_LEFT &&
6174 move_pattern != MV_TURNING_RIGHT &&
6175 move_pattern != MV_TURNING_LEFT_RIGHT &&
6176 move_pattern != MV_TURNING_RIGHT_LEFT &&
6177 move_pattern != MV_TURNING_RANDOM)
6181 if (MovDelay[x][y] && (element == EL_BUG ||
6182 element == EL_SPACESHIP ||
6183 element == EL_SP_SNIKSNAK ||
6184 element == EL_SP_ELECTRON ||
6185 element == EL_MOLE))
6186 DrawLevelField(x, y);
6190 if (MovDelay[x][y]) /* wait some time before next movement */
6194 if (element == EL_ROBOT ||
6195 element == EL_YAMYAM ||
6196 element == EL_DARK_YAMYAM)
6198 DrawLevelElementAnimationIfNeeded(x, y, element);
6199 PlayLevelSoundAction(x, y, ACTION_WAITING);
6201 else if (element == EL_SP_ELECTRON)
6202 DrawLevelElementAnimationIfNeeded(x, y, element);
6203 else if (element == EL_DRAGON)
6206 int dir = MovDir[x][y];
6207 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6208 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6209 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6210 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6211 dir == MV_UP ? IMG_FLAMES_1_UP :
6212 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6213 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6215 GfxAction[x][y] = ACTION_ATTACKING;
6217 if (IS_PLAYER(x, y))
6218 DrawPlayerField(x, y);
6220 DrawLevelField(x, y);
6222 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6224 for (i = 1; i <= 3; i++)
6226 int xx = x + i * dx;
6227 int yy = y + i * dy;
6228 int sx = SCREENX(xx);
6229 int sy = SCREENY(yy);
6230 int flame_graphic = graphic + (i - 1);
6232 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6237 int flamed = MovingOrBlocked2Element(xx, yy);
6241 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6243 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6244 RemoveMovingField(xx, yy);
6246 RemoveField(xx, yy);
6248 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6251 RemoveMovingField(xx, yy);
6254 ChangeDelay[xx][yy] = 0;
6256 Feld[xx][yy] = EL_FLAMES;
6258 if (IN_SCR_FIELD(sx, sy))
6260 DrawLevelFieldCrumbledSand(xx, yy);
6261 DrawGraphic(sx, sy, flame_graphic, frame);
6266 if (Feld[xx][yy] == EL_FLAMES)
6267 Feld[xx][yy] = EL_EMPTY;
6268 DrawLevelField(xx, yy);
6273 if (MovDelay[x][y]) /* element still has to wait some time */
6275 PlayLevelSoundAction(x, y, ACTION_WAITING);
6281 /* now make next step */
6283 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6285 if (DONT_COLLIDE_WITH(element) &&
6286 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6287 !PLAYER_ENEMY_PROTECTED(newx, newy))
6289 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6294 else if (CAN_MOVE_INTO_ACID(element) &&
6295 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6296 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6297 (MovDir[x][y] == MV_DOWN ||
6298 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6300 SplashAcid(newx, newy);
6301 Store[x][y] = EL_ACID;
6303 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6305 if (Feld[newx][newy] == EL_EXIT_OPEN)
6308 DrawLevelField(x, y);
6310 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6311 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6312 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6314 local_player->friends_still_needed--;
6315 if (!local_player->friends_still_needed &&
6316 !local_player->GameOver && AllPlayersGone)
6317 local_player->LevelSolved = local_player->GameOver = TRUE;
6321 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6323 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6324 DrawLevelField(newx, newy);
6326 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6328 else if (!IS_FREE(newx, newy))
6330 GfxAction[x][y] = ACTION_WAITING;
6332 if (IS_PLAYER(x, y))
6333 DrawPlayerField(x, y);
6335 DrawLevelField(x, y);
6340 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6342 if (IS_FOOD_PIG(Feld[newx][newy]))
6344 if (IS_MOVING(newx, newy))
6345 RemoveMovingField(newx, newy);
6348 Feld[newx][newy] = EL_EMPTY;
6349 DrawLevelField(newx, newy);
6352 PlayLevelSound(x, y, SND_PIG_DIGGING);
6354 else if (!IS_FREE(newx, newy))
6356 if (IS_PLAYER(x, y))
6357 DrawPlayerField(x, y);
6359 DrawLevelField(x, y);
6364 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6366 if (Store[x][y] != EL_EMPTY)
6368 boolean can_clone = FALSE;
6371 /* check if element to clone is still there */
6372 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6374 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6382 /* cannot clone or target field not free anymore -- do not clone */
6383 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6384 Store[x][y] = EL_EMPTY;
6387 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6389 if (IS_MV_DIAGONAL(MovDir[x][y]))
6391 int diagonal_move_dir = MovDir[x][y];
6392 int stored = Store[x][y];
6393 int change_delay = 8;
6396 /* android is moving diagonally */
6398 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6400 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6401 GfxElement[x][y] = EL_EMC_ANDROID;
6402 GfxAction[x][y] = ACTION_SHRINKING;
6403 GfxDir[x][y] = diagonal_move_dir;
6404 ChangeDelay[x][y] = change_delay;
6406 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6409 DrawLevelGraphicAnimation(x, y, graphic);
6410 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6412 if (Feld[newx][newy] == EL_ACID)
6414 SplashAcid(newx, newy);
6419 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6421 Store[newx][newy] = EL_EMC_ANDROID;
6422 GfxElement[newx][newy] = EL_EMC_ANDROID;
6423 GfxAction[newx][newy] = ACTION_GROWING;
6424 GfxDir[newx][newy] = diagonal_move_dir;
6425 ChangeDelay[newx][newy] = change_delay;
6427 graphic = el_act_dir2img(GfxElement[newx][newy],
6428 GfxAction[newx][newy], GfxDir[newx][newy]);
6430 DrawLevelGraphicAnimation(newx, newy, graphic);
6431 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6437 Feld[newx][newy] = EL_EMPTY;
6438 DrawLevelField(newx, newy);
6440 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6443 else if (!IS_FREE(newx, newy))
6446 if (IS_PLAYER(x, y))
6447 DrawPlayerField(x, y);
6449 DrawLevelField(x, y);
6455 else if (IS_CUSTOM_ELEMENT(element) &&
6456 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6458 int new_element = Feld[newx][newy];
6460 if (!IS_FREE(newx, newy))
6462 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6463 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6466 /* no element can dig solid indestructible elements */
6467 if (IS_INDESTRUCTIBLE(new_element) &&
6468 !IS_DIGGABLE(new_element) &&
6469 !IS_COLLECTIBLE(new_element))
6472 if (AmoebaNr[newx][newy] &&
6473 (new_element == EL_AMOEBA_FULL ||
6474 new_element == EL_BD_AMOEBA ||
6475 new_element == EL_AMOEBA_GROWING))
6477 AmoebaCnt[AmoebaNr[newx][newy]]--;
6478 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6481 if (IS_MOVING(newx, newy))
6482 RemoveMovingField(newx, newy);
6485 RemoveField(newx, newy);
6486 DrawLevelField(newx, newy);
6489 /* if digged element was about to explode, prevent the explosion */
6490 ExplodeField[newx][newy] = EX_TYPE_NONE;
6492 PlayLevelSoundAction(x, y, action);
6495 Store[newx][newy] = EL_EMPTY;
6497 /* this makes it possible to leave the removed element again */
6498 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6499 Store[newx][newy] = new_element;
6501 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6503 int move_leave_element = element_info[element].move_leave_element;
6505 /* this makes it possible to leave the removed element again */
6506 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6507 new_element : move_leave_element);
6511 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6513 RunnerVisit[x][y] = FrameCounter;
6514 PlayerVisit[x][y] /= 8; /* expire player visit path */
6517 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6519 if (!IS_FREE(newx, newy))
6521 if (IS_PLAYER(x, y))
6522 DrawPlayerField(x, y);
6524 DrawLevelField(x, y);
6530 boolean wanna_flame = !RND(10);
6531 int dx = newx - x, dy = newy - y;
6532 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6533 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6534 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6535 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6536 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6537 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6540 IS_CLASSIC_ENEMY(element1) ||
6541 IS_CLASSIC_ENEMY(element2)) &&
6542 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6543 element1 != EL_FLAMES && element2 != EL_FLAMES)
6545 ResetGfxAnimation(x, y);
6546 GfxAction[x][y] = ACTION_ATTACKING;
6548 if (IS_PLAYER(x, y))
6549 DrawPlayerField(x, y);
6551 DrawLevelField(x, y);
6553 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6555 MovDelay[x][y] = 50;
6559 RemoveField(newx, newy);
6561 Feld[newx][newy] = EL_FLAMES;
6562 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6565 RemoveField(newx1, newy1);
6567 Feld[newx1][newy1] = EL_FLAMES;
6569 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6572 RemoveField(newx2, newy2);
6574 Feld[newx2][newy2] = EL_FLAMES;
6581 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6582 Feld[newx][newy] == EL_DIAMOND)
6584 if (IS_MOVING(newx, newy))
6585 RemoveMovingField(newx, newy);
6588 Feld[newx][newy] = EL_EMPTY;
6589 DrawLevelField(newx, newy);
6592 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6594 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6595 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6597 if (AmoebaNr[newx][newy])
6599 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6600 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6601 Feld[newx][newy] == EL_BD_AMOEBA)
6602 AmoebaCnt[AmoebaNr[newx][newy]]--;
6607 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6609 RemoveMovingField(newx, newy);
6612 if (IS_MOVING(newx, newy))
6614 RemoveMovingField(newx, newy);
6619 Feld[newx][newy] = EL_EMPTY;
6620 DrawLevelField(newx, newy);
6623 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6625 else if ((element == EL_PACMAN || element == EL_MOLE)
6626 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6628 if (AmoebaNr[newx][newy])
6630 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6631 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6632 Feld[newx][newy] == EL_BD_AMOEBA)
6633 AmoebaCnt[AmoebaNr[newx][newy]]--;
6636 if (element == EL_MOLE)
6638 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6639 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6641 ResetGfxAnimation(x, y);
6642 GfxAction[x][y] = ACTION_DIGGING;
6643 DrawLevelField(x, y);
6645 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6647 return; /* wait for shrinking amoeba */
6649 else /* element == EL_PACMAN */
6651 Feld[newx][newy] = EL_EMPTY;
6652 DrawLevelField(newx, newy);
6653 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6656 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6657 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6658 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6660 /* wait for shrinking amoeba to completely disappear */
6663 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6665 /* object was running against a wall */
6670 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6671 if (move_pattern & MV_ANY_DIRECTION &&
6672 move_pattern == MovDir[x][y])
6674 int blocking_element =
6675 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6677 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6680 element = Feld[x][y]; /* element might have changed */
6684 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6685 DrawLevelElementAnimation(x, y, element);
6687 if (DONT_TOUCH(element))
6688 TestIfBadThingTouchesPlayer(x, y);
6693 InitMovingField(x, y, MovDir[x][y]);
6695 PlayLevelSoundAction(x, y, ACTION_MOVING);
6699 ContinueMoving(x, y);
6702 void ContinueMoving(int x, int y)
6704 int element = Feld[x][y];
6705 struct ElementInfo *ei = &element_info[element];
6706 int direction = MovDir[x][y];
6707 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6708 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6709 int newx = x + dx, newy = y + dy;
6710 int stored = Store[x][y];
6711 int stored_new = Store[newx][newy];
6712 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6713 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6714 boolean last_line = (newy == lev_fieldy - 1);
6716 MovPos[x][y] += getElementMoveStepsize(x, y);
6718 if (pushed_by_player) /* special case: moving object pushed by player */
6719 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6721 if (ABS(MovPos[x][y]) < TILEX)
6723 DrawLevelField(x, y);
6725 return; /* element is still moving */
6728 /* element reached destination field */
6730 Feld[x][y] = EL_EMPTY;
6731 Feld[newx][newy] = element;
6732 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6734 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6736 element = Feld[newx][newy] = EL_ACID;
6738 else if (element == EL_MOLE)
6740 Feld[x][y] = EL_SAND;
6742 DrawLevelFieldCrumbledSandNeighbours(x, y);
6744 else if (element == EL_QUICKSAND_FILLING)
6746 element = Feld[newx][newy] = get_next_element(element);
6747 Store[newx][newy] = Store[x][y];
6749 else if (element == EL_QUICKSAND_EMPTYING)
6751 Feld[x][y] = get_next_element(element);
6752 element = Feld[newx][newy] = Store[x][y];
6754 else if (element == EL_MAGIC_WALL_FILLING)
6756 element = Feld[newx][newy] = get_next_element(element);
6757 if (!game.magic_wall_active)
6758 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6759 Store[newx][newy] = Store[x][y];
6761 else if (element == EL_MAGIC_WALL_EMPTYING)
6763 Feld[x][y] = get_next_element(element);
6764 if (!game.magic_wall_active)
6765 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6766 element = Feld[newx][newy] = Store[x][y];
6768 #if USE_NEW_CUSTOM_VALUE
6769 InitField(newx, newy, FALSE);
6772 else if (element == EL_BD_MAGIC_WALL_FILLING)
6774 element = Feld[newx][newy] = get_next_element(element);
6775 if (!game.magic_wall_active)
6776 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6777 Store[newx][newy] = Store[x][y];
6779 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6781 Feld[x][y] = get_next_element(element);
6782 if (!game.magic_wall_active)
6783 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6784 element = Feld[newx][newy] = Store[x][y];
6786 #if USE_NEW_CUSTOM_VALUE
6787 InitField(newx, newy, FALSE);
6790 else if (element == EL_AMOEBA_DROPPING)
6792 Feld[x][y] = get_next_element(element);
6793 element = Feld[newx][newy] = Store[x][y];
6795 else if (element == EL_SOKOBAN_OBJECT)
6798 Feld[x][y] = Back[x][y];
6800 if (Back[newx][newy])
6801 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6803 Back[x][y] = Back[newx][newy] = 0;
6806 Store[x][y] = EL_EMPTY;
6811 MovDelay[newx][newy] = 0;
6814 if (CAN_CHANGE_OR_HAS_ACTION(element))
6816 if (CAN_CHANGE(element))
6819 /* copy element change control values to new field */
6820 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6821 ChangePage[newx][newy] = ChangePage[x][y];
6822 ChangeCount[newx][newy] = ChangeCount[x][y];
6823 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6826 #if USE_NEW_CUSTOM_VALUE
6827 CustomValue[newx][newy] = CustomValue[x][y];
6833 #if USE_NEW_CUSTOM_VALUE
6834 CustomValue[newx][newy] = CustomValue[x][y];
6838 ChangeDelay[x][y] = 0;
6839 ChangePage[x][y] = -1;
6840 ChangeCount[x][y] = 0;
6841 ChangeEvent[x][y] = -1;
6843 #if USE_NEW_CUSTOM_VALUE
6844 CustomValue[x][y] = 0;
6847 /* copy animation control values to new field */
6848 GfxFrame[newx][newy] = GfxFrame[x][y];
6849 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6850 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6851 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6853 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6855 /* some elements can leave other elements behind after moving */
6857 if (ei->move_leave_element != EL_EMPTY &&
6858 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6859 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6861 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6862 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6863 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6866 int move_leave_element = ei->move_leave_element;
6870 /* this makes it possible to leave the removed element again */
6871 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6872 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6874 /* this makes it possible to leave the removed element again */
6875 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6876 move_leave_element = stored;
6879 /* this makes it possible to leave the removed element again */
6880 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6881 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6882 move_leave_element = stored;
6885 Feld[x][y] = move_leave_element;
6887 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6888 MovDir[x][y] = direction;
6890 InitField(x, y, FALSE);
6892 if (GFX_CRUMBLED(Feld[x][y]))
6893 DrawLevelFieldCrumbledSandNeighbours(x, y);
6895 if (ELEM_IS_PLAYER(move_leave_element))
6896 RelocatePlayer(x, y, move_leave_element);
6899 /* do this after checking for left-behind element */
6900 ResetGfxAnimation(x, y); /* reset animation values for old field */
6902 if (!CAN_MOVE(element) ||
6903 (CAN_FALL(element) && direction == MV_DOWN &&
6904 (element == EL_SPRING ||
6905 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6906 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6907 GfxDir[x][y] = MovDir[newx][newy] = 0;
6909 DrawLevelField(x, y);
6910 DrawLevelField(newx, newy);
6912 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6914 /* prevent pushed element from moving on in pushed direction */
6915 if (pushed_by_player && CAN_MOVE(element) &&
6916 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6917 !(element_info[element].move_pattern & direction))
6918 TurnRound(newx, newy);
6920 /* prevent elements on conveyor belt from moving on in last direction */
6921 if (pushed_by_conveyor && CAN_FALL(element) &&
6922 direction & MV_HORIZONTAL)
6923 MovDir[newx][newy] = 0;
6925 if (!pushed_by_player)
6927 int nextx = newx + dx, nexty = newy + dy;
6928 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6930 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6932 if (CAN_FALL(element) && direction == MV_DOWN)
6933 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6935 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6936 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6939 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6941 TestIfBadThingTouchesPlayer(newx, newy);
6942 TestIfBadThingTouchesFriend(newx, newy);
6944 if (!IS_CUSTOM_ELEMENT(element))
6945 TestIfBadThingTouchesOtherBadThing(newx, newy);
6947 else if (element == EL_PENGUIN)
6948 TestIfFriendTouchesBadThing(newx, newy);
6950 /* give the player one last chance (one more frame) to move away */
6951 if (CAN_FALL(element) && direction == MV_DOWN &&
6952 (last_line || (!IS_FREE(x, newy + 1) &&
6953 (!IS_PLAYER(x, newy + 1) ||
6954 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6957 if (pushed_by_player && !game.use_change_when_pushing_bug)
6959 int push_side = MV_DIR_OPPOSITE(direction);
6960 struct PlayerInfo *player = PLAYERINFO(x, y);
6962 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6963 player->index_bit, push_side);
6964 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6965 player->index_bit, push_side);
6968 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6969 MovDelay[newx][newy] = 1;
6971 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6973 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6976 if (ChangePage[newx][newy] != -1) /* delayed change */
6978 int page = ChangePage[newx][newy];
6979 struct ElementChangeInfo *change = &ei->change_page[page];
6981 ChangePage[newx][newy] = -1;
6983 if (change->can_change)
6985 if (ChangeElement(newx, newy, element, page))
6987 if (change->post_change_function)
6988 change->post_change_function(newx, newy);
6992 if (change->has_action)
6993 ExecuteCustomElementAction(newx, newy, element, page);
6997 TestIfElementHitsCustomElement(newx, newy, direction);
6998 TestIfPlayerTouchesCustomElement(newx, newy);
6999 TestIfElementTouchesCustomElement(newx, newy);
7002 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7003 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7004 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7005 MV_DIR_OPPOSITE(direction));
7009 int AmoebeNachbarNr(int ax, int ay)
7012 int element = Feld[ax][ay];
7014 static int xy[4][2] =
7022 for (i = 0; i < NUM_DIRECTIONS; i++)
7024 int x = ax + xy[i][0];
7025 int y = ay + xy[i][1];
7027 if (!IN_LEV_FIELD(x, y))
7030 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7031 group_nr = AmoebaNr[x][y];
7037 void AmoebenVereinigen(int ax, int ay)
7039 int i, x, y, xx, yy;
7040 int new_group_nr = AmoebaNr[ax][ay];
7041 static int xy[4][2] =
7049 if (new_group_nr == 0)
7052 for (i = 0; i < NUM_DIRECTIONS; i++)
7057 if (!IN_LEV_FIELD(x, y))
7060 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7061 Feld[x][y] == EL_BD_AMOEBA ||
7062 Feld[x][y] == EL_AMOEBA_DEAD) &&
7063 AmoebaNr[x][y] != new_group_nr)
7065 int old_group_nr = AmoebaNr[x][y];
7067 if (old_group_nr == 0)
7070 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7071 AmoebaCnt[old_group_nr] = 0;
7072 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7073 AmoebaCnt2[old_group_nr] = 0;
7076 SCAN_PLAYFIELD(xx, yy)
7078 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7081 if (AmoebaNr[xx][yy] == old_group_nr)
7082 AmoebaNr[xx][yy] = new_group_nr;
7088 void AmoebeUmwandeln(int ax, int ay)
7092 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7094 int group_nr = AmoebaNr[ax][ay];
7099 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7100 printf("AmoebeUmwandeln(): This should never happen!\n");
7106 SCAN_PLAYFIELD(x, y)
7108 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7111 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7114 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7118 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7119 SND_AMOEBA_TURNING_TO_GEM :
7120 SND_AMOEBA_TURNING_TO_ROCK));
7125 static int xy[4][2] =
7133 for (i = 0; i < NUM_DIRECTIONS; i++)
7138 if (!IN_LEV_FIELD(x, y))
7141 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7143 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7144 SND_AMOEBA_TURNING_TO_GEM :
7145 SND_AMOEBA_TURNING_TO_ROCK));
7152 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7155 int group_nr = AmoebaNr[ax][ay];
7156 boolean done = FALSE;
7161 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7162 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7168 SCAN_PLAYFIELD(x, y)
7170 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7173 if (AmoebaNr[x][y] == group_nr &&
7174 (Feld[x][y] == EL_AMOEBA_DEAD ||
7175 Feld[x][y] == EL_BD_AMOEBA ||
7176 Feld[x][y] == EL_AMOEBA_GROWING))
7179 Feld[x][y] = new_element;
7180 InitField(x, y, FALSE);
7181 DrawLevelField(x, y);
7187 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7188 SND_BD_AMOEBA_TURNING_TO_ROCK :
7189 SND_BD_AMOEBA_TURNING_TO_GEM));
7192 void AmoebeWaechst(int x, int y)
7194 static unsigned long sound_delay = 0;
7195 static unsigned long sound_delay_value = 0;
7197 if (!MovDelay[x][y]) /* start new growing cycle */
7201 if (DelayReached(&sound_delay, sound_delay_value))
7203 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7204 sound_delay_value = 30;
7208 if (MovDelay[x][y]) /* wait some time before growing bigger */
7211 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7213 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7214 6 - MovDelay[x][y]);
7216 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7219 if (!MovDelay[x][y])
7221 Feld[x][y] = Store[x][y];
7223 DrawLevelField(x, y);
7228 void AmoebaDisappearing(int x, int y)
7230 static unsigned long sound_delay = 0;
7231 static unsigned long sound_delay_value = 0;
7233 if (!MovDelay[x][y]) /* start new shrinking cycle */
7237 if (DelayReached(&sound_delay, sound_delay_value))
7238 sound_delay_value = 30;
7241 if (MovDelay[x][y]) /* wait some time before shrinking */
7244 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7246 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7247 6 - MovDelay[x][y]);
7249 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7252 if (!MovDelay[x][y])
7254 Feld[x][y] = EL_EMPTY;
7255 DrawLevelField(x, y);
7257 /* don't let mole enter this field in this cycle;
7258 (give priority to objects falling to this field from above) */
7264 void AmoebeAbleger(int ax, int ay)
7267 int element = Feld[ax][ay];
7268 int graphic = el2img(element);
7269 int newax = ax, neway = ay;
7270 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7271 static int xy[4][2] =
7279 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7281 Feld[ax][ay] = EL_AMOEBA_DEAD;
7282 DrawLevelField(ax, ay);
7286 if (IS_ANIMATED(graphic))
7287 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7289 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7290 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7292 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7295 if (MovDelay[ax][ay])
7299 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7302 int x = ax + xy[start][0];
7303 int y = ay + xy[start][1];
7305 if (!IN_LEV_FIELD(x, y))
7308 if (IS_FREE(x, y) ||
7309 CAN_GROW_INTO(Feld[x][y]) ||
7310 Feld[x][y] == EL_QUICKSAND_EMPTY)
7316 if (newax == ax && neway == ay)
7319 else /* normal or "filled" (BD style) amoeba */
7322 boolean waiting_for_player = FALSE;
7324 for (i = 0; i < NUM_DIRECTIONS; i++)
7326 int j = (start + i) % 4;
7327 int x = ax + xy[j][0];
7328 int y = ay + xy[j][1];
7330 if (!IN_LEV_FIELD(x, y))
7333 if (IS_FREE(x, y) ||
7334 CAN_GROW_INTO(Feld[x][y]) ||
7335 Feld[x][y] == EL_QUICKSAND_EMPTY)
7341 else if (IS_PLAYER(x, y))
7342 waiting_for_player = TRUE;
7345 if (newax == ax && neway == ay) /* amoeba cannot grow */
7347 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7349 Feld[ax][ay] = EL_AMOEBA_DEAD;
7350 DrawLevelField(ax, ay);
7351 AmoebaCnt[AmoebaNr[ax][ay]]--;
7353 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7355 if (element == EL_AMOEBA_FULL)
7356 AmoebeUmwandeln(ax, ay);
7357 else if (element == EL_BD_AMOEBA)
7358 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7363 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7365 /* amoeba gets larger by growing in some direction */
7367 int new_group_nr = AmoebaNr[ax][ay];
7370 if (new_group_nr == 0)
7372 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7373 printf("AmoebeAbleger(): This should never happen!\n");
7378 AmoebaNr[newax][neway] = new_group_nr;
7379 AmoebaCnt[new_group_nr]++;
7380 AmoebaCnt2[new_group_nr]++;
7382 /* if amoeba touches other amoeba(s) after growing, unify them */
7383 AmoebenVereinigen(newax, neway);
7385 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7387 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7393 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7394 (neway == lev_fieldy - 1 && newax != ax))
7396 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7397 Store[newax][neway] = element;
7399 else if (neway == ay || element == EL_EMC_DRIPPER)
7401 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7403 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7407 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7408 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7409 Store[ax][ay] = EL_AMOEBA_DROP;
7410 ContinueMoving(ax, ay);
7414 DrawLevelField(newax, neway);
7417 void Life(int ax, int ay)
7421 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7424 int element = Feld[ax][ay];
7425 int graphic = el2img(element);
7426 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7428 boolean changed = FALSE;
7430 if (IS_ANIMATED(graphic))
7431 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7436 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7437 MovDelay[ax][ay] = life_time;
7439 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7442 if (MovDelay[ax][ay])
7446 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7448 int xx = ax+x1, yy = ay+y1;
7451 if (!IN_LEV_FIELD(xx, yy))
7454 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7456 int x = xx+x2, y = yy+y2;
7458 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7461 if (((Feld[x][y] == element ||
7462 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7464 (IS_FREE(x, y) && Stop[x][y]))
7468 if (xx == ax && yy == ay) /* field in the middle */
7470 if (nachbarn < life_parameter[0] ||
7471 nachbarn > life_parameter[1])
7473 Feld[xx][yy] = EL_EMPTY;
7475 DrawLevelField(xx, yy);
7476 Stop[xx][yy] = TRUE;
7480 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7481 { /* free border field */
7482 if (nachbarn >= life_parameter[2] &&
7483 nachbarn <= life_parameter[3])
7485 Feld[xx][yy] = element;
7486 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7488 DrawLevelField(xx, yy);
7489 Stop[xx][yy] = TRUE;
7496 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7497 SND_GAME_OF_LIFE_GROWING);
7500 static void InitRobotWheel(int x, int y)
7502 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7505 static void RunRobotWheel(int x, int y)
7507 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7510 static void StopRobotWheel(int x, int y)
7512 if (ZX == x && ZY == y)
7516 static void InitTimegateWheel(int x, int y)
7518 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7521 static void RunTimegateWheel(int x, int y)
7523 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7526 static void InitMagicBallDelay(int x, int y)
7529 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7531 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7535 static void ActivateMagicBall(int bx, int by)
7539 if (level.ball_random)
7541 int pos_border = RND(8); /* select one of the eight border elements */
7542 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7543 int xx = pos_content % 3;
7544 int yy = pos_content / 3;
7549 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7550 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7554 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7556 int xx = x - bx + 1;
7557 int yy = y - by + 1;
7559 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7560 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7564 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7567 static void InitDiagonalMovingElement(int x, int y)
7570 MovDelay[x][y] = level.android_move_time;
7574 void CheckExit(int x, int y)
7576 if (local_player->gems_still_needed > 0 ||
7577 local_player->sokobanfields_still_needed > 0 ||
7578 local_player->lights_still_needed > 0)
7580 int element = Feld[x][y];
7581 int graphic = el2img(element);
7583 if (IS_ANIMATED(graphic))
7584 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7589 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7592 Feld[x][y] = EL_EXIT_OPENING;
7594 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7597 void CheckExitSP(int x, int y)
7599 if (local_player->gems_still_needed > 0)
7601 int element = Feld[x][y];
7602 int graphic = el2img(element);
7604 if (IS_ANIMATED(graphic))
7605 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7610 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7613 Feld[x][y] = EL_SP_EXIT_OPENING;
7615 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7618 static void CloseAllOpenTimegates()
7623 SCAN_PLAYFIELD(x, y)
7625 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7628 int element = Feld[x][y];
7630 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7632 Feld[x][y] = EL_TIMEGATE_CLOSING;
7634 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7639 void EdelsteinFunkeln(int x, int y)
7641 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7644 if (Feld[x][y] == EL_BD_DIAMOND)
7647 if (MovDelay[x][y] == 0) /* next animation frame */
7648 MovDelay[x][y] = 11 * !SimpleRND(500);
7650 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7654 if (setup.direct_draw && MovDelay[x][y])
7655 SetDrawtoField(DRAW_BUFFERED);
7657 DrawLevelElementAnimation(x, y, Feld[x][y]);
7659 if (MovDelay[x][y] != 0)
7661 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7662 10 - MovDelay[x][y]);
7664 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7666 if (setup.direct_draw)
7670 dest_x = FX + SCREENX(x) * TILEX;
7671 dest_y = FY + SCREENY(y) * TILEY;
7673 BlitBitmap(drawto_field, window,
7674 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7675 SetDrawtoField(DRAW_DIRECT);
7681 void MauerWaechst(int x, int y)
7685 if (!MovDelay[x][y]) /* next animation frame */
7686 MovDelay[x][y] = 3 * delay;
7688 if (MovDelay[x][y]) /* wait some time before next frame */
7692 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7694 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7695 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7697 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7700 if (!MovDelay[x][y])
7702 if (MovDir[x][y] == MV_LEFT)
7704 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7705 DrawLevelField(x - 1, y);
7707 else if (MovDir[x][y] == MV_RIGHT)
7709 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7710 DrawLevelField(x + 1, y);
7712 else if (MovDir[x][y] == MV_UP)
7714 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7715 DrawLevelField(x, y - 1);
7719 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7720 DrawLevelField(x, y + 1);
7723 Feld[x][y] = Store[x][y];
7725 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7726 DrawLevelField(x, y);
7731 void MauerAbleger(int ax, int ay)
7733 int element = Feld[ax][ay];
7734 int graphic = el2img(element);
7735 boolean oben_frei = FALSE, unten_frei = FALSE;
7736 boolean links_frei = FALSE, rechts_frei = FALSE;
7737 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7738 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7739 boolean new_wall = FALSE;
7741 if (IS_ANIMATED(graphic))
7742 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7744 if (!MovDelay[ax][ay]) /* start building new wall */
7745 MovDelay[ax][ay] = 6;
7747 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7750 if (MovDelay[ax][ay])
7754 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7756 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7758 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7760 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7763 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7764 element == EL_EXPANDABLE_WALL_ANY)
7768 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7769 Store[ax][ay-1] = element;
7770 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7771 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7772 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7773 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7778 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7779 Store[ax][ay+1] = element;
7780 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7781 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7782 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7783 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7788 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7789 element == EL_EXPANDABLE_WALL_ANY ||
7790 element == EL_EXPANDABLE_WALL)
7794 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7795 Store[ax-1][ay] = element;
7796 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7797 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7798 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7799 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7805 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7806 Store[ax+1][ay] = element;
7807 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7808 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7809 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7810 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7815 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7816 DrawLevelField(ax, ay);
7818 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7820 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7821 unten_massiv = TRUE;
7822 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7823 links_massiv = TRUE;
7824 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7825 rechts_massiv = TRUE;
7827 if (((oben_massiv && unten_massiv) ||
7828 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7829 element == EL_EXPANDABLE_WALL) &&
7830 ((links_massiv && rechts_massiv) ||
7831 element == EL_EXPANDABLE_WALL_VERTICAL))
7832 Feld[ax][ay] = EL_WALL;
7835 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7838 void CheckForDragon(int x, int y)
7841 boolean dragon_found = FALSE;
7842 static int xy[4][2] =
7850 for (i = 0; i < NUM_DIRECTIONS; i++)
7852 for (j = 0; j < 4; j++)
7854 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7856 if (IN_LEV_FIELD(xx, yy) &&
7857 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7859 if (Feld[xx][yy] == EL_DRAGON)
7860 dragon_found = TRUE;
7869 for (i = 0; i < NUM_DIRECTIONS; i++)
7871 for (j = 0; j < 3; j++)
7873 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7875 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7877 Feld[xx][yy] = EL_EMPTY;
7878 DrawLevelField(xx, yy);
7887 static void InitBuggyBase(int x, int y)
7889 int element = Feld[x][y];
7890 int activating_delay = FRAMES_PER_SECOND / 4;
7893 (element == EL_SP_BUGGY_BASE ?
7894 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7895 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7897 element == EL_SP_BUGGY_BASE_ACTIVE ?
7898 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7901 static void WarnBuggyBase(int x, int y)
7904 static int xy[4][2] =
7912 for (i = 0; i < NUM_DIRECTIONS; i++)
7914 int xx = x + xy[i][0];
7915 int yy = y + xy[i][1];
7917 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7919 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7926 static void InitTrap(int x, int y)
7928 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7931 static void ActivateTrap(int x, int y)
7933 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7936 static void ChangeActiveTrap(int x, int y)
7938 int graphic = IMG_TRAP_ACTIVE;
7940 /* if new animation frame was drawn, correct crumbled sand border */
7941 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7942 DrawLevelFieldCrumbledSand(x, y);
7945 static int getSpecialActionElement(int element, int number, int base_element)
7947 return (element != EL_EMPTY ? element :
7948 number != -1 ? base_element + number - 1 :
7952 static int getModifiedActionNumber(int value_old, int operator, int operand,
7953 int value_min, int value_max)
7955 int value_new = (operator == CA_MODE_SET ? operand :
7956 operator == CA_MODE_ADD ? value_old + operand :
7957 operator == CA_MODE_SUBTRACT ? value_old - operand :
7958 operator == CA_MODE_MULTIPLY ? value_old * operand :
7959 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7960 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7963 return (value_new < value_min ? value_min :
7964 value_new > value_max ? value_max :
7968 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7970 struct ElementInfo *ei = &element_info[element];
7971 struct ElementChangeInfo *change = &ei->change_page[page];
7972 int target_element = change->target_element;
7973 int action_type = change->action_type;
7974 int action_mode = change->action_mode;
7975 int action_arg = change->action_arg;
7978 if (!change->has_action)
7981 /* ---------- determine action paramater values -------------------------- */
7983 int level_time_value =
7984 (level.time > 0 ? TimeLeft :
7987 int action_arg_element =
7988 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7989 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7990 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7993 int action_arg_direction =
7994 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7995 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7996 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7997 change->actual_trigger_side :
7998 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7999 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8002 int action_arg_number_min =
8003 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8006 int action_arg_number_max =
8007 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8008 action_type == CA_SET_LEVEL_GEMS ? 999 :
8009 action_type == CA_SET_LEVEL_TIME ? 9999 :
8010 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8011 action_type == CA_SET_CE_VALUE ? 9999 :
8012 action_type == CA_SET_CE_SCORE ? 9999 :
8015 int action_arg_number_reset =
8016 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8017 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8018 action_type == CA_SET_LEVEL_TIME ? level.time :
8019 action_type == CA_SET_LEVEL_SCORE ? 0 :
8021 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8023 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8025 action_type == CA_SET_CE_SCORE ? 0 :
8028 int action_arg_number =
8029 (action_arg <= CA_ARG_MAX ? action_arg :
8030 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8031 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8032 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8033 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8034 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8035 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8036 #if USE_NEW_CUSTOM_VALUE
8037 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8039 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8041 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8042 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8043 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8044 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8045 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8046 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8047 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8048 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8049 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8050 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8051 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8054 int action_arg_number_old =
8055 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8056 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8057 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8058 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8059 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8062 int action_arg_number_new =
8063 getModifiedActionNumber(action_arg_number_old,
8064 action_mode, action_arg_number,
8065 action_arg_number_min, action_arg_number_max);
8067 int trigger_player_bits =
8068 (change->actual_trigger_player >= EL_PLAYER_1 &&
8069 change->actual_trigger_player <= EL_PLAYER_4 ?
8070 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8073 int action_arg_player_bits =
8074 (action_arg >= CA_ARG_PLAYER_1 &&
8075 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8076 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8079 /* ---------- execute action -------------------------------------------- */
8088 /* ---------- level actions ------------------------------------------- */
8090 case CA_RESTART_LEVEL:
8092 game.restart_level = TRUE;
8097 case CA_SHOW_ENVELOPE:
8099 int element = getSpecialActionElement(action_arg_element,
8100 action_arg_number, EL_ENVELOPE_1);
8102 if (IS_ENVELOPE(element))
8103 local_player->show_envelope = element;
8108 case CA_SET_LEVEL_TIME:
8110 if (level.time > 0) /* only modify limited time value */
8112 TimeLeft = action_arg_number_new;
8114 DrawGameValue_Time(TimeLeft);
8116 if (!TimeLeft && setup.time_limit)
8117 for (i = 0; i < MAX_PLAYERS; i++)
8118 KillPlayer(&stored_player[i]);
8124 case CA_SET_LEVEL_SCORE:
8126 local_player->score = action_arg_number_new;
8128 DrawGameValue_Score(local_player->score);
8133 case CA_SET_LEVEL_GEMS:
8135 local_player->gems_still_needed = action_arg_number_new;
8137 DrawGameValue_Emeralds(local_player->gems_still_needed);
8142 #if !USE_PLAYER_GRAVITY
8143 case CA_SET_LEVEL_GRAVITY:
8145 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8146 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8147 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8153 case CA_SET_LEVEL_WIND:
8155 game.wind_direction = action_arg_direction;
8160 /* ---------- player actions ------------------------------------------ */
8162 case CA_MOVE_PLAYER:
8164 /* automatically move to the next field in specified direction */
8165 for (i = 0; i < MAX_PLAYERS; i++)
8166 if (trigger_player_bits & (1 << i))
8167 stored_player[i].programmed_action = action_arg_direction;
8172 case CA_EXIT_PLAYER:
8174 for (i = 0; i < MAX_PLAYERS; i++)
8175 if (action_arg_player_bits & (1 << i))
8176 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8181 case CA_KILL_PLAYER:
8183 for (i = 0; i < MAX_PLAYERS; i++)
8184 if (action_arg_player_bits & (1 << i))
8185 KillPlayer(&stored_player[i]);
8190 case CA_SET_PLAYER_KEYS:
8192 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8193 int element = getSpecialActionElement(action_arg_element,
8194 action_arg_number, EL_KEY_1);
8196 if (IS_KEY(element))
8198 for (i = 0; i < MAX_PLAYERS; i++)
8200 if (trigger_player_bits & (1 << i))
8202 stored_player[i].key[KEY_NR(element)] = key_state;
8205 DrawGameDoorValues();
8207 DrawGameValue_Keys(stored_player[i].key);
8210 redraw_mask |= REDRAW_DOOR_1;
8218 case CA_SET_PLAYER_SPEED:
8220 for (i = 0; i < MAX_PLAYERS; i++)
8222 if (trigger_player_bits & (1 << i))
8224 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8226 if (action_arg == CA_ARG_SPEED_FASTER &&
8227 stored_player[i].cannot_move)
8229 action_arg_number = STEPSIZE_VERY_SLOW;
8231 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8232 action_arg == CA_ARG_SPEED_FASTER)
8234 action_arg_number = 2;
8235 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8238 else if (action_arg == CA_ARG_NUMBER_RESET)
8240 action_arg_number = level.initial_player_stepsize[i];
8244 getModifiedActionNumber(move_stepsize,
8247 action_arg_number_min,
8248 action_arg_number_max);
8251 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8253 /* make sure that value is power of 2 */
8254 move_stepsize = (1 << log_2(move_stepsize));
8256 /* do no immediately change -- the player might just be moving */
8257 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8259 stored_player[i].cannot_move =
8260 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8268 case CA_SET_PLAYER_SHIELD:
8270 for (i = 0; i < MAX_PLAYERS; i++)
8272 if (trigger_player_bits & (1 << i))
8274 if (action_arg == CA_ARG_SHIELD_OFF)
8276 stored_player[i].shield_normal_time_left = 0;
8277 stored_player[i].shield_deadly_time_left = 0;
8279 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8281 stored_player[i].shield_normal_time_left = 999999;
8283 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8285 stored_player[i].shield_normal_time_left = 999999;
8286 stored_player[i].shield_deadly_time_left = 999999;
8294 #if USE_PLAYER_GRAVITY
8295 case CA_SET_PLAYER_GRAVITY:
8297 for (i = 0; i < MAX_PLAYERS; i++)
8299 if (trigger_player_bits & (1 << i))
8301 stored_player[i].gravity =
8302 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8303 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8304 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8305 stored_player[i].gravity);
8313 case CA_SET_PLAYER_ARTWORK:
8315 for (i = 0; i < MAX_PLAYERS; i++)
8317 if (trigger_player_bits & (1 << i))
8319 int artwork_element = action_arg_element;
8321 if (action_arg == CA_ARG_ELEMENT_RESET)
8323 (level.use_artwork_element[i] ? level.artwork_element[i] :
8324 stored_player[i].element_nr);
8326 stored_player[i].artwork_element = artwork_element;
8328 SetPlayerWaiting(&stored_player[i], FALSE);
8330 /* set number of special actions for bored and sleeping animation */
8331 stored_player[i].num_special_action_bored =
8332 get_num_special_action(artwork_element,
8333 ACTION_BORING_1, ACTION_BORING_LAST);
8334 stored_player[i].num_special_action_sleeping =
8335 get_num_special_action(artwork_element,
8336 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8343 /* ---------- CE actions ---------------------------------------------- */
8345 case CA_SET_CE_VALUE:
8347 #if USE_NEW_CUSTOM_VALUE
8348 int last_ce_value = CustomValue[x][y];
8350 CustomValue[x][y] = action_arg_number_new;
8353 printf("::: CE value == %d\n", CustomValue[x][y]);
8356 if (CustomValue[x][y] != last_ce_value)
8358 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8359 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8361 if (CustomValue[x][y] == 0)
8364 printf("::: CE_VALUE_GETS_ZERO\n");
8367 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8368 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8371 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8381 case CA_SET_CE_SCORE:
8383 #if USE_NEW_CUSTOM_VALUE
8384 int last_ce_score = ei->collect_score;
8386 ei->collect_score = action_arg_number_new;
8389 printf("::: CE score == %d\n", ei->collect_score);
8392 if (ei->collect_score != last_ce_score)
8394 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8395 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8397 if (ei->collect_score == 0)
8400 printf("::: CE_SCORE_GETS_ZERO\n");
8403 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8404 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8407 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8417 /* ---------- engine actions ------------------------------------------ */
8419 case CA_SET_ENGINE_SCAN_MODE:
8421 InitPlayfieldScanMode(action_arg);
8431 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8433 int old_element = Feld[x][y];
8434 int new_element = get_element_from_group_element(element);
8435 int previous_move_direction = MovDir[x][y];
8436 #if USE_NEW_CUSTOM_VALUE
8437 int last_ce_value = CustomValue[x][y];
8439 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8440 IS_WALKABLE(old_element));
8443 /* check if element under the player changes from accessible to unaccessible
8444 (needed for special case of dropping element which then changes) */
8445 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8446 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8456 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8457 RemoveMovingField(x, y);
8461 Feld[x][y] = new_element;
8463 #if !USE_GFX_RESET_GFX_ANIMATION
8464 ResetGfxAnimation(x, y);
8465 ResetRandomAnimationValue(x, y);
8468 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8469 MovDir[x][y] = previous_move_direction;
8471 #if USE_NEW_CUSTOM_VALUE
8472 if (element_info[new_element].use_last_ce_value)
8473 CustomValue[x][y] = last_ce_value;
8476 InitField_WithBug1(x, y, FALSE);
8478 new_element = Feld[x][y]; /* element may have changed */
8480 #if USE_GFX_RESET_GFX_ANIMATION
8481 ResetGfxAnimation(x, y);
8482 ResetRandomAnimationValue(x, y);
8485 DrawLevelField(x, y);
8487 if (GFX_CRUMBLED(new_element))
8488 DrawLevelFieldCrumbledSandNeighbours(x, y);
8492 /* check if element under the player changes from accessible to unaccessible
8493 (needed for special case of dropping element which then changes) */
8494 /* (must be checked after creating new element for walkable group elements) */
8495 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8496 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8504 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8505 if (ELEM_IS_PLAYER(new_element))
8506 RelocatePlayer(x, y, new_element);
8509 ChangeCount[x][y]++; /* count number of changes in the same frame */
8511 TestIfBadThingTouchesPlayer(x, y);
8512 TestIfPlayerTouchesCustomElement(x, y);
8513 TestIfElementTouchesCustomElement(x, y);
8516 static void CreateField(int x, int y, int element)
8518 CreateFieldExt(x, y, element, FALSE);
8521 static void CreateElementFromChange(int x, int y, int element)
8523 element = GET_VALID_RUNTIME_ELEMENT(element);
8525 #if USE_STOP_CHANGED_ELEMENTS
8526 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8528 int old_element = Feld[x][y];
8530 /* prevent changed element from moving in same engine frame
8531 unless both old and new element can either fall or move */
8532 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8533 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8538 CreateFieldExt(x, y, element, TRUE);
8541 static boolean ChangeElement(int x, int y, int element, int page)
8543 struct ElementInfo *ei = &element_info[element];
8544 struct ElementChangeInfo *change = &ei->change_page[page];
8545 int ce_value = CustomValue[x][y];
8546 int ce_score = ei->collect_score;
8548 int old_element = Feld[x][y];
8550 /* always use default change event to prevent running into a loop */
8551 if (ChangeEvent[x][y] == -1)
8552 ChangeEvent[x][y] = CE_DELAY;
8554 if (ChangeEvent[x][y] == CE_DELAY)
8556 /* reset actual trigger element, trigger player and action element */
8557 change->actual_trigger_element = EL_EMPTY;
8558 change->actual_trigger_player = EL_PLAYER_1;
8559 change->actual_trigger_side = CH_SIDE_NONE;
8560 change->actual_trigger_ce_value = 0;
8561 change->actual_trigger_ce_score = 0;
8564 /* do not change elements more than a specified maximum number of changes */
8565 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8568 ChangeCount[x][y]++; /* count number of changes in the same frame */
8570 if (change->explode)
8577 if (change->use_target_content)
8579 boolean complete_replace = TRUE;
8580 boolean can_replace[3][3];
8583 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8586 boolean is_walkable;
8587 boolean is_diggable;
8588 boolean is_collectible;
8589 boolean is_removable;
8590 boolean is_destructible;
8591 int ex = x + xx - 1;
8592 int ey = y + yy - 1;
8593 int content_element = change->target_content.e[xx][yy];
8596 can_replace[xx][yy] = TRUE;
8598 if (ex == x && ey == y) /* do not check changing element itself */
8601 if (content_element == EL_EMPTY_SPACE)
8603 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8608 if (!IN_LEV_FIELD(ex, ey))
8610 can_replace[xx][yy] = FALSE;
8611 complete_replace = FALSE;
8618 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8619 e = MovingOrBlocked2Element(ex, ey);
8621 is_empty = (IS_FREE(ex, ey) ||
8622 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8624 is_walkable = (is_empty || IS_WALKABLE(e));
8625 is_diggable = (is_empty || IS_DIGGABLE(e));
8626 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8627 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8628 is_removable = (is_diggable || is_collectible);
8630 can_replace[xx][yy] =
8631 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8632 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8633 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8634 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8635 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8636 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8637 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8639 if (!can_replace[xx][yy])
8640 complete_replace = FALSE;
8643 if (!change->only_if_complete || complete_replace)
8645 boolean something_has_changed = FALSE;
8647 if (change->only_if_complete && change->use_random_replace &&
8648 RND(100) < change->random_percentage)
8651 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8653 int ex = x + xx - 1;
8654 int ey = y + yy - 1;
8655 int content_element;
8657 if (can_replace[xx][yy] && (!change->use_random_replace ||
8658 RND(100) < change->random_percentage))
8660 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8661 RemoveMovingField(ex, ey);
8663 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8665 content_element = change->target_content.e[xx][yy];
8666 target_element = GET_TARGET_ELEMENT(content_element, change,
8667 ce_value, ce_score);
8669 CreateElementFromChange(ex, ey, target_element);
8671 something_has_changed = TRUE;
8673 /* for symmetry reasons, freeze newly created border elements */
8674 if (ex != x || ey != y)
8675 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8679 if (something_has_changed)
8681 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8682 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8688 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8689 ce_value, ce_score);
8691 if (element == EL_DIAGONAL_GROWING ||
8692 element == EL_DIAGONAL_SHRINKING)
8694 target_element = Store[x][y];
8696 Store[x][y] = EL_EMPTY;
8699 CreateElementFromChange(x, y, target_element);
8701 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8702 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8705 /* this uses direct change before indirect change */
8706 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8711 #if USE_NEW_DELAYED_ACTION
8713 static void HandleElementChange(int x, int y, int page)
8715 int element = MovingOrBlocked2Element(x, y);
8716 struct ElementInfo *ei = &element_info[element];
8717 struct ElementChangeInfo *change = &ei->change_page[page];
8720 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8721 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8724 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8725 x, y, element, element_info[element].token_name);
8726 printf("HandleElementChange(): This should never happen!\n");
8731 /* this can happen with classic bombs on walkable, changing elements */
8732 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8735 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8736 ChangeDelay[x][y] = 0;
8742 if (ChangeDelay[x][y] == 0) /* initialize element change */
8744 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8746 if (change->can_change)
8748 ResetGfxAnimation(x, y);
8749 ResetRandomAnimationValue(x, y);
8751 if (change->pre_change_function)
8752 change->pre_change_function(x, y);
8756 ChangeDelay[x][y]--;
8758 if (ChangeDelay[x][y] != 0) /* continue element change */
8760 if (change->can_change)
8762 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8764 if (IS_ANIMATED(graphic))
8765 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8767 if (change->change_function)
8768 change->change_function(x, y);
8771 else /* finish element change */
8773 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8775 page = ChangePage[x][y];
8776 ChangePage[x][y] = -1;
8778 change = &ei->change_page[page];
8781 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8783 ChangeDelay[x][y] = 1; /* try change after next move step */
8784 ChangePage[x][y] = page; /* remember page to use for change */
8789 if (change->can_change)
8791 if (ChangeElement(x, y, element, page))
8793 if (change->post_change_function)
8794 change->post_change_function(x, y);
8798 if (change->has_action)
8799 ExecuteCustomElementAction(x, y, element, page);
8805 static void HandleElementChange(int x, int y, int page)
8807 int element = MovingOrBlocked2Element(x, y);
8808 struct ElementInfo *ei = &element_info[element];
8809 struct ElementChangeInfo *change = &ei->change_page[page];
8812 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8815 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8816 x, y, element, element_info[element].token_name);
8817 printf("HandleElementChange(): This should never happen!\n");
8822 /* this can happen with classic bombs on walkable, changing elements */
8823 if (!CAN_CHANGE(element))
8826 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8827 ChangeDelay[x][y] = 0;
8833 if (ChangeDelay[x][y] == 0) /* initialize element change */
8835 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8837 ResetGfxAnimation(x, y);
8838 ResetRandomAnimationValue(x, y);
8840 if (change->pre_change_function)
8841 change->pre_change_function(x, y);
8844 ChangeDelay[x][y]--;
8846 if (ChangeDelay[x][y] != 0) /* continue element change */
8848 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8850 if (IS_ANIMATED(graphic))
8851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8853 if (change->change_function)
8854 change->change_function(x, y);
8856 else /* finish element change */
8858 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8860 page = ChangePage[x][y];
8861 ChangePage[x][y] = -1;
8863 change = &ei->change_page[page];
8866 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8868 ChangeDelay[x][y] = 1; /* try change after next move step */
8869 ChangePage[x][y] = page; /* remember page to use for change */
8874 if (ChangeElement(x, y, element, page))
8876 if (change->post_change_function)
8877 change->post_change_function(x, y);
8884 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8885 int trigger_element,
8891 boolean change_done_any = FALSE;
8892 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8895 if (!(trigger_events[trigger_element][trigger_event]))
8898 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8900 int element = EL_CUSTOM_START + i;
8901 boolean change_done = FALSE;
8904 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8905 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8908 for (p = 0; p < element_info[element].num_change_pages; p++)
8910 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8912 if (change->can_change_or_has_action &&
8913 change->has_event[trigger_event] &&
8914 change->trigger_side & trigger_side &&
8915 change->trigger_player & trigger_player &&
8916 change->trigger_page & trigger_page_bits &&
8917 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8919 change->actual_trigger_element = trigger_element;
8920 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8921 change->actual_trigger_side = trigger_side;
8922 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8923 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8925 if ((change->can_change && !change_done) || change->has_action)
8930 SCAN_PLAYFIELD(x, y)
8932 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8935 if (Feld[x][y] == element)
8937 if (change->can_change && !change_done)
8939 ChangeDelay[x][y] = 1;
8940 ChangeEvent[x][y] = trigger_event;
8942 HandleElementChange(x, y, p);
8944 #if USE_NEW_DELAYED_ACTION
8945 else if (change->has_action)
8947 ExecuteCustomElementAction(x, y, element, p);
8948 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8951 if (change->has_action)
8953 ExecuteCustomElementAction(x, y, element, p);
8954 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8960 if (change->can_change)
8963 change_done_any = TRUE;
8970 return change_done_any;
8973 static boolean CheckElementChangeExt(int x, int y,
8975 int trigger_element,
8980 boolean change_done = FALSE;
8983 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8984 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8987 if (Feld[x][y] == EL_BLOCKED)
8989 Blocked2Moving(x, y, &x, &y);
8990 element = Feld[x][y];
8994 /* check if element has already changed */
8995 if (Feld[x][y] != element)
8998 /* check if element has already changed or is about to change after moving */
8999 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9000 Feld[x][y] != element) ||
9002 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9003 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9004 ChangePage[x][y] != -1)))
9008 for (p = 0; p < element_info[element].num_change_pages; p++)
9010 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9012 boolean check_trigger_element =
9013 (trigger_event == CE_TOUCHING_X ||
9014 trigger_event == CE_HITTING_X ||
9015 trigger_event == CE_HIT_BY_X);
9017 if (change->can_change_or_has_action &&
9018 change->has_event[trigger_event] &&
9019 change->trigger_side & trigger_side &&
9020 change->trigger_player & trigger_player &&
9021 (!check_trigger_element ||
9022 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9024 change->actual_trigger_element = trigger_element;
9025 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9026 change->actual_trigger_side = trigger_side;
9027 change->actual_trigger_ce_value = CustomValue[x][y];
9028 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9030 /* special case: trigger element not at (x,y) position for some events */
9031 if (check_trigger_element)
9043 { 0, 0 }, { 0, 0 }, { 0, 0 },
9047 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9048 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9050 change->actual_trigger_ce_value = CustomValue[xx][yy];
9051 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9054 if (change->can_change && !change_done)
9056 ChangeDelay[x][y] = 1;
9057 ChangeEvent[x][y] = trigger_event;
9059 HandleElementChange(x, y, p);
9063 #if USE_NEW_DELAYED_ACTION
9064 else if (change->has_action)
9066 ExecuteCustomElementAction(x, y, element, p);
9067 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9070 if (change->has_action)
9072 ExecuteCustomElementAction(x, y, element, p);
9073 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9082 static void PlayPlayerSound(struct PlayerInfo *player)
9084 int jx = player->jx, jy = player->jy;
9085 int sound_element = player->artwork_element;
9086 int last_action = player->last_action_waiting;
9087 int action = player->action_waiting;
9089 if (player->is_waiting)
9091 if (action != last_action)
9092 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9094 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9098 if (action != last_action)
9099 StopSound(element_info[sound_element].sound[last_action]);
9101 if (last_action == ACTION_SLEEPING)
9102 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9106 static void PlayAllPlayersSound()
9110 for (i = 0; i < MAX_PLAYERS; i++)
9111 if (stored_player[i].active)
9112 PlayPlayerSound(&stored_player[i]);
9115 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9117 boolean last_waiting = player->is_waiting;
9118 int move_dir = player->MovDir;
9120 player->dir_waiting = move_dir;
9121 player->last_action_waiting = player->action_waiting;
9125 if (!last_waiting) /* not waiting -> waiting */
9127 player->is_waiting = TRUE;
9129 player->frame_counter_bored =
9131 game.player_boring_delay_fixed +
9132 SimpleRND(game.player_boring_delay_random);
9133 player->frame_counter_sleeping =
9135 game.player_sleeping_delay_fixed +
9136 SimpleRND(game.player_sleeping_delay_random);
9139 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9141 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9145 if (game.player_sleeping_delay_fixed +
9146 game.player_sleeping_delay_random > 0 &&
9147 player->anim_delay_counter == 0 &&
9148 player->post_delay_counter == 0 &&
9149 FrameCounter >= player->frame_counter_sleeping)
9150 player->is_sleeping = TRUE;
9151 else if (game.player_boring_delay_fixed +
9152 game.player_boring_delay_random > 0 &&
9153 FrameCounter >= player->frame_counter_bored)
9154 player->is_bored = TRUE;
9156 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9157 player->is_bored ? ACTION_BORING :
9161 if (player->is_sleeping && player->use_murphy)
9163 /* special case for sleeping Murphy when leaning against non-free tile */
9165 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9166 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9167 !IS_MOVING(player->jx - 1, player->jy)))
9169 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9170 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9171 !IS_MOVING(player->jx + 1, player->jy)))
9172 move_dir = MV_RIGHT;
9174 player->is_sleeping = FALSE;
9176 player->dir_waiting = move_dir;
9180 if (player->is_sleeping)
9182 if (player->num_special_action_sleeping > 0)
9184 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9186 int last_special_action = player->special_action_sleeping;
9187 int num_special_action = player->num_special_action_sleeping;
9188 int special_action =
9189 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9190 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9191 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9192 last_special_action + 1 : ACTION_SLEEPING);
9193 int special_graphic =
9194 el_act_dir2img(player->artwork_element, special_action, move_dir);
9196 player->anim_delay_counter =
9197 graphic_info[special_graphic].anim_delay_fixed +
9198 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9199 player->post_delay_counter =
9200 graphic_info[special_graphic].post_delay_fixed +
9201 SimpleRND(graphic_info[special_graphic].post_delay_random);
9203 player->special_action_sleeping = special_action;
9206 if (player->anim_delay_counter > 0)
9208 player->action_waiting = player->special_action_sleeping;
9209 player->anim_delay_counter--;
9211 else if (player->post_delay_counter > 0)
9213 player->post_delay_counter--;
9217 else if (player->is_bored)
9219 if (player->num_special_action_bored > 0)
9221 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9223 int special_action =
9224 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9225 int special_graphic =
9226 el_act_dir2img(player->artwork_element, special_action, move_dir);
9228 player->anim_delay_counter =
9229 graphic_info[special_graphic].anim_delay_fixed +
9230 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9231 player->post_delay_counter =
9232 graphic_info[special_graphic].post_delay_fixed +
9233 SimpleRND(graphic_info[special_graphic].post_delay_random);
9235 player->special_action_bored = special_action;
9238 if (player->anim_delay_counter > 0)
9240 player->action_waiting = player->special_action_bored;
9241 player->anim_delay_counter--;
9243 else if (player->post_delay_counter > 0)
9245 player->post_delay_counter--;
9250 else if (last_waiting) /* waiting -> not waiting */
9252 player->is_waiting = FALSE;
9253 player->is_bored = FALSE;
9254 player->is_sleeping = FALSE;
9256 player->frame_counter_bored = -1;
9257 player->frame_counter_sleeping = -1;
9259 player->anim_delay_counter = 0;
9260 player->post_delay_counter = 0;
9262 player->dir_waiting = player->MovDir;
9263 player->action_waiting = ACTION_DEFAULT;
9265 player->special_action_bored = ACTION_DEFAULT;
9266 player->special_action_sleeping = ACTION_DEFAULT;
9270 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9272 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9273 int left = player_action & JOY_LEFT;
9274 int right = player_action & JOY_RIGHT;
9275 int up = player_action & JOY_UP;
9276 int down = player_action & JOY_DOWN;
9277 int button1 = player_action & JOY_BUTTON_1;
9278 int button2 = player_action & JOY_BUTTON_2;
9279 int dx = (left ? -1 : right ? 1 : 0);
9280 int dy = (up ? -1 : down ? 1 : 0);
9282 if (!player->active || tape.pausing)
9288 snapped = SnapField(player, dx, dy);
9292 dropped = DropElement(player);
9294 moved = MovePlayer(player, dx, dy);
9297 if (tape.single_step && tape.recording && !tape.pausing)
9299 if (button1 || (dropped && !moved))
9301 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9302 SnapField(player, 0, 0); /* stop snapping */
9306 SetPlayerWaiting(player, FALSE);
9308 return player_action;
9312 /* no actions for this player (no input at player's configured device) */
9314 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9315 SnapField(player, 0, 0);
9316 CheckGravityMovementWhenNotMoving(player);
9318 if (player->MovPos == 0)
9319 SetPlayerWaiting(player, TRUE);
9321 if (player->MovPos == 0) /* needed for tape.playing */
9322 player->is_moving = FALSE;
9324 player->is_dropping = FALSE;
9325 player->is_dropping_pressed = FALSE;
9326 player->drop_pressed_delay = 0;
9332 static void CheckLevelTime()
9336 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9338 if (level.native_em_level->lev->home == 0) /* all players at home */
9340 local_player->LevelSolved = TRUE;
9341 AllPlayersGone = TRUE;
9343 level.native_em_level->lev->home = -1;
9346 if (level.native_em_level->ply[0]->alive == 0 &&
9347 level.native_em_level->ply[1]->alive == 0 &&
9348 level.native_em_level->ply[2]->alive == 0 &&
9349 level.native_em_level->ply[3]->alive == 0) /* all dead */
9350 AllPlayersGone = TRUE;
9353 if (TimeFrames >= FRAMES_PER_SECOND)
9358 for (i = 0; i < MAX_PLAYERS; i++)
9360 struct PlayerInfo *player = &stored_player[i];
9362 if (SHIELD_ON(player))
9364 player->shield_normal_time_left--;
9366 if (player->shield_deadly_time_left > 0)
9367 player->shield_deadly_time_left--;
9371 if (!level.use_step_counter)
9379 if (TimeLeft <= 10 && setup.time_limit)
9380 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9382 DrawGameValue_Time(TimeLeft);
9384 if (!TimeLeft && setup.time_limit)
9386 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9387 level.native_em_level->lev->killed_out_of_time = TRUE;
9389 for (i = 0; i < MAX_PLAYERS; i++)
9390 KillPlayer(&stored_player[i]);
9393 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9394 DrawGameValue_Time(TimePlayed);
9396 level.native_em_level->lev->time =
9397 (level.time == 0 ? TimePlayed : TimeLeft);
9400 if (tape.recording || tape.playing)
9401 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9405 void AdvanceFrameAndPlayerCounters(int player_nr)
9410 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9411 FrameCounter, FrameCounter + 1);
9414 /* advance frame counters (global frame counter and time frame counter) */
9418 /* advance player counters (counters for move delay, move animation etc.) */
9419 for (i = 0; i < MAX_PLAYERS; i++)
9421 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9422 int move_delay_value = stored_player[i].move_delay_value;
9423 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9425 if (!advance_player_counters) /* not all players may be affected */
9428 #if USE_NEW_PLAYER_ANIM
9429 if (move_frames == 0) /* less than one move per game frame */
9431 int stepsize = TILEX / move_delay_value;
9432 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9433 int count = (stored_player[i].is_moving ?
9434 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9436 if (count % delay == 0)
9441 stored_player[i].Frame += move_frames;
9443 if (stored_player[i].MovPos != 0)
9444 stored_player[i].StepFrame += move_frames;
9446 if (stored_player[i].move_delay > 0)
9447 stored_player[i].move_delay--;
9449 /* due to bugs in previous versions, counter must count up, not down */
9450 if (stored_player[i].push_delay != -1)
9451 stored_player[i].push_delay++;
9453 if (stored_player[i].drop_delay > 0)
9454 stored_player[i].drop_delay--;
9456 if (stored_player[i].is_dropping_pressed)
9457 stored_player[i].drop_pressed_delay++;
9461 void StartGameActions(boolean init_network_game, boolean record_tape,
9464 unsigned long new_random_seed = InitRND(random_seed);
9467 TapeStartRecording(new_random_seed);
9469 #if defined(NETWORK_AVALIABLE)
9470 if (init_network_game)
9472 SendToServer_StartPlaying();
9480 game_status = GAME_MODE_PLAYING;
9487 static unsigned long game_frame_delay = 0;
9488 unsigned long game_frame_delay_value;
9489 byte *recorded_player_action;
9490 byte summarized_player_action = 0;
9491 byte tape_action[MAX_PLAYERS];
9494 if (game.restart_level)
9495 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9497 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9499 if (level.native_em_level->lev->home == 0) /* all players at home */
9501 local_player->LevelSolved = TRUE;
9502 AllPlayersGone = TRUE;
9504 level.native_em_level->lev->home = -1;
9507 if (level.native_em_level->ply[0]->alive == 0 &&
9508 level.native_em_level->ply[1]->alive == 0 &&
9509 level.native_em_level->ply[2]->alive == 0 &&
9510 level.native_em_level->ply[3]->alive == 0) /* all dead */
9511 AllPlayersGone = TRUE;
9514 if (local_player->LevelSolved)
9517 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9520 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9523 game_frame_delay_value =
9524 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9526 if (tape.playing && tape.warp_forward && !tape.pausing)
9527 game_frame_delay_value = 0;
9529 /* ---------- main game synchronization point ---------- */
9531 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9533 if (network_playing && !network_player_action_received)
9535 /* try to get network player actions in time */
9537 #if defined(NETWORK_AVALIABLE)
9538 /* last chance to get network player actions without main loop delay */
9542 /* game was quit by network peer */
9543 if (game_status != GAME_MODE_PLAYING)
9546 if (!network_player_action_received)
9547 return; /* failed to get network player actions in time */
9549 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9555 /* at this point we know that we really continue executing the game */
9558 network_player_action_received = FALSE;
9561 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9563 if (tape.set_centered_player)
9565 game.centered_player_nr_next = tape.centered_player_nr_next;
9566 game.set_centered_player = TRUE;
9569 for (i = 0; i < MAX_PLAYERS; i++)
9571 summarized_player_action |= stored_player[i].action;
9573 if (!network_playing)
9574 stored_player[i].effective_action = stored_player[i].action;
9577 #if defined(NETWORK_AVALIABLE)
9578 if (network_playing)
9579 SendToServer_MovePlayer(summarized_player_action);
9582 if (!options.network && !setup.team_mode)
9583 local_player->effective_action = summarized_player_action;
9585 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9587 for (i = 0; i < MAX_PLAYERS; i++)
9588 stored_player[i].effective_action =
9589 (i == game.centered_player_nr ? summarized_player_action : 0);
9592 if (recorded_player_action != NULL)
9593 for (i = 0; i < MAX_PLAYERS; i++)
9594 stored_player[i].effective_action = recorded_player_action[i];
9596 for (i = 0; i < MAX_PLAYERS; i++)
9598 tape_action[i] = stored_player[i].effective_action;
9600 /* (this can only happen in the R'n'D game engine) */
9601 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9602 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9605 /* only record actions from input devices, but not programmed actions */
9607 TapeRecordAction(tape_action);
9609 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9611 GameActions_EM_Main();
9619 void GameActions_EM_Main()
9621 byte effective_action[MAX_PLAYERS];
9622 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9625 for (i = 0; i < MAX_PLAYERS; i++)
9626 effective_action[i] = stored_player[i].effective_action;
9628 GameActions_EM(effective_action, warp_mode);
9632 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9635 void GameActions_RND()
9637 int magic_wall_x = 0, magic_wall_y = 0;
9638 int i, x, y, element, graphic;
9640 InitPlayfieldScanModeVars();
9642 #if USE_ONE_MORE_CHANGE_PER_FRAME
9643 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9645 SCAN_PLAYFIELD(x, y)
9647 ChangeCount[x][y] = 0;
9648 ChangeEvent[x][y] = -1;
9654 if (game.set_centered_player)
9656 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9658 /* switching to "all players" only possible if all players fit to screen */
9659 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9661 game.centered_player_nr_next = game.centered_player_nr;
9662 game.set_centered_player = FALSE;
9665 /* do not switch focus to non-existing (or non-active) player */
9666 if (game.centered_player_nr_next >= 0 &&
9667 !stored_player[game.centered_player_nr_next].active)
9669 game.centered_player_nr_next = game.centered_player_nr;
9670 game.set_centered_player = FALSE;
9674 if (game.set_centered_player &&
9675 ScreenMovPos == 0) /* screen currently aligned at tile position */
9679 if (game.centered_player_nr_next == -1)
9681 setScreenCenteredToAllPlayers(&sx, &sy);
9685 sx = stored_player[game.centered_player_nr_next].jx;
9686 sy = stored_player[game.centered_player_nr_next].jy;
9689 game.centered_player_nr = game.centered_player_nr_next;
9690 game.set_centered_player = FALSE;
9692 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9693 DrawGameDoorValues();
9697 for (i = 0; i < MAX_PLAYERS; i++)
9699 int actual_player_action = stored_player[i].effective_action;
9702 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9703 - rnd_equinox_tetrachloride 048
9704 - rnd_equinox_tetrachloride_ii 096
9705 - rnd_emanuel_schmieg 002
9706 - doctor_sloan_ww 001, 020
9708 if (stored_player[i].MovPos == 0)
9709 CheckGravityMovement(&stored_player[i]);
9712 /* overwrite programmed action with tape action */
9713 if (stored_player[i].programmed_action)
9714 actual_player_action = stored_player[i].programmed_action;
9717 PlayerActions(&stored_player[i], actual_player_action);
9719 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9721 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9722 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9725 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9729 network_player_action_received = FALSE;
9732 ScrollScreen(NULL, SCROLL_GO_ON);
9734 /* for backwards compatibility, the following code emulates a fixed bug that
9735 occured when pushing elements (causing elements that just made their last
9736 pushing step to already (if possible) make their first falling step in the
9737 same game frame, which is bad); this code is also needed to use the famous
9738 "spring push bug" which is used in older levels and might be wanted to be
9739 used also in newer levels, but in this case the buggy pushing code is only
9740 affecting the "spring" element and no other elements */
9742 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9744 for (i = 0; i < MAX_PLAYERS; i++)
9746 struct PlayerInfo *player = &stored_player[i];
9750 if (player->active && player->is_pushing && player->is_moving &&
9752 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9753 Feld[x][y] == EL_SPRING))
9755 ContinueMoving(x, y);
9757 /* continue moving after pushing (this is actually a bug) */
9758 if (!IS_MOVING(x, y))
9767 SCAN_PLAYFIELD(x, y)
9769 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9772 ChangeCount[x][y] = 0;
9773 ChangeEvent[x][y] = -1;
9775 /* this must be handled before main playfield loop */
9776 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9779 if (MovDelay[x][y] <= 0)
9783 #if USE_NEW_SNAP_DELAY
9784 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9787 if (MovDelay[x][y] <= 0)
9790 DrawLevelField(x, y);
9792 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9798 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9800 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9801 printf("GameActions(): This should never happen!\n");
9803 ChangePage[x][y] = -1;
9808 if (WasJustMoving[x][y] > 0)
9809 WasJustMoving[x][y]--;
9810 if (WasJustFalling[x][y] > 0)
9811 WasJustFalling[x][y]--;
9812 if (CheckCollision[x][y] > 0)
9813 CheckCollision[x][y]--;
9817 /* reset finished pushing action (not done in ContinueMoving() to allow
9818 continuous pushing animation for elements with zero push delay) */
9819 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9821 ResetGfxAnimation(x, y);
9822 DrawLevelField(x, y);
9826 if (IS_BLOCKED(x, y))
9830 Blocked2Moving(x, y, &oldx, &oldy);
9831 if (!IS_MOVING(oldx, oldy))
9833 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9834 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9835 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9836 printf("GameActions(): This should never happen!\n");
9843 SCAN_PLAYFIELD(x, y)
9845 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9848 element = Feld[x][y];
9849 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9852 printf("::: %d,%d\n", x, y);
9854 if (element == EL_ROCK)
9855 printf("::: Yo man! Rocks can fall!\n");
9859 ResetGfxFrame(x, y, TRUE);
9861 if (graphic_info[graphic].anim_global_sync)
9862 GfxFrame[x][y] = FrameCounter;
9863 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9865 int old_gfx_frame = GfxFrame[x][y];
9867 GfxFrame[x][y] = CustomValue[x][y];
9870 if (GfxFrame[x][y] != old_gfx_frame)
9872 DrawLevelGraphicAnimation(x, y, graphic);
9874 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9876 int old_gfx_frame = GfxFrame[x][y];
9878 GfxFrame[x][y] = element_info[element].collect_score;
9881 if (GfxFrame[x][y] != old_gfx_frame)
9883 DrawLevelGraphicAnimation(x, y, graphic);
9885 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9887 int old_gfx_frame = GfxFrame[x][y];
9889 GfxFrame[x][y] = ChangeDelay[x][y];
9892 if (GfxFrame[x][y] != old_gfx_frame)
9894 DrawLevelGraphicAnimation(x, y, graphic);
9898 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9899 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9900 ResetRandomAnimationValue(x, y);
9902 SetRandomAnimationValue(x, y);
9904 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9906 if (IS_INACTIVE(element))
9908 if (IS_ANIMATED(graphic))
9909 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9914 /* this may take place after moving, so 'element' may have changed */
9915 if (IS_CHANGING(x, y) &&
9916 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9918 int page = element_info[element].event_page_nr[CE_DELAY];
9920 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9924 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9928 if (element == EL_CUSTOM_255)
9929 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9933 HandleElementChange(x, y, page);
9935 if (CAN_CHANGE(element))
9936 HandleElementChange(x, y, page);
9938 if (HAS_ACTION(element))
9939 ExecuteCustomElementAction(x, y, element, page);
9944 element = Feld[x][y];
9945 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9948 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9952 element = Feld[x][y];
9953 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9955 if (IS_ANIMATED(graphic) &&
9958 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9960 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9961 EdelsteinFunkeln(x, y);
9963 else if ((element == EL_ACID ||
9964 element == EL_EXIT_OPEN ||
9965 element == EL_SP_EXIT_OPEN ||
9966 element == EL_SP_TERMINAL ||
9967 element == EL_SP_TERMINAL_ACTIVE ||
9968 element == EL_EXTRA_TIME ||
9969 element == EL_SHIELD_NORMAL ||
9970 element == EL_SHIELD_DEADLY) &&
9971 IS_ANIMATED(graphic))
9972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9973 else if (IS_MOVING(x, y))
9974 ContinueMoving(x, y);
9975 else if (IS_ACTIVE_BOMB(element))
9976 CheckDynamite(x, y);
9977 else if (element == EL_AMOEBA_GROWING)
9978 AmoebeWaechst(x, y);
9979 else if (element == EL_AMOEBA_SHRINKING)
9980 AmoebaDisappearing(x, y);
9982 #if !USE_NEW_AMOEBA_CODE
9983 else if (IS_AMOEBALIVE(element))
9984 AmoebeAbleger(x, y);
9987 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9989 else if (element == EL_EXIT_CLOSED)
9991 else if (element == EL_SP_EXIT_CLOSED)
9993 else if (element == EL_EXPANDABLE_WALL_GROWING)
9995 else if (element == EL_EXPANDABLE_WALL ||
9996 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9997 element == EL_EXPANDABLE_WALL_VERTICAL ||
9998 element == EL_EXPANDABLE_WALL_ANY)
10000 else if (element == EL_FLAMES)
10001 CheckForDragon(x, y);
10002 else if (element == EL_EXPLOSION)
10003 ; /* drawing of correct explosion animation is handled separately */
10004 else if (element == EL_ELEMENT_SNAPPING ||
10005 element == EL_DIAGONAL_SHRINKING ||
10006 element == EL_DIAGONAL_GROWING)
10009 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10011 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10014 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10015 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10018 if (element == EL_CUSTOM_255 ||
10019 element == EL_CUSTOM_256)
10020 DrawLevelGraphicAnimation(x, y, graphic);
10023 if (IS_BELT_ACTIVE(element))
10024 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10026 if (game.magic_wall_active)
10028 int jx = local_player->jx, jy = local_player->jy;
10030 /* play the element sound at the position nearest to the player */
10031 if ((element == EL_MAGIC_WALL_FULL ||
10032 element == EL_MAGIC_WALL_ACTIVE ||
10033 element == EL_MAGIC_WALL_EMPTYING ||
10034 element == EL_BD_MAGIC_WALL_FULL ||
10035 element == EL_BD_MAGIC_WALL_ACTIVE ||
10036 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10037 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10045 #if USE_NEW_AMOEBA_CODE
10046 /* new experimental amoeba growth stuff */
10047 if (!(FrameCounter % 8))
10049 static unsigned long random = 1684108901;
10051 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10053 x = RND(lev_fieldx);
10054 y = RND(lev_fieldy);
10055 element = Feld[x][y];
10057 if (!IS_PLAYER(x,y) &&
10058 (element == EL_EMPTY ||
10059 CAN_GROW_INTO(element) ||
10060 element == EL_QUICKSAND_EMPTY ||
10061 element == EL_ACID_SPLASH_LEFT ||
10062 element == EL_ACID_SPLASH_RIGHT))
10064 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10065 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10066 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10067 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10068 Feld[x][y] = EL_AMOEBA_DROP;
10071 random = random * 129 + 1;
10077 if (game.explosions_delayed)
10080 game.explosions_delayed = FALSE;
10083 SCAN_PLAYFIELD(x, y)
10085 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10088 element = Feld[x][y];
10090 if (ExplodeField[x][y])
10091 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10092 else if (element == EL_EXPLOSION)
10093 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10095 ExplodeField[x][y] = EX_TYPE_NONE;
10098 game.explosions_delayed = TRUE;
10101 if (game.magic_wall_active)
10103 if (!(game.magic_wall_time_left % 4))
10105 int element = Feld[magic_wall_x][magic_wall_y];
10107 if (element == EL_BD_MAGIC_WALL_FULL ||
10108 element == EL_BD_MAGIC_WALL_ACTIVE ||
10109 element == EL_BD_MAGIC_WALL_EMPTYING)
10110 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10112 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10115 if (game.magic_wall_time_left > 0)
10117 game.magic_wall_time_left--;
10118 if (!game.magic_wall_time_left)
10121 SCAN_PLAYFIELD(x, y)
10123 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10126 element = Feld[x][y];
10128 if (element == EL_MAGIC_WALL_ACTIVE ||
10129 element == EL_MAGIC_WALL_FULL)
10131 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10132 DrawLevelField(x, y);
10134 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10135 element == EL_BD_MAGIC_WALL_FULL)
10137 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10138 DrawLevelField(x, y);
10142 game.magic_wall_active = FALSE;
10147 if (game.light_time_left > 0)
10149 game.light_time_left--;
10151 if (game.light_time_left == 0)
10152 RedrawAllLightSwitchesAndInvisibleElements();
10155 if (game.timegate_time_left > 0)
10157 game.timegate_time_left--;
10159 if (game.timegate_time_left == 0)
10160 CloseAllOpenTimegates();
10163 if (game.lenses_time_left > 0)
10165 game.lenses_time_left--;
10167 if (game.lenses_time_left == 0)
10168 RedrawAllInvisibleElementsForLenses();
10171 if (game.magnify_time_left > 0)
10173 game.magnify_time_left--;
10175 if (game.magnify_time_left == 0)
10176 RedrawAllInvisibleElementsForMagnifier();
10179 for (i = 0; i < MAX_PLAYERS; i++)
10181 struct PlayerInfo *player = &stored_player[i];
10183 if (SHIELD_ON(player))
10185 if (player->shield_deadly_time_left)
10186 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10187 else if (player->shield_normal_time_left)
10188 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10195 PlayAllPlayersSound();
10197 if (options.debug) /* calculate frames per second */
10199 static unsigned long fps_counter = 0;
10200 static int fps_frames = 0;
10201 unsigned long fps_delay_ms = Counter() - fps_counter;
10205 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10207 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10210 fps_counter = Counter();
10213 redraw_mask |= REDRAW_FPS;
10216 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10218 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10220 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10222 local_player->show_envelope = 0;
10225 /* use random number generator in every frame to make it less predictable */
10226 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10230 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10232 int min_x = x, min_y = y, max_x = x, max_y = y;
10235 for (i = 0; i < MAX_PLAYERS; i++)
10237 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10239 if (!stored_player[i].active || &stored_player[i] == player)
10242 min_x = MIN(min_x, jx);
10243 min_y = MIN(min_y, jy);
10244 max_x = MAX(max_x, jx);
10245 max_y = MAX(max_y, jy);
10248 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10251 static boolean AllPlayersInVisibleScreen()
10255 for (i = 0; i < MAX_PLAYERS; i++)
10257 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10259 if (!stored_player[i].active)
10262 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10269 void ScrollLevel(int dx, int dy)
10271 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10274 BlitBitmap(drawto_field, drawto_field,
10275 FX + TILEX * (dx == -1) - softscroll_offset,
10276 FY + TILEY * (dy == -1) - softscroll_offset,
10277 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10278 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10279 FX + TILEX * (dx == 1) - softscroll_offset,
10280 FY + TILEY * (dy == 1) - softscroll_offset);
10284 x = (dx == 1 ? BX1 : BX2);
10285 for (y = BY1; y <= BY2; y++)
10286 DrawScreenField(x, y);
10291 y = (dy == 1 ? BY1 : BY2);
10292 for (x = BX1; x <= BX2; x++)
10293 DrawScreenField(x, y);
10296 redraw_mask |= REDRAW_FIELD;
10299 static boolean canFallDown(struct PlayerInfo *player)
10301 int jx = player->jx, jy = player->jy;
10303 return (IN_LEV_FIELD(jx, jy + 1) &&
10304 (IS_FREE(jx, jy + 1) ||
10305 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10306 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10307 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10310 static boolean canPassField(int x, int y, int move_dir)
10312 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10313 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10314 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10315 int nextx = x + dx;
10316 int nexty = y + dy;
10317 int element = Feld[x][y];
10319 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10320 !CAN_MOVE(element) &&
10321 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10322 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10323 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10326 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10328 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10329 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10330 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10334 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10335 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10336 (IS_DIGGABLE(Feld[newx][newy]) ||
10337 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10338 canPassField(newx, newy, move_dir)));
10341 static void CheckGravityMovement(struct PlayerInfo *player)
10343 #if USE_PLAYER_GRAVITY
10344 if (player->gravity && !player->programmed_action)
10346 if (game.gravity && !player->programmed_action)
10349 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10350 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10351 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10352 int jx = player->jx, jy = player->jy;
10353 boolean player_is_moving_to_valid_field =
10354 (!player_is_snapping &&
10355 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10356 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10357 boolean player_can_fall_down = canFallDown(player);
10359 if (player_can_fall_down &&
10360 !player_is_moving_to_valid_field)
10361 player->programmed_action = MV_DOWN;
10365 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10367 return CheckGravityMovement(player);
10369 #if USE_PLAYER_GRAVITY
10370 if (player->gravity && !player->programmed_action)
10372 if (game.gravity && !player->programmed_action)
10375 int jx = player->jx, jy = player->jy;
10376 boolean field_under_player_is_free =
10377 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10378 boolean player_is_standing_on_valid_field =
10379 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10380 (IS_WALKABLE(Feld[jx][jy]) &&
10381 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10383 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10384 player->programmed_action = MV_DOWN;
10389 MovePlayerOneStep()
10390 -----------------------------------------------------------------------------
10391 dx, dy: direction (non-diagonal) to try to move the player to
10392 real_dx, real_dy: direction as read from input device (can be diagonal)
10395 boolean MovePlayerOneStep(struct PlayerInfo *player,
10396 int dx, int dy, int real_dx, int real_dy)
10398 int jx = player->jx, jy = player->jy;
10399 int new_jx = jx + dx, new_jy = jy + dy;
10400 #if !USE_FIXED_DONT_RUN_INTO
10404 boolean player_can_move = !player->cannot_move;
10406 if (!player->active || (!dx && !dy))
10407 return MP_NO_ACTION;
10409 player->MovDir = (dx < 0 ? MV_LEFT :
10410 dx > 0 ? MV_RIGHT :
10412 dy > 0 ? MV_DOWN : MV_NONE);
10414 if (!IN_LEV_FIELD(new_jx, new_jy))
10415 return MP_NO_ACTION;
10417 if (!player_can_move)
10420 if (player->MovPos == 0)
10422 player->is_moving = FALSE;
10423 player->is_digging = FALSE;
10424 player->is_collecting = FALSE;
10425 player->is_snapping = FALSE;
10426 player->is_pushing = FALSE;
10429 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10430 SnapField(player, 0, 0);
10434 return MP_NO_ACTION;
10439 if (!options.network && game.centered_player_nr == -1 &&
10440 !AllPlayersInSight(player, new_jx, new_jy))
10441 return MP_NO_ACTION;
10443 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10444 return MP_NO_ACTION;
10447 #if !USE_FIXED_DONT_RUN_INTO
10448 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10450 /* (moved to DigField()) */
10451 if (player_can_move && DONT_RUN_INTO(element))
10453 if (element == EL_ACID && dx == 0 && dy == 1)
10455 SplashAcid(new_jx, new_jy);
10456 Feld[jx][jy] = EL_PLAYER_1;
10457 InitMovingField(jx, jy, MV_DOWN);
10458 Store[jx][jy] = EL_ACID;
10459 ContinueMoving(jx, jy);
10460 BuryPlayer(player);
10463 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10469 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10471 #if USE_FIXED_DONT_RUN_INTO
10472 if (can_move == MP_DONT_RUN_INTO)
10476 if (can_move != MP_MOVING)
10479 #if USE_FIXED_DONT_RUN_INTO
10482 /* check if DigField() has caused relocation of the player */
10483 if (player->jx != jx || player->jy != jy)
10484 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10486 StorePlayer[jx][jy] = 0;
10487 player->last_jx = jx;
10488 player->last_jy = jy;
10489 player->jx = new_jx;
10490 player->jy = new_jy;
10491 StorePlayer[new_jx][new_jy] = player->element_nr;
10493 if (player->move_delay_value_next != -1)
10495 player->move_delay_value = player->move_delay_value_next;
10496 player->move_delay_value_next = -1;
10500 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10502 player->step_counter++;
10504 PlayerVisit[jx][jy] = FrameCounter;
10506 ScrollPlayer(player, SCROLL_INIT);
10511 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10513 int jx = player->jx, jy = player->jy;
10514 int old_jx = jx, old_jy = jy;
10515 int moved = MP_NO_ACTION;
10517 if (!player->active)
10522 if (player->MovPos == 0)
10524 player->is_moving = FALSE;
10525 player->is_digging = FALSE;
10526 player->is_collecting = FALSE;
10527 player->is_snapping = FALSE;
10528 player->is_pushing = FALSE;
10534 if (player->move_delay > 0)
10537 player->move_delay = -1; /* set to "uninitialized" value */
10539 /* store if player is automatically moved to next field */
10540 player->is_auto_moving = (player->programmed_action != MV_NONE);
10542 /* remove the last programmed player action */
10543 player->programmed_action = 0;
10545 if (player->MovPos)
10547 /* should only happen if pre-1.2 tape recordings are played */
10548 /* this is only for backward compatibility */
10550 int original_move_delay_value = player->move_delay_value;
10553 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10557 /* scroll remaining steps with finest movement resolution */
10558 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10560 while (player->MovPos)
10562 ScrollPlayer(player, SCROLL_GO_ON);
10563 ScrollScreen(NULL, SCROLL_GO_ON);
10565 AdvanceFrameAndPlayerCounters(player->index_nr);
10571 player->move_delay_value = original_move_delay_value;
10574 if (player->last_move_dir & MV_HORIZONTAL)
10576 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10577 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10581 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10582 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10589 if (moved & MP_MOVING && !ScreenMovPos &&
10590 (player->index_nr == game.centered_player_nr ||
10591 game.centered_player_nr == -1))
10593 if (moved & MP_MOVING && !ScreenMovPos &&
10594 (player == local_player || !options.network))
10597 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10598 int offset = (setup.scroll_delay ? 3 : 0);
10600 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10602 /* actual player has left the screen -- scroll in that direction */
10603 if (jx != old_jx) /* player has moved horizontally */
10604 scroll_x += (jx - old_jx);
10605 else /* player has moved vertically */
10606 scroll_y += (jy - old_jy);
10610 if (jx != old_jx) /* player has moved horizontally */
10612 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10613 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10614 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10616 /* don't scroll over playfield boundaries */
10617 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10618 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10620 /* don't scroll more than one field at a time */
10621 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10623 /* don't scroll against the player's moving direction */
10624 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10625 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10626 scroll_x = old_scroll_x;
10628 else /* player has moved vertically */
10630 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10631 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10632 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10634 /* don't scroll over playfield boundaries */
10635 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10636 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10638 /* don't scroll more than one field at a time */
10639 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10641 /* don't scroll against the player's moving direction */
10642 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10643 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10644 scroll_y = old_scroll_y;
10648 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10651 if (!options.network && game.centered_player_nr == -1 &&
10652 !AllPlayersInVisibleScreen())
10654 scroll_x = old_scroll_x;
10655 scroll_y = old_scroll_y;
10659 if (!options.network && !AllPlayersInVisibleScreen())
10661 scroll_x = old_scroll_x;
10662 scroll_y = old_scroll_y;
10667 ScrollScreen(player, SCROLL_INIT);
10668 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10673 player->StepFrame = 0;
10675 if (moved & MP_MOVING)
10677 if (old_jx != jx && old_jy == jy)
10678 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10679 else if (old_jx == jx && old_jy != jy)
10680 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10682 DrawLevelField(jx, jy); /* for "crumbled sand" */
10684 player->last_move_dir = player->MovDir;
10685 player->is_moving = TRUE;
10686 player->is_snapping = FALSE;
10687 player->is_switching = FALSE;
10688 player->is_dropping = FALSE;
10689 player->is_dropping_pressed = FALSE;
10690 player->drop_pressed_delay = 0;
10694 CheckGravityMovementWhenNotMoving(player);
10696 player->is_moving = FALSE;
10698 /* at this point, the player is allowed to move, but cannot move right now
10699 (e.g. because of something blocking the way) -- ensure that the player
10700 is also allowed to move in the next frame (in old versions before 3.1.1,
10701 the player was forced to wait again for eight frames before next try) */
10703 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10704 player->move_delay = 0; /* allow direct movement in the next frame */
10707 if (player->move_delay == -1) /* not yet initialized by DigField() */
10708 player->move_delay = player->move_delay_value;
10710 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10712 TestIfPlayerTouchesBadThing(jx, jy);
10713 TestIfPlayerTouchesCustomElement(jx, jy);
10716 if (!player->active)
10717 RemovePlayer(player);
10722 void ScrollPlayer(struct PlayerInfo *player, int mode)
10724 int jx = player->jx, jy = player->jy;
10725 int last_jx = player->last_jx, last_jy = player->last_jy;
10726 int move_stepsize = TILEX / player->move_delay_value;
10728 #if USE_NEW_PLAYER_SPEED
10729 if (!player->active)
10732 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10735 if (!player->active || player->MovPos == 0)
10739 if (mode == SCROLL_INIT)
10741 player->actual_frame_counter = FrameCounter;
10742 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10744 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10745 Feld[last_jx][last_jy] == EL_EMPTY)
10747 int last_field_block_delay = 0; /* start with no blocking at all */
10748 int block_delay_adjustment = player->block_delay_adjustment;
10750 /* if player blocks last field, add delay for exactly one move */
10751 if (player->block_last_field)
10753 last_field_block_delay += player->move_delay_value;
10755 /* when blocking enabled, prevent moving up despite gravity */
10756 #if USE_PLAYER_GRAVITY
10757 if (player->gravity && player->MovDir == MV_UP)
10758 block_delay_adjustment = -1;
10760 if (game.gravity && player->MovDir == MV_UP)
10761 block_delay_adjustment = -1;
10765 /* add block delay adjustment (also possible when not blocking) */
10766 last_field_block_delay += block_delay_adjustment;
10768 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10769 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10772 #if USE_NEW_PLAYER_SPEED
10773 if (player->MovPos != 0) /* player has not yet reached destination */
10779 else if (!FrameReached(&player->actual_frame_counter, 1))
10783 printf("::: player->MovPos: %d -> %d\n",
10785 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10788 #if USE_NEW_PLAYER_SPEED
10789 if (player->MovPos != 0)
10791 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10792 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10794 /* before DrawPlayer() to draw correct player graphic for this case */
10795 if (player->MovPos == 0)
10796 CheckGravityMovement(player);
10799 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10800 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10802 /* before DrawPlayer() to draw correct player graphic for this case */
10803 if (player->MovPos == 0)
10804 CheckGravityMovement(player);
10807 if (player->MovPos == 0) /* player reached destination field */
10810 printf("::: player reached destination field\n");
10813 if (player->move_delay_reset_counter > 0)
10815 player->move_delay_reset_counter--;
10817 if (player->move_delay_reset_counter == 0)
10819 /* continue with normal speed after quickly moving through gate */
10820 HALVE_PLAYER_SPEED(player);
10822 /* be able to make the next move without delay */
10823 player->move_delay = 0;
10827 player->last_jx = jx;
10828 player->last_jy = jy;
10830 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10831 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10832 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10834 DrawPlayer(player); /* needed here only to cleanup last field */
10835 RemovePlayer(player);
10837 if (local_player->friends_still_needed == 0 ||
10838 IS_SP_ELEMENT(Feld[jx][jy]))
10839 player->LevelSolved = player->GameOver = TRUE;
10842 /* this breaks one level: "machine", level 000 */
10844 int move_direction = player->MovDir;
10845 int enter_side = MV_DIR_OPPOSITE(move_direction);
10846 int leave_side = move_direction;
10847 int old_jx = last_jx;
10848 int old_jy = last_jy;
10849 int old_element = Feld[old_jx][old_jy];
10850 int new_element = Feld[jx][jy];
10852 if (IS_CUSTOM_ELEMENT(old_element))
10853 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10855 player->index_bit, leave_side);
10857 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10858 CE_PLAYER_LEAVES_X,
10859 player->index_bit, leave_side);
10861 if (IS_CUSTOM_ELEMENT(new_element))
10862 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10863 player->index_bit, enter_side);
10865 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10866 CE_PLAYER_ENTERS_X,
10867 player->index_bit, enter_side);
10869 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10870 CE_MOVE_OF_X, move_direction);
10873 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10875 TestIfPlayerTouchesBadThing(jx, jy);
10876 TestIfPlayerTouchesCustomElement(jx, jy);
10878 /* needed because pushed element has not yet reached its destination,
10879 so it would trigger a change event at its previous field location */
10880 if (!player->is_pushing)
10881 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10883 if (!player->active)
10884 RemovePlayer(player);
10887 if (level.use_step_counter)
10897 if (TimeLeft <= 10 && setup.time_limit)
10898 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10900 DrawGameValue_Time(TimeLeft);
10902 if (!TimeLeft && setup.time_limit)
10903 for (i = 0; i < MAX_PLAYERS; i++)
10904 KillPlayer(&stored_player[i]);
10906 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10907 DrawGameValue_Time(TimePlayed);
10910 if (tape.single_step && tape.recording && !tape.pausing &&
10911 !player->programmed_action)
10912 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10916 void ScrollScreen(struct PlayerInfo *player, int mode)
10918 static unsigned long screen_frame_counter = 0;
10920 if (mode == SCROLL_INIT)
10922 /* set scrolling step size according to actual player's moving speed */
10923 ScrollStepSize = TILEX / player->move_delay_value;
10925 screen_frame_counter = FrameCounter;
10926 ScreenMovDir = player->MovDir;
10927 ScreenMovPos = player->MovPos;
10928 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10931 else if (!FrameReached(&screen_frame_counter, 1))
10936 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10937 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10938 redraw_mask |= REDRAW_FIELD;
10941 ScreenMovDir = MV_NONE;
10944 void TestIfPlayerTouchesCustomElement(int x, int y)
10946 static int xy[4][2] =
10953 static int trigger_sides[4][2] =
10955 /* center side border side */
10956 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10957 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10958 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10959 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10961 static int touch_dir[4] =
10963 MV_LEFT | MV_RIGHT,
10968 int center_element = Feld[x][y]; /* should always be non-moving! */
10971 for (i = 0; i < NUM_DIRECTIONS; i++)
10973 int xx = x + xy[i][0];
10974 int yy = y + xy[i][1];
10975 int center_side = trigger_sides[i][0];
10976 int border_side = trigger_sides[i][1];
10977 int border_element;
10979 if (!IN_LEV_FIELD(xx, yy))
10982 if (IS_PLAYER(x, y))
10984 struct PlayerInfo *player = PLAYERINFO(x, y);
10986 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10987 border_element = Feld[xx][yy]; /* may be moving! */
10988 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10989 border_element = Feld[xx][yy];
10990 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10991 border_element = MovingOrBlocked2Element(xx, yy);
10993 continue; /* center and border element do not touch */
10995 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10996 player->index_bit, border_side);
10997 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10998 CE_PLAYER_TOUCHES_X,
10999 player->index_bit, border_side);
11001 else if (IS_PLAYER(xx, yy))
11003 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11005 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11007 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11008 continue; /* center and border element do not touch */
11011 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11012 player->index_bit, center_side);
11013 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11014 CE_PLAYER_TOUCHES_X,
11015 player->index_bit, center_side);
11021 #if USE_ELEMENT_TOUCHING_BUGFIX
11023 void TestIfElementTouchesCustomElement(int x, int y)
11025 static int xy[4][2] =
11032 static int trigger_sides[4][2] =
11034 /* center side border side */
11035 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11036 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11037 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11038 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11040 static int touch_dir[4] =
11042 MV_LEFT | MV_RIGHT,
11047 boolean change_center_element = FALSE;
11048 int center_element = Feld[x][y]; /* should always be non-moving! */
11049 int border_element_old[NUM_DIRECTIONS];
11052 for (i = 0; i < NUM_DIRECTIONS; i++)
11054 int xx = x + xy[i][0];
11055 int yy = y + xy[i][1];
11056 int border_element;
11058 border_element_old[i] = -1;
11060 if (!IN_LEV_FIELD(xx, yy))
11063 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11064 border_element = Feld[xx][yy]; /* may be moving! */
11065 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11066 border_element = Feld[xx][yy];
11067 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11068 border_element = MovingOrBlocked2Element(xx, yy);
11070 continue; /* center and border element do not touch */
11072 border_element_old[i] = border_element;
11075 for (i = 0; i < NUM_DIRECTIONS; i++)
11077 int xx = x + xy[i][0];
11078 int yy = y + xy[i][1];
11079 int center_side = trigger_sides[i][0];
11080 int border_element = border_element_old[i];
11082 if (border_element == -1)
11085 /* check for change of border element */
11086 CheckElementChangeBySide(xx, yy, border_element, center_element,
11087 CE_TOUCHING_X, center_side);
11090 for (i = 0; i < NUM_DIRECTIONS; i++)
11092 int border_side = trigger_sides[i][1];
11093 int border_element = border_element_old[i];
11095 if (border_element == -1)
11098 /* check for change of center element (but change it only once) */
11099 if (!change_center_element)
11100 change_center_element =
11101 CheckElementChangeBySide(x, y, center_element, border_element,
11102 CE_TOUCHING_X, border_side);
11108 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11110 static int xy[4][2] =
11117 static int trigger_sides[4][2] =
11119 /* center side border side */
11120 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11121 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11122 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11123 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11125 static int touch_dir[4] =
11127 MV_LEFT | MV_RIGHT,
11132 boolean change_center_element = FALSE;
11133 int center_element = Feld[x][y]; /* should always be non-moving! */
11136 for (i = 0; i < NUM_DIRECTIONS; i++)
11138 int xx = x + xy[i][0];
11139 int yy = y + xy[i][1];
11140 int center_side = trigger_sides[i][0];
11141 int border_side = trigger_sides[i][1];
11142 int border_element;
11144 if (!IN_LEV_FIELD(xx, yy))
11147 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11148 border_element = Feld[xx][yy]; /* may be moving! */
11149 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11150 border_element = Feld[xx][yy];
11151 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11152 border_element = MovingOrBlocked2Element(xx, yy);
11154 continue; /* center and border element do not touch */
11156 /* check for change of center element (but change it only once) */
11157 if (!change_center_element)
11158 change_center_element =
11159 CheckElementChangeBySide(x, y, center_element, border_element,
11160 CE_TOUCHING_X, border_side);
11162 /* check for change of border element */
11163 CheckElementChangeBySide(xx, yy, border_element, center_element,
11164 CE_TOUCHING_X, center_side);
11170 void TestIfElementHitsCustomElement(int x, int y, int direction)
11172 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11173 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11174 int hitx = x + dx, hity = y + dy;
11175 int hitting_element = Feld[x][y];
11176 int touched_element;
11178 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11181 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11182 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11184 if (IN_LEV_FIELD(hitx, hity))
11186 int opposite_direction = MV_DIR_OPPOSITE(direction);
11187 int hitting_side = direction;
11188 int touched_side = opposite_direction;
11189 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11190 MovDir[hitx][hity] != direction ||
11191 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11197 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11198 CE_HITTING_X, touched_side);
11200 CheckElementChangeBySide(hitx, hity, touched_element,
11201 hitting_element, CE_HIT_BY_X, hitting_side);
11203 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11204 CE_HIT_BY_SOMETHING, opposite_direction);
11208 /* "hitting something" is also true when hitting the playfield border */
11209 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11210 CE_HITTING_SOMETHING, direction);
11214 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11216 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11217 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11218 int hitx = x + dx, hity = y + dy;
11219 int hitting_element = Feld[x][y];
11220 int touched_element;
11222 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11223 !IS_FREE(hitx, hity) &&
11224 (!IS_MOVING(hitx, hity) ||
11225 MovDir[hitx][hity] != direction ||
11226 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11229 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11233 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11237 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11238 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11240 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11241 EP_CAN_SMASH_EVERYTHING, direction);
11243 if (IN_LEV_FIELD(hitx, hity))
11245 int opposite_direction = MV_DIR_OPPOSITE(direction);
11246 int hitting_side = direction;
11247 int touched_side = opposite_direction;
11249 int touched_element = MovingOrBlocked2Element(hitx, hity);
11252 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11253 MovDir[hitx][hity] != direction ||
11254 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11263 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11264 CE_SMASHED_BY_SOMETHING, opposite_direction);
11266 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11267 CE_OTHER_IS_SMASHING, touched_side);
11269 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11270 CE_OTHER_GETS_SMASHED, hitting_side);
11276 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11278 int i, kill_x = -1, kill_y = -1;
11280 int bad_element = -1;
11281 static int test_xy[4][2] =
11288 static int test_dir[4] =
11296 for (i = 0; i < NUM_DIRECTIONS; i++)
11298 int test_x, test_y, test_move_dir, test_element;
11300 test_x = good_x + test_xy[i][0];
11301 test_y = good_y + test_xy[i][1];
11303 if (!IN_LEV_FIELD(test_x, test_y))
11307 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11309 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11311 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11312 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11314 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11315 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11319 bad_element = test_element;
11325 if (kill_x != -1 || kill_y != -1)
11327 if (IS_PLAYER(good_x, good_y))
11329 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11331 if (player->shield_deadly_time_left > 0 &&
11332 !IS_INDESTRUCTIBLE(bad_element))
11333 Bang(kill_x, kill_y);
11334 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11335 KillPlayer(player);
11338 Bang(good_x, good_y);
11342 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11344 int i, kill_x = -1, kill_y = -1;
11345 int bad_element = Feld[bad_x][bad_y];
11346 static int test_xy[4][2] =
11353 static int touch_dir[4] =
11355 MV_LEFT | MV_RIGHT,
11360 static int test_dir[4] =
11368 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11371 for (i = 0; i < NUM_DIRECTIONS; i++)
11373 int test_x, test_y, test_move_dir, test_element;
11375 test_x = bad_x + test_xy[i][0];
11376 test_y = bad_y + test_xy[i][1];
11377 if (!IN_LEV_FIELD(test_x, test_y))
11381 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11383 test_element = Feld[test_x][test_y];
11385 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11386 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11388 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11389 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11391 /* good thing is player or penguin that does not move away */
11392 if (IS_PLAYER(test_x, test_y))
11394 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11396 if (bad_element == EL_ROBOT && player->is_moving)
11397 continue; /* robot does not kill player if he is moving */
11399 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11401 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11402 continue; /* center and border element do not touch */
11409 else if (test_element == EL_PENGUIN)
11418 if (kill_x != -1 || kill_y != -1)
11420 if (IS_PLAYER(kill_x, kill_y))
11422 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11424 if (player->shield_deadly_time_left > 0 &&
11425 !IS_INDESTRUCTIBLE(bad_element))
11426 Bang(bad_x, bad_y);
11427 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11428 KillPlayer(player);
11431 Bang(kill_x, kill_y);
11435 void TestIfPlayerTouchesBadThing(int x, int y)
11437 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11440 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11442 TestIfGoodThingHitsBadThing(x, y, move_dir);
11445 void TestIfBadThingTouchesPlayer(int x, int y)
11447 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11450 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11452 TestIfBadThingHitsGoodThing(x, y, move_dir);
11455 void TestIfFriendTouchesBadThing(int x, int y)
11457 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11460 void TestIfBadThingTouchesFriend(int x, int y)
11462 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11465 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11467 int i, kill_x = bad_x, kill_y = bad_y;
11468 static int xy[4][2] =
11476 for (i = 0; i < NUM_DIRECTIONS; i++)
11480 x = bad_x + xy[i][0];
11481 y = bad_y + xy[i][1];
11482 if (!IN_LEV_FIELD(x, y))
11485 element = Feld[x][y];
11486 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11487 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11495 if (kill_x != bad_x || kill_y != bad_y)
11496 Bang(bad_x, bad_y);
11499 void KillPlayer(struct PlayerInfo *player)
11501 int jx = player->jx, jy = player->jy;
11503 if (!player->active)
11506 /* remove accessible field at the player's position */
11507 Feld[jx][jy] = EL_EMPTY;
11509 /* deactivate shield (else Bang()/Explode() would not work right) */
11510 player->shield_normal_time_left = 0;
11511 player->shield_deadly_time_left = 0;
11514 BuryPlayer(player);
11517 static void KillPlayerUnlessEnemyProtected(int x, int y)
11519 if (!PLAYER_ENEMY_PROTECTED(x, y))
11520 KillPlayer(PLAYERINFO(x, y));
11523 static void KillPlayerUnlessExplosionProtected(int x, int y)
11525 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11526 KillPlayer(PLAYERINFO(x, y));
11529 void BuryPlayer(struct PlayerInfo *player)
11531 int jx = player->jx, jy = player->jy;
11533 if (!player->active)
11536 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11537 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11539 player->GameOver = TRUE;
11540 RemovePlayer(player);
11543 void RemovePlayer(struct PlayerInfo *player)
11545 int jx = player->jx, jy = player->jy;
11546 int i, found = FALSE;
11548 player->present = FALSE;
11549 player->active = FALSE;
11551 if (!ExplodeField[jx][jy])
11552 StorePlayer[jx][jy] = 0;
11554 if (player->is_moving)
11555 DrawLevelField(player->last_jx, player->last_jy);
11557 for (i = 0; i < MAX_PLAYERS; i++)
11558 if (stored_player[i].active)
11562 AllPlayersGone = TRUE;
11568 #if USE_NEW_SNAP_DELAY
11569 static void setFieldForSnapping(int x, int y, int element, int direction)
11571 struct ElementInfo *ei = &element_info[element];
11572 int direction_bit = MV_DIR_TO_BIT(direction);
11573 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11574 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11575 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11577 Feld[x][y] = EL_ELEMENT_SNAPPING;
11578 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11580 ResetGfxAnimation(x, y);
11582 GfxElement[x][y] = element;
11583 GfxAction[x][y] = action;
11584 GfxDir[x][y] = direction;
11585 GfxFrame[x][y] = -1;
11590 =============================================================================
11591 checkDiagonalPushing()
11592 -----------------------------------------------------------------------------
11593 check if diagonal input device direction results in pushing of object
11594 (by checking if the alternative direction is walkable, diggable, ...)
11595 =============================================================================
11598 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11599 int x, int y, int real_dx, int real_dy)
11601 int jx, jy, dx, dy, xx, yy;
11603 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11606 /* diagonal direction: check alternative direction */
11611 xx = jx + (dx == 0 ? real_dx : 0);
11612 yy = jy + (dy == 0 ? real_dy : 0);
11614 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11618 =============================================================================
11620 -----------------------------------------------------------------------------
11621 x, y: field next to player (non-diagonal) to try to dig to
11622 real_dx, real_dy: direction as read from input device (can be diagonal)
11623 =============================================================================
11626 int DigField(struct PlayerInfo *player,
11627 int oldx, int oldy, int x, int y,
11628 int real_dx, int real_dy, int mode)
11630 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11631 boolean player_was_pushing = player->is_pushing;
11632 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11633 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11634 int jx = oldx, jy = oldy;
11635 int dx = x - jx, dy = y - jy;
11636 int nextx = x + dx, nexty = y + dy;
11637 int move_direction = (dx == -1 ? MV_LEFT :
11638 dx == +1 ? MV_RIGHT :
11640 dy == +1 ? MV_DOWN : MV_NONE);
11641 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11642 int dig_side = MV_DIR_OPPOSITE(move_direction);
11643 int old_element = Feld[jx][jy];
11644 #if USE_FIXED_DONT_RUN_INTO
11645 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11651 if (is_player) /* function can also be called by EL_PENGUIN */
11653 if (player->MovPos == 0)
11655 player->is_digging = FALSE;
11656 player->is_collecting = FALSE;
11659 if (player->MovPos == 0) /* last pushing move finished */
11660 player->is_pushing = FALSE;
11662 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11664 player->is_switching = FALSE;
11665 player->push_delay = -1;
11667 return MP_NO_ACTION;
11671 #if !USE_FIXED_DONT_RUN_INTO
11672 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11673 return MP_NO_ACTION;
11676 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11677 old_element = Back[jx][jy];
11679 /* in case of element dropped at player position, check background */
11680 else if (Back[jx][jy] != EL_EMPTY &&
11681 game.engine_version >= VERSION_IDENT(2,2,0,0))
11682 old_element = Back[jx][jy];
11685 #if USE_FIXED_DONT_RUN_INTO
11686 if (player_can_move && DONT_RUN_INTO(element))
11688 if (element == EL_ACID && dx == 0 && dy == 1)
11691 Feld[jx][jy] = EL_PLAYER_1;
11692 InitMovingField(jx, jy, MV_DOWN);
11693 Store[jx][jy] = EL_ACID;
11694 ContinueMoving(jx, jy);
11695 BuryPlayer(player);
11698 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11700 return MP_DONT_RUN_INTO;
11706 #if USE_FIXED_DONT_RUN_INTO
11707 if (player_can_move && DONT_RUN_INTO(element))
11709 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11711 return MP_DONT_RUN_INTO;
11716 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11717 return MP_NO_ACTION; /* field has no opening in this direction */
11719 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11720 return MP_NO_ACTION; /* field has no opening in this direction */
11723 #if USE_FIXED_DONT_RUN_INTO
11724 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11727 Feld[jx][jy] = EL_PLAYER_1;
11728 InitMovingField(jx, jy, MV_DOWN);
11729 Store[jx][jy] = EL_ACID;
11730 ContinueMoving(jx, jy);
11731 BuryPlayer(player);
11733 return MP_DONT_RUN_INTO;
11739 #if USE_FIXED_DONT_RUN_INTO
11740 if (player_can_move && DONT_RUN_INTO(element))
11742 if (element == EL_ACID && dx == 0 && dy == 1)
11745 Feld[jx][jy] = EL_PLAYER_1;
11746 InitMovingField(jx, jy, MV_DOWN);
11747 Store[jx][jy] = EL_ACID;
11748 ContinueMoving(jx, jy);
11749 BuryPlayer(player);
11752 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11754 return MP_DONT_RUN_INTO;
11759 #if USE_FIXED_DONT_RUN_INTO
11760 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11761 return MP_NO_ACTION;
11764 #if !USE_FIXED_DONT_RUN_INTO
11765 element = Feld[x][y];
11768 collect_count = element_info[element].collect_count_initial;
11770 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11771 return MP_NO_ACTION;
11773 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11774 player_can_move = player_can_move_or_snap;
11776 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11777 game.engine_version >= VERSION_IDENT(2,2,0,0))
11779 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11780 player->index_bit, dig_side);
11781 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11782 player->index_bit, dig_side);
11784 if (Feld[x][y] != element) /* field changed by snapping */
11787 return MP_NO_ACTION;
11790 #if USE_PLAYER_GRAVITY
11791 if (player->gravity && is_player && !player->is_auto_moving &&
11792 canFallDown(player) && move_direction != MV_DOWN &&
11793 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11794 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11796 if (game.gravity && is_player && !player->is_auto_moving &&
11797 canFallDown(player) && move_direction != MV_DOWN &&
11798 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11799 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11802 if (player_can_move &&
11803 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11805 int sound_element = SND_ELEMENT(element);
11806 int sound_action = ACTION_WALKING;
11808 if (IS_RND_GATE(element))
11810 if (!player->key[RND_GATE_NR(element)])
11811 return MP_NO_ACTION;
11813 else if (IS_RND_GATE_GRAY(element))
11815 if (!player->key[RND_GATE_GRAY_NR(element)])
11816 return MP_NO_ACTION;
11818 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11820 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11821 return MP_NO_ACTION;
11823 else if (element == EL_EXIT_OPEN ||
11824 element == EL_SP_EXIT_OPEN ||
11825 element == EL_SP_EXIT_OPENING)
11827 sound_action = ACTION_PASSING; /* player is passing exit */
11829 else if (element == EL_EMPTY)
11831 sound_action = ACTION_MOVING; /* nothing to walk on */
11834 /* play sound from background or player, whatever is available */
11835 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11836 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11838 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11840 else if (player_can_move &&
11841 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11843 if (!ACCESS_FROM(element, opposite_direction))
11844 return MP_NO_ACTION; /* field not accessible from this direction */
11846 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11847 return MP_NO_ACTION;
11849 if (IS_EM_GATE(element))
11851 if (!player->key[EM_GATE_NR(element)])
11852 return MP_NO_ACTION;
11854 else if (IS_EM_GATE_GRAY(element))
11856 if (!player->key[EM_GATE_GRAY_NR(element)])
11857 return MP_NO_ACTION;
11859 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11861 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11862 return MP_NO_ACTION;
11864 else if (IS_SP_PORT(element))
11866 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11867 element == EL_SP_GRAVITY_PORT_RIGHT ||
11868 element == EL_SP_GRAVITY_PORT_UP ||
11869 element == EL_SP_GRAVITY_PORT_DOWN)
11870 #if USE_PLAYER_GRAVITY
11871 player->gravity = !player->gravity;
11873 game.gravity = !game.gravity;
11875 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11876 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11877 element == EL_SP_GRAVITY_ON_PORT_UP ||
11878 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11879 #if USE_PLAYER_GRAVITY
11880 player->gravity = TRUE;
11882 game.gravity = TRUE;
11884 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11885 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11886 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11887 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11888 #if USE_PLAYER_GRAVITY
11889 player->gravity = FALSE;
11891 game.gravity = FALSE;
11895 /* automatically move to the next field with double speed */
11896 player->programmed_action = move_direction;
11898 if (player->move_delay_reset_counter == 0)
11900 player->move_delay_reset_counter = 2; /* two double speed steps */
11902 DOUBLE_PLAYER_SPEED(player);
11905 PlayLevelSoundAction(x, y, ACTION_PASSING);
11907 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11911 if (mode != DF_SNAP)
11913 GfxElement[x][y] = GFX_ELEMENT(element);
11914 player->is_digging = TRUE;
11917 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11919 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11920 player->index_bit, dig_side);
11922 if (mode == DF_SNAP)
11924 #if USE_NEW_SNAP_DELAY
11925 if (level.block_snap_field)
11926 setFieldForSnapping(x, y, element, move_direction);
11928 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11930 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11933 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11934 player->index_bit, dig_side);
11937 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11941 if (is_player && mode != DF_SNAP)
11943 GfxElement[x][y] = element;
11944 player->is_collecting = TRUE;
11947 if (element == EL_SPEED_PILL)
11949 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11951 else if (element == EL_EXTRA_TIME && level.time > 0)
11953 TimeLeft += level.extra_time;
11954 DrawGameValue_Time(TimeLeft);
11956 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11958 player->shield_normal_time_left += level.shield_normal_time;
11959 if (element == EL_SHIELD_DEADLY)
11960 player->shield_deadly_time_left += level.shield_deadly_time;
11962 else if (element == EL_DYNAMITE ||
11963 element == EL_EM_DYNAMITE ||
11964 element == EL_SP_DISK_RED)
11966 if (player->inventory_size < MAX_INVENTORY_SIZE)
11967 player->inventory_element[player->inventory_size++] = element;
11970 DrawGameDoorValues();
11972 DrawGameValue_Dynamite(local_player->inventory_size);
11975 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11977 player->dynabomb_count++;
11978 player->dynabombs_left++;
11980 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11982 player->dynabomb_size++;
11984 else if (element == EL_DYNABOMB_INCREASE_POWER)
11986 player->dynabomb_xl = TRUE;
11988 else if (IS_KEY(element))
11990 player->key[KEY_NR(element)] = TRUE;
11993 DrawGameDoorValues();
11995 DrawGameValue_Keys(player->key);
11998 redraw_mask |= REDRAW_DOOR_1;
12000 else if (IS_ENVELOPE(element))
12002 player->show_envelope = element;
12004 else if (element == EL_EMC_LENSES)
12006 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12008 RedrawAllInvisibleElementsForLenses();
12010 else if (element == EL_EMC_MAGNIFIER)
12012 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12014 RedrawAllInvisibleElementsForMagnifier();
12016 else if (IS_DROPPABLE(element) ||
12017 IS_THROWABLE(element)) /* can be collected and dropped */
12021 if (collect_count == 0)
12022 player->inventory_infinite_element = element;
12024 for (i = 0; i < collect_count; i++)
12025 if (player->inventory_size < MAX_INVENTORY_SIZE)
12026 player->inventory_element[player->inventory_size++] = element;
12029 DrawGameDoorValues();
12031 DrawGameValue_Dynamite(local_player->inventory_size);
12034 else if (collect_count > 0)
12036 local_player->gems_still_needed -= collect_count;
12037 if (local_player->gems_still_needed < 0)
12038 local_player->gems_still_needed = 0;
12040 DrawGameValue_Emeralds(local_player->gems_still_needed);
12043 RaiseScoreElement(element);
12044 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12047 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12048 player->index_bit, dig_side);
12050 if (mode == DF_SNAP)
12052 #if USE_NEW_SNAP_DELAY
12053 if (level.block_snap_field)
12054 setFieldForSnapping(x, y, element, move_direction);
12056 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12058 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12061 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12062 player->index_bit, dig_side);
12065 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12067 if (mode == DF_SNAP && element != EL_BD_ROCK)
12068 return MP_NO_ACTION;
12070 if (CAN_FALL(element) && dy)
12071 return MP_NO_ACTION;
12073 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12074 !(element == EL_SPRING && level.use_spring_bug))
12075 return MP_NO_ACTION;
12077 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12078 ((move_direction & MV_VERTICAL &&
12079 ((element_info[element].move_pattern & MV_LEFT &&
12080 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12081 (element_info[element].move_pattern & MV_RIGHT &&
12082 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12083 (move_direction & MV_HORIZONTAL &&
12084 ((element_info[element].move_pattern & MV_UP &&
12085 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12086 (element_info[element].move_pattern & MV_DOWN &&
12087 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12088 return MP_NO_ACTION;
12090 /* do not push elements already moving away faster than player */
12091 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12092 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12093 return MP_NO_ACTION;
12095 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12097 if (player->push_delay_value == -1 || !player_was_pushing)
12098 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12100 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12102 if (player->push_delay_value == -1)
12103 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12105 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12107 if (!player->is_pushing)
12108 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12111 player->is_pushing = TRUE;
12113 if (!(IN_LEV_FIELD(nextx, nexty) &&
12114 (IS_FREE(nextx, nexty) ||
12115 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12116 IS_SB_ELEMENT(element)))))
12117 return MP_NO_ACTION;
12119 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12120 return MP_NO_ACTION;
12122 if (player->push_delay == -1) /* new pushing; restart delay */
12123 player->push_delay = 0;
12125 if (player->push_delay < player->push_delay_value &&
12126 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12127 element != EL_SPRING && element != EL_BALLOON)
12129 /* make sure that there is no move delay before next try to push */
12130 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12131 player->move_delay = 0;
12133 return MP_NO_ACTION;
12136 if (IS_SB_ELEMENT(element))
12138 if (element == EL_SOKOBAN_FIELD_FULL)
12140 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12141 local_player->sokobanfields_still_needed++;
12144 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12146 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12147 local_player->sokobanfields_still_needed--;
12150 Feld[x][y] = EL_SOKOBAN_OBJECT;
12152 if (Back[x][y] == Back[nextx][nexty])
12153 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12154 else if (Back[x][y] != 0)
12155 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12158 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12161 if (local_player->sokobanfields_still_needed == 0 &&
12162 game.emulation == EMU_SOKOBAN)
12164 player->LevelSolved = player->GameOver = TRUE;
12165 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12169 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12171 InitMovingField(x, y, move_direction);
12172 GfxAction[x][y] = ACTION_PUSHING;
12174 if (mode == DF_SNAP)
12175 ContinueMoving(x, y);
12177 MovPos[x][y] = (dx != 0 ? dx : dy);
12179 Pushed[x][y] = TRUE;
12180 Pushed[nextx][nexty] = TRUE;
12182 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12183 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12185 player->push_delay_value = -1; /* get new value later */
12187 /* check for element change _after_ element has been pushed */
12188 if (game.use_change_when_pushing_bug)
12190 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12191 player->index_bit, dig_side);
12192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12193 player->index_bit, dig_side);
12196 else if (IS_SWITCHABLE(element))
12198 if (PLAYER_SWITCHING(player, x, y))
12200 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12201 player->index_bit, dig_side);
12206 player->is_switching = TRUE;
12207 player->switch_x = x;
12208 player->switch_y = y;
12210 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12212 if (element == EL_ROBOT_WHEEL)
12214 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12218 DrawLevelField(x, y);
12220 else if (element == EL_SP_TERMINAL)
12225 SCAN_PLAYFIELD(xx, yy)
12227 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12230 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12232 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12233 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12236 else if (IS_BELT_SWITCH(element))
12238 ToggleBeltSwitch(x, y);
12240 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12241 element == EL_SWITCHGATE_SWITCH_DOWN)
12243 ToggleSwitchgateSwitch(x, y);
12245 else if (element == EL_LIGHT_SWITCH ||
12246 element == EL_LIGHT_SWITCH_ACTIVE)
12248 ToggleLightSwitch(x, y);
12250 else if (element == EL_TIMEGATE_SWITCH)
12252 ActivateTimegateSwitch(x, y);
12254 else if (element == EL_BALLOON_SWITCH_LEFT ||
12255 element == EL_BALLOON_SWITCH_RIGHT ||
12256 element == EL_BALLOON_SWITCH_UP ||
12257 element == EL_BALLOON_SWITCH_DOWN ||
12258 element == EL_BALLOON_SWITCH_NONE ||
12259 element == EL_BALLOON_SWITCH_ANY)
12261 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12262 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12263 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12264 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12265 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12268 else if (element == EL_LAMP)
12270 Feld[x][y] = EL_LAMP_ACTIVE;
12271 local_player->lights_still_needed--;
12273 ResetGfxAnimation(x, y);
12274 DrawLevelField(x, y);
12276 else if (element == EL_TIME_ORB_FULL)
12278 Feld[x][y] = EL_TIME_ORB_EMPTY;
12280 if (level.time > 0 || level.use_time_orb_bug)
12282 TimeLeft += level.time_orb_time;
12283 DrawGameValue_Time(TimeLeft);
12286 ResetGfxAnimation(x, y);
12287 DrawLevelField(x, y);
12289 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12290 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12294 game.ball_state = !game.ball_state;
12297 SCAN_PLAYFIELD(xx, yy)
12299 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12302 int e = Feld[xx][yy];
12304 if (game.ball_state)
12306 if (e == EL_EMC_MAGIC_BALL)
12307 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12308 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12309 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12313 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12314 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12315 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12316 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12321 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12322 player->index_bit, dig_side);
12324 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12325 player->index_bit, dig_side);
12327 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12328 player->index_bit, dig_side);
12334 if (!PLAYER_SWITCHING(player, x, y))
12336 player->is_switching = TRUE;
12337 player->switch_x = x;
12338 player->switch_y = y;
12340 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12341 player->index_bit, dig_side);
12342 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12343 player->index_bit, dig_side);
12345 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12346 player->index_bit, dig_side);
12347 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12348 player->index_bit, dig_side);
12351 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12352 player->index_bit, dig_side);
12353 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12354 player->index_bit, dig_side);
12356 return MP_NO_ACTION;
12359 player->push_delay = -1;
12361 if (is_player) /* function can also be called by EL_PENGUIN */
12363 if (Feld[x][y] != element) /* really digged/collected something */
12364 player->is_collecting = !player->is_digging;
12370 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12372 int jx = player->jx, jy = player->jy;
12373 int x = jx + dx, y = jy + dy;
12374 int snap_direction = (dx == -1 ? MV_LEFT :
12375 dx == +1 ? MV_RIGHT :
12377 dy == +1 ? MV_DOWN : MV_NONE);
12378 boolean can_continue_snapping = (level.continuous_snapping &&
12379 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12381 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12384 if (!player->active || !IN_LEV_FIELD(x, y))
12392 if (player->MovPos == 0)
12393 player->is_pushing = FALSE;
12395 player->is_snapping = FALSE;
12397 if (player->MovPos == 0)
12399 player->is_moving = FALSE;
12400 player->is_digging = FALSE;
12401 player->is_collecting = FALSE;
12407 #if USE_NEW_CONTINUOUS_SNAPPING
12408 /* prevent snapping with already pressed snap key when not allowed */
12409 if (player->is_snapping && !can_continue_snapping)
12412 if (player->is_snapping)
12416 player->MovDir = snap_direction;
12418 if (player->MovPos == 0)
12420 player->is_moving = FALSE;
12421 player->is_digging = FALSE;
12422 player->is_collecting = FALSE;
12425 player->is_dropping = FALSE;
12426 player->is_dropping_pressed = FALSE;
12427 player->drop_pressed_delay = 0;
12429 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12432 player->is_snapping = TRUE;
12434 if (player->MovPos == 0)
12436 player->is_moving = FALSE;
12437 player->is_digging = FALSE;
12438 player->is_collecting = FALSE;
12441 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12442 DrawLevelField(player->last_jx, player->last_jy);
12444 DrawLevelField(x, y);
12449 boolean DropElement(struct PlayerInfo *player)
12451 int old_element, new_element;
12452 int dropx = player->jx, dropy = player->jy;
12453 int drop_direction = player->MovDir;
12454 int drop_side = drop_direction;
12455 int drop_element = (player->inventory_size > 0 ?
12456 player->inventory_element[player->inventory_size - 1] :
12457 player->inventory_infinite_element != EL_UNDEFINED ?
12458 player->inventory_infinite_element :
12459 player->dynabombs_left > 0 ?
12460 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12463 player->is_dropping_pressed = TRUE;
12465 /* do not drop an element on top of another element; when holding drop key
12466 pressed without moving, dropped element must move away before the next
12467 element can be dropped (this is especially important if the next element
12468 is dynamite, which can be placed on background for historical reasons) */
12469 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12472 if (IS_THROWABLE(drop_element))
12474 dropx += GET_DX_FROM_DIR(drop_direction);
12475 dropy += GET_DY_FROM_DIR(drop_direction);
12477 if (!IN_LEV_FIELD(dropx, dropy))
12481 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12482 new_element = drop_element; /* default: no change when dropping */
12484 /* check if player is active, not moving and ready to drop */
12485 if (!player->active || player->MovPos || player->drop_delay > 0)
12488 /* check if player has anything that can be dropped */
12489 if (new_element == EL_UNDEFINED)
12492 /* check if drop key was pressed long enough for EM style dynamite */
12493 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12496 /* check if anything can be dropped at the current position */
12497 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12500 /* collected custom elements can only be dropped on empty fields */
12501 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12504 if (old_element != EL_EMPTY)
12505 Back[dropx][dropy] = old_element; /* store old element on this field */
12507 ResetGfxAnimation(dropx, dropy);
12508 ResetRandomAnimationValue(dropx, dropy);
12510 if (player->inventory_size > 0 ||
12511 player->inventory_infinite_element != EL_UNDEFINED)
12513 if (player->inventory_size > 0)
12515 player->inventory_size--;
12518 DrawGameDoorValues();
12520 DrawGameValue_Dynamite(local_player->inventory_size);
12523 if (new_element == EL_DYNAMITE)
12524 new_element = EL_DYNAMITE_ACTIVE;
12525 else if (new_element == EL_EM_DYNAMITE)
12526 new_element = EL_EM_DYNAMITE_ACTIVE;
12527 else if (new_element == EL_SP_DISK_RED)
12528 new_element = EL_SP_DISK_RED_ACTIVE;
12531 Feld[dropx][dropy] = new_element;
12533 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12534 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12535 el2img(Feld[dropx][dropy]), 0);
12537 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12539 /* needed if previous element just changed to "empty" in the last frame */
12540 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12542 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12543 player->index_bit, drop_side);
12544 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12546 player->index_bit, drop_side);
12548 TestIfElementTouchesCustomElement(dropx, dropy);
12550 else /* player is dropping a dyna bomb */
12552 player->dynabombs_left--;
12554 Feld[dropx][dropy] = new_element;
12556 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12557 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12558 el2img(Feld[dropx][dropy]), 0);
12560 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12563 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12564 InitField_WithBug1(dropx, dropy, FALSE);
12566 new_element = Feld[dropx][dropy]; /* element might have changed */
12568 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12569 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12571 int move_direction, nextx, nexty;
12573 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12574 MovDir[dropx][dropy] = drop_direction;
12576 move_direction = MovDir[dropx][dropy];
12577 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12578 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12580 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12581 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12584 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12585 player->is_dropping = TRUE;
12587 player->drop_pressed_delay = 0;
12588 player->is_dropping_pressed = FALSE;
12590 player->drop_x = dropx;
12591 player->drop_y = dropy;
12596 /* ------------------------------------------------------------------------- */
12597 /* game sound playing functions */
12598 /* ------------------------------------------------------------------------- */
12600 static int *loop_sound_frame = NULL;
12601 static int *loop_sound_volume = NULL;
12603 void InitPlayLevelSound()
12605 int num_sounds = getSoundListSize();
12607 checked_free(loop_sound_frame);
12608 checked_free(loop_sound_volume);
12610 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12611 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12614 static void PlayLevelSound(int x, int y, int nr)
12616 int sx = SCREENX(x), sy = SCREENY(y);
12617 int volume, stereo_position;
12618 int max_distance = 8;
12619 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12621 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12622 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12625 if (!IN_LEV_FIELD(x, y) ||
12626 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12627 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12630 volume = SOUND_MAX_VOLUME;
12632 if (!IN_SCR_FIELD(sx, sy))
12634 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12635 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12637 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12640 stereo_position = (SOUND_MAX_LEFT +
12641 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12642 (SCR_FIELDX + 2 * max_distance));
12644 if (IS_LOOP_SOUND(nr))
12646 /* This assures that quieter loop sounds do not overwrite louder ones,
12647 while restarting sound volume comparison with each new game frame. */
12649 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12652 loop_sound_volume[nr] = volume;
12653 loop_sound_frame[nr] = FrameCounter;
12656 PlaySoundExt(nr, volume, stereo_position, type);
12659 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12661 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12662 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12663 y < LEVELY(BY1) ? LEVELY(BY1) :
12664 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12668 static void PlayLevelSoundAction(int x, int y, int action)
12670 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12673 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12675 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12677 if (sound_effect != SND_UNDEFINED)
12678 PlayLevelSound(x, y, sound_effect);
12681 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12684 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12686 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12687 PlayLevelSound(x, y, sound_effect);
12690 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12692 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12694 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12695 PlayLevelSound(x, y, sound_effect);
12698 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12700 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12702 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12703 StopSound(sound_effect);
12706 static void PlayLevelMusic()
12708 if (levelset.music[level_nr] != MUS_UNDEFINED)
12709 PlayMusic(levelset.music[level_nr]); /* from config file */
12711 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12714 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12716 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12721 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12725 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12729 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12733 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12737 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12741 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12745 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12748 case SAMPLE_android_clone:
12749 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12752 case SAMPLE_android_move:
12753 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12756 case SAMPLE_spring:
12757 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12761 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12765 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12768 case SAMPLE_eater_eat:
12769 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12773 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12776 case SAMPLE_collect:
12777 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12780 case SAMPLE_diamond:
12781 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12784 case SAMPLE_squash:
12785 /* !!! CHECK THIS !!! */
12787 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12789 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12793 case SAMPLE_wonderfall:
12794 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12798 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12802 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12806 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12810 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12814 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12818 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12821 case SAMPLE_wonder:
12822 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12826 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12829 case SAMPLE_exit_open:
12830 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12833 case SAMPLE_exit_leave:
12834 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12837 case SAMPLE_dynamite:
12838 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12842 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12846 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12850 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12854 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12858 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12862 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12866 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12871 void RaiseScore(int value)
12873 local_player->score += value;
12875 DrawGameValue_Score(local_player->score);
12878 void RaiseScoreElement(int element)
12883 case EL_BD_DIAMOND:
12884 case EL_EMERALD_YELLOW:
12885 case EL_EMERALD_RED:
12886 case EL_EMERALD_PURPLE:
12887 case EL_SP_INFOTRON:
12888 RaiseScore(level.score[SC_EMERALD]);
12891 RaiseScore(level.score[SC_DIAMOND]);
12894 RaiseScore(level.score[SC_CRYSTAL]);
12897 RaiseScore(level.score[SC_PEARL]);
12900 case EL_BD_BUTTERFLY:
12901 case EL_SP_ELECTRON:
12902 RaiseScore(level.score[SC_BUG]);
12905 case EL_BD_FIREFLY:
12906 case EL_SP_SNIKSNAK:
12907 RaiseScore(level.score[SC_SPACESHIP]);
12910 case EL_DARK_YAMYAM:
12911 RaiseScore(level.score[SC_YAMYAM]);
12914 RaiseScore(level.score[SC_ROBOT]);
12917 RaiseScore(level.score[SC_PACMAN]);
12920 RaiseScore(level.score[SC_NUT]);
12923 case EL_EM_DYNAMITE:
12924 case EL_SP_DISK_RED:
12925 case EL_DYNABOMB_INCREASE_NUMBER:
12926 case EL_DYNABOMB_INCREASE_SIZE:
12927 case EL_DYNABOMB_INCREASE_POWER:
12928 RaiseScore(level.score[SC_DYNAMITE]);
12930 case EL_SHIELD_NORMAL:
12931 case EL_SHIELD_DEADLY:
12932 RaiseScore(level.score[SC_SHIELD]);
12934 case EL_EXTRA_TIME:
12935 RaiseScore(level.extra_time_score);
12949 RaiseScore(level.score[SC_KEY]);
12952 RaiseScore(element_info[element].collect_score);
12957 void RequestQuitGame(boolean ask_if_really_quit)
12959 if (AllPlayersGone ||
12960 !ask_if_really_quit ||
12961 level_editor_test_game ||
12962 Request("Do you really want to quit the game ?",
12963 REQ_ASK | REQ_STAY_CLOSED))
12965 #if defined(NETWORK_AVALIABLE)
12966 if (options.network)
12967 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12971 game_status = GAME_MODE_MAIN;
12977 if (tape.playing && tape.deactivate_display)
12978 TapeDeactivateDisplayOff(TRUE);
12980 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12982 if (tape.playing && tape.deactivate_display)
12983 TapeDeactivateDisplayOn();
12988 /* ---------- new game button stuff ---------------------------------------- */
12990 /* graphic position values for game buttons */
12991 #define GAME_BUTTON_XSIZE 30
12992 #define GAME_BUTTON_YSIZE 30
12993 #define GAME_BUTTON_XPOS 5
12994 #define GAME_BUTTON_YPOS 215
12995 #define SOUND_BUTTON_XPOS 5
12996 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12998 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12999 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13000 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13001 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13002 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13003 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13010 } gamebutton_info[NUM_GAME_BUTTONS] =
13013 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13018 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13019 GAME_CTRL_ID_PAUSE,
13023 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13028 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13029 SOUND_CTRL_ID_MUSIC,
13030 "background music on/off"
13033 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13034 SOUND_CTRL_ID_LOOPS,
13035 "sound loops on/off"
13038 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13039 SOUND_CTRL_ID_SIMPLE,
13040 "normal sounds on/off"
13044 void CreateGameButtons()
13048 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13050 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13051 struct GadgetInfo *gi;
13054 unsigned long event_mask;
13055 int gd_xoffset, gd_yoffset;
13056 int gd_x1, gd_x2, gd_y1, gd_y2;
13059 gd_xoffset = gamebutton_info[i].x;
13060 gd_yoffset = gamebutton_info[i].y;
13061 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13062 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13064 if (id == GAME_CTRL_ID_STOP ||
13065 id == GAME_CTRL_ID_PAUSE ||
13066 id == GAME_CTRL_ID_PLAY)
13068 button_type = GD_TYPE_NORMAL_BUTTON;
13070 event_mask = GD_EVENT_RELEASED;
13071 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13072 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13076 button_type = GD_TYPE_CHECK_BUTTON;
13078 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13079 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13080 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13081 event_mask = GD_EVENT_PRESSED;
13082 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13083 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13086 gi = CreateGadget(GDI_CUSTOM_ID, id,
13087 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13088 GDI_X, DX + gd_xoffset,
13089 GDI_Y, DY + gd_yoffset,
13090 GDI_WIDTH, GAME_BUTTON_XSIZE,
13091 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13092 GDI_TYPE, button_type,
13093 GDI_STATE, GD_BUTTON_UNPRESSED,
13094 GDI_CHECKED, checked,
13095 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13096 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13097 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13098 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13099 GDI_EVENT_MASK, event_mask,
13100 GDI_CALLBACK_ACTION, HandleGameButtons,
13104 Error(ERR_EXIT, "cannot create gadget");
13106 game_gadget[id] = gi;
13110 void FreeGameButtons()
13114 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13115 FreeGadget(game_gadget[i]);
13118 static void MapGameButtons()
13122 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13123 MapGadget(game_gadget[i]);
13126 void UnmapGameButtons()
13130 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13131 UnmapGadget(game_gadget[i]);
13134 static void HandleGameButtons(struct GadgetInfo *gi)
13136 int id = gi->custom_id;
13138 if (game_status != GAME_MODE_PLAYING)
13143 case GAME_CTRL_ID_STOP:
13147 RequestQuitGame(TRUE);
13150 case GAME_CTRL_ID_PAUSE:
13151 if (options.network)
13153 #if defined(NETWORK_AVALIABLE)
13155 SendToServer_ContinuePlaying();
13157 SendToServer_PausePlaying();
13161 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13164 case GAME_CTRL_ID_PLAY:
13167 #if defined(NETWORK_AVALIABLE)
13168 if (options.network)
13169 SendToServer_ContinuePlaying();
13173 tape.pausing = FALSE;
13174 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13179 case SOUND_CTRL_ID_MUSIC:
13180 if (setup.sound_music)
13182 setup.sound_music = FALSE;
13185 else if (audio.music_available)
13187 setup.sound = setup.sound_music = TRUE;
13189 SetAudioMode(setup.sound);
13195 case SOUND_CTRL_ID_LOOPS:
13196 if (setup.sound_loops)
13197 setup.sound_loops = FALSE;
13198 else if (audio.loops_available)
13200 setup.sound = setup.sound_loops = TRUE;
13201 SetAudioMode(setup.sound);
13205 case SOUND_CTRL_ID_SIMPLE:
13206 if (setup.sound_simple)
13207 setup.sound_simple = FALSE;
13208 else if (audio.sound_available)
13210 setup.sound = setup.sound_simple = TRUE;
13211 SetAudioMode(setup.sound);