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 move_delay = get_move_delay_from_stepsize(move_stepsize);
784 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
786 /* do no immediately change move delay -- the player might just be moving */
787 player->move_delay_value_next = move_delay;
789 /* information if player can move must be set separately */
790 player->cannot_move = cannot_move;
794 player->move_delay = game.initial_move_delay;
795 player->move_delay_value = game.initial_move_delay_value;
797 player->move_delay_value_next = -1;
799 player->move_delay_reset_counter = 0;
803 void GetPlayerConfig()
805 if (!audio.sound_available)
806 setup.sound_simple = FALSE;
808 if (!audio.loops_available)
809 setup.sound_loops = FALSE;
811 if (!audio.music_available)
812 setup.sound_music = FALSE;
814 if (!video.fullscreen_available)
815 setup.fullscreen = FALSE;
817 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
819 SetAudioMode(setup.sound);
823 static int getBeltNrFromBeltElement(int element)
825 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
826 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
827 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
830 static int getBeltNrFromBeltActiveElement(int element)
832 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
833 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
834 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
837 static int getBeltNrFromBeltSwitchElement(int element)
839 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
840 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
841 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
844 static int getBeltDirNrFromBeltSwitchElement(int element)
846 static int belt_base_element[4] =
848 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
849 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
850 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
851 EL_CONVEYOR_BELT_4_SWITCH_LEFT
854 int belt_nr = getBeltNrFromBeltSwitchElement(element);
855 int belt_dir_nr = element - belt_base_element[belt_nr];
857 return (belt_dir_nr % 3);
860 static int getBeltDirFromBeltSwitchElement(int element)
862 static int belt_move_dir[3] =
869 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
871 return belt_move_dir[belt_dir_nr];
874 static int get_element_from_group_element(int element)
876 if (IS_GROUP_ELEMENT(element))
878 struct ElementGroupInfo *group = element_info[element].group;
879 int last_anim_random_frame = gfx.anim_random_frame;
882 if (group->choice_mode == ANIM_RANDOM)
883 gfx.anim_random_frame = RND(group->num_elements_resolved);
885 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
886 group->choice_mode, 0,
889 if (group->choice_mode == ANIM_RANDOM)
890 gfx.anim_random_frame = last_anim_random_frame;
894 element = group->element_resolved[element_pos];
900 static void InitPlayerField(int x, int y, int element, boolean init_game)
902 if (element == EL_SP_MURPHY)
906 if (stored_player[0].present)
908 Feld[x][y] = EL_SP_MURPHY_CLONE;
914 stored_player[0].use_murphy = TRUE;
916 if (!level.use_artwork_element[0])
917 stored_player[0].artwork_element = EL_SP_MURPHY;
920 Feld[x][y] = EL_PLAYER_1;
926 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
927 int jx = player->jx, jy = player->jy;
929 player->present = TRUE;
931 player->block_last_field = (element == EL_SP_MURPHY ?
932 level.sp_block_last_field :
933 level.block_last_field);
935 /* ---------- initialize player's last field block delay --------------- */
937 /* always start with reliable default value (no adjustment needed) */
938 player->block_delay_adjustment = 0;
940 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
941 if (player->block_last_field && element == EL_SP_MURPHY)
942 player->block_delay_adjustment = 1;
944 /* special case 2: in game engines before 3.1.1, blocking was different */
945 if (game.use_block_last_field_bug)
946 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
948 if (!options.network || player->connected)
950 player->active = TRUE;
952 /* remove potentially duplicate players */
953 if (StorePlayer[jx][jy] == Feld[x][y])
954 StorePlayer[jx][jy] = 0;
956 StorePlayer[x][y] = Feld[x][y];
960 printf("Player %d activated.\n", player->element_nr);
961 printf("[Local player is %d and currently %s.]\n",
962 local_player->element_nr,
963 local_player->active ? "active" : "not active");
967 Feld[x][y] = EL_EMPTY;
969 player->jx = player->last_jx = x;
970 player->jy = player->last_jy = y;
974 static void InitField(int x, int y, boolean init_game)
976 int element = Feld[x][y];
985 InitPlayerField(x, y, element, init_game);
988 case EL_SOKOBAN_FIELD_PLAYER:
989 element = Feld[x][y] = EL_PLAYER_1;
990 InitField(x, y, init_game);
992 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
993 InitField(x, y, init_game);
996 case EL_SOKOBAN_FIELD_EMPTY:
997 local_player->sokobanfields_still_needed++;
1001 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1002 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1003 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1004 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1005 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1006 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1007 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1008 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1009 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1010 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1019 case EL_SPACESHIP_RIGHT:
1020 case EL_SPACESHIP_UP:
1021 case EL_SPACESHIP_LEFT:
1022 case EL_SPACESHIP_DOWN:
1023 case EL_BD_BUTTERFLY:
1024 case EL_BD_BUTTERFLY_RIGHT:
1025 case EL_BD_BUTTERFLY_UP:
1026 case EL_BD_BUTTERFLY_LEFT:
1027 case EL_BD_BUTTERFLY_DOWN:
1029 case EL_BD_FIREFLY_RIGHT:
1030 case EL_BD_FIREFLY_UP:
1031 case EL_BD_FIREFLY_LEFT:
1032 case EL_BD_FIREFLY_DOWN:
1033 case EL_PACMAN_RIGHT:
1035 case EL_PACMAN_LEFT:
1036 case EL_PACMAN_DOWN:
1038 case EL_YAMYAM_LEFT:
1039 case EL_YAMYAM_RIGHT:
1041 case EL_YAMYAM_DOWN:
1042 case EL_DARK_YAMYAM:
1045 case EL_SP_SNIKSNAK:
1046 case EL_SP_ELECTRON:
1055 case EL_AMOEBA_FULL:
1060 case EL_AMOEBA_DROP:
1061 if (y == lev_fieldy - 1)
1063 Feld[x][y] = EL_AMOEBA_GROWING;
1064 Store[x][y] = EL_AMOEBA_WET;
1068 case EL_DYNAMITE_ACTIVE:
1069 case EL_SP_DISK_RED_ACTIVE:
1070 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1071 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1072 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1074 MovDelay[x][y] = 96;
1077 case EL_EM_DYNAMITE_ACTIVE:
1078 MovDelay[x][y] = 32;
1082 local_player->lights_still_needed++;
1086 local_player->friends_still_needed++;
1091 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1094 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1095 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1096 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1097 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1098 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1099 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1100 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1101 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1102 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1103 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1104 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1105 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1108 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1109 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1110 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1112 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1114 game.belt_dir[belt_nr] = belt_dir;
1115 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1117 else /* more than one switch -- set it like the first switch */
1119 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1124 #if !USE_BOTH_SWITCHGATE_SWITCHES
1125 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1127 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1131 case EL_LIGHT_SWITCH_ACTIVE:
1133 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1136 case EL_INVISIBLE_STEELWALL:
1137 case EL_INVISIBLE_WALL:
1138 case EL_INVISIBLE_SAND:
1139 if (game.light_time_left > 0 ||
1140 game.lenses_time_left > 0)
1141 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1144 case EL_EMC_MAGIC_BALL:
1145 if (game.ball_state)
1146 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1149 case EL_EMC_MAGIC_BALL_SWITCH:
1150 if (game.ball_state)
1151 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1156 if (IS_CUSTOM_ELEMENT(element))
1158 if (CAN_MOVE(element))
1161 #if USE_NEW_CUSTOM_VALUE
1162 if (!element_info[element].use_last_ce_value || init_game)
1163 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1167 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1170 else if (IS_GROUP_ELEMENT(element))
1173 Feld[x][y] = get_element_from_group_element(element);
1175 InitField(x, y, init_game);
1177 struct ElementGroupInfo *group = element_info[element].group;
1178 int last_anim_random_frame = gfx.anim_random_frame;
1181 if (group->choice_mode == ANIM_RANDOM)
1182 gfx.anim_random_frame = RND(group->num_elements_resolved);
1184 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1185 group->choice_mode, 0,
1188 if (group->choice_mode == ANIM_RANDOM)
1189 gfx.anim_random_frame = last_anim_random_frame;
1191 group->choice_pos++;
1193 Feld[x][y] = group->element_resolved[element_pos];
1195 InitField(x, y, init_game);
1203 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1208 #if USE_NEW_CUSTOM_VALUE
1211 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1213 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1221 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1223 InitField(x, y, init_game);
1225 /* not needed to call InitMovDir() -- already done by InitField()! */
1226 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1227 CAN_MOVE(Feld[x][y]))
1231 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1233 int old_element = Feld[x][y];
1235 InitField(x, y, init_game);
1237 /* not needed to call InitMovDir() -- already done by InitField()! */
1238 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1239 CAN_MOVE(old_element) &&
1240 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1243 /* this case is in fact a combination of not less than three bugs:
1244 first, it calls InitMovDir() for elements that can move, although this is
1245 already done by InitField(); then, it checks the element that was at this
1246 field _before_ the call to InitField() (which can change it); lastly, it
1247 was not called for "mole with direction" elements, which were treated as
1248 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1252 inline void DrawGameValue_Emeralds(int value)
1254 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1256 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1259 inline void DrawGameValue_Dynamite(int value)
1261 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1263 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1266 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1268 int base_key_graphic = EL_KEY_1;
1271 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1272 base_key_graphic = EL_EM_KEY_1;
1274 /* currently only 4 of 8 possible keys are displayed */
1275 for (i = 0; i < STD_NUM_KEYS; i++)
1278 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1279 el2edimg(base_key_graphic + i));
1281 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1282 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1283 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1287 inline void DrawGameValue_Score(int value)
1289 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1291 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1294 inline void DrawGameValue_Time(int value)
1296 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1297 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1299 /* clear background if value just changed its size */
1300 if (value == 999 || value == 1000)
1301 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1304 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1306 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1309 inline void DrawGameValue_Level(int value)
1312 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1315 /* misuse area for displaying emeralds to draw bigger level number */
1316 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1317 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1319 /* now copy it to the area for displaying level number */
1320 BlitBitmap(drawto, drawto,
1321 DX_EMERALDS, DY_EMERALDS + 1,
1322 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1323 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1324 DX_LEVEL - 1, DY_LEVEL + 1);
1326 /* restore the area for displaying emeralds */
1327 DrawGameValue_Emeralds(local_player->gems_still_needed);
1329 /* yes, this is all really ugly :-) */
1333 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1336 int key[MAX_NUM_KEYS];
1339 for (i = 0; i < MAX_NUM_KEYS; i++)
1340 key[i] = key_bits & (1 << i);
1342 DrawGameValue_Level(level_nr);
1344 DrawGameValue_Emeralds(emeralds);
1345 DrawGameValue_Dynamite(dynamite);
1346 DrawGameValue_Score(score);
1347 DrawGameValue_Time(time);
1349 DrawGameValue_Keys(key);
1352 void DrawGameDoorValues()
1354 int dynamite_state = 0;
1358 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1360 DrawGameDoorValues_EM();
1366 DrawGameValue_Level(level_nr);
1368 DrawGameValue_Emeralds(local_player->gems_still_needed);
1369 DrawGameValue_Dynamite(local_player->inventory_size);
1370 DrawGameValue_Score(local_player->score);
1371 DrawGameValue_Time(TimeLeft);
1375 if (game.centered_player_nr == -1)
1377 for (i = 0; i < MAX_PLAYERS; i++)
1379 for (j = 0; j < MAX_NUM_KEYS; j++)
1380 if (stored_player[i].key[j])
1381 key_bits |= (1 << j);
1383 dynamite_state += stored_player[i].inventory_size;
1387 DrawGameValue_Keys(stored_player[i].key);
1392 int player_nr = game.centered_player_nr;
1394 for (i = 0; i < MAX_NUM_KEYS; i++)
1395 if (stored_player[player_nr].key[i])
1396 key_bits |= (1 << i);
1398 dynamite_state = stored_player[player_nr].inventory_size;
1401 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1402 local_player->score, TimeLeft, key_bits);
1407 static void resolve_group_element(int group_element, int recursion_depth)
1409 static int group_nr;
1410 static struct ElementGroupInfo *group;
1411 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1414 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1416 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1417 group_element - EL_GROUP_START + 1);
1419 /* replace element which caused too deep recursion by question mark */
1420 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1425 if (recursion_depth == 0) /* initialization */
1427 group = element_info[group_element].group;
1428 group_nr = group_element - EL_GROUP_START;
1430 group->num_elements_resolved = 0;
1431 group->choice_pos = 0;
1434 for (i = 0; i < actual_group->num_elements; i++)
1436 int element = actual_group->element[i];
1438 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1441 if (IS_GROUP_ELEMENT(element))
1442 resolve_group_element(element, recursion_depth + 1);
1445 group->element_resolved[group->num_elements_resolved++] = element;
1446 element_info[element].in_group[group_nr] = TRUE;
1453 =============================================================================
1455 -----------------------------------------------------------------------------
1456 initialize game engine due to level / tape version number
1457 =============================================================================
1460 static void InitGameEngine()
1462 int i, j, k, l, x, y;
1464 /* set game engine from tape file when re-playing, else from level file */
1465 game.engine_version = (tape.playing ? tape.engine_version :
1466 level.game_version);
1468 /* ---------------------------------------------------------------------- */
1469 /* set flags for bugs and changes according to active game engine version */
1470 /* ---------------------------------------------------------------------- */
1473 Summary of bugfix/change:
1474 Fixed handling for custom elements that change when pushed by the player.
1476 Fixed/changed in version:
1480 Before 3.1.0, custom elements that "change when pushing" changed directly
1481 after the player started pushing them (until then handled in "DigField()").
1482 Since 3.1.0, these custom elements are not changed until the "pushing"
1483 move of the element is finished (now handled in "ContinueMoving()").
1485 Affected levels/tapes:
1486 The first condition is generally needed for all levels/tapes before version
1487 3.1.0, which might use the old behaviour before it was changed; known tapes
1488 that are affected are some tapes from the level set "Walpurgis Gardens" by
1490 The second condition is an exception from the above case and is needed for
1491 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1492 above (including some development versions of 3.1.0), but before it was
1493 known that this change would break tapes like the above and was fixed in
1494 3.1.1, so that the changed behaviour was active although the engine version
1495 while recording maybe was before 3.1.0. There is at least one tape that is
1496 affected by this exception, which is the tape for the one-level set "Bug
1497 Machine" by Juergen Bonhagen.
1500 game.use_change_when_pushing_bug =
1501 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1503 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1504 tape.game_version < VERSION_IDENT(3,1,1,0)));
1507 Summary of bugfix/change:
1508 Fixed handling for blocking the field the player leaves when moving.
1510 Fixed/changed in version:
1514 Before 3.1.1, when "block last field when moving" was enabled, the field
1515 the player is leaving when moving was blocked for the time of the move,
1516 and was directly unblocked afterwards. This resulted in the last field
1517 being blocked for exactly one less than the number of frames of one player
1518 move. Additionally, even when blocking was disabled, the last field was
1519 blocked for exactly one frame.
1520 Since 3.1.1, due to changes in player movement handling, the last field
1521 is not blocked at all when blocking is disabled. When blocking is enabled,
1522 the last field is blocked for exactly the number of frames of one player
1523 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1524 last field is blocked for exactly one more than the number of frames of
1527 Affected levels/tapes:
1528 (!!! yet to be determined -- probably many !!!)
1531 game.use_block_last_field_bug =
1532 (game.engine_version < VERSION_IDENT(3,1,1,0));
1535 Summary of bugfix/change:
1536 Changed behaviour of CE changes with multiple changes per single frame.
1538 Fixed/changed in version:
1542 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1543 This resulted in race conditions where CEs seem to behave strange in some
1544 situations (where triggered CE changes were just skipped because there was
1545 already a CE change on that tile in the playfield in that engine frame).
1546 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1547 (The number of changes per frame must be limited in any case, because else
1548 it is easily possible to define CE changes that would result in an infinite
1549 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1550 should be set large enough so that it would only be reached in cases where
1551 the corresponding CE change conditions run into a loop. Therefore, it seems
1552 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1553 maximal number of change pages for custom elements.)
1555 Affected levels/tapes:
1559 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1560 game.max_num_changes_per_frame = 1;
1562 game.max_num_changes_per_frame =
1563 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1566 /* ---------------------------------------------------------------------- */
1568 /* default scan direction: scan playfield from top/left to bottom/right */
1569 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1571 /* dynamically adjust element properties according to game engine version */
1572 InitElementPropertiesEngine(game.engine_version);
1575 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1576 printf(" tape version == %06d [%s] [file: %06d]\n",
1577 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1579 printf(" => game.engine_version == %06d\n", game.engine_version);
1583 /* ---------- recursively resolve group elements ------------------------- */
1585 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1586 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1587 element_info[i].in_group[j] = FALSE;
1589 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1590 resolve_group_element(EL_GROUP_START + i, 0);
1593 /* ---------- initialize player's initial move delay --------------------- */
1596 /* dynamically adjust player properties according to level information */
1597 game.initial_move_delay_value =
1598 get_move_delay_from_stepsize(level.initial_player_stepsize);
1600 /* dynamically adjust player properties according to level information */
1601 game.initial_move_delay_value =
1602 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1605 /* dynamically adjust player properties according to game engine version */
1606 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1607 game.initial_move_delay_value : 0);
1609 /* ---------- initialize player's initial push delay --------------------- */
1611 /* dynamically adjust player properties according to game engine version */
1612 game.initial_push_delay_value =
1613 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1615 /* ---------- initialize changing elements ------------------------------- */
1617 /* initialize changing elements information */
1618 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 struct ElementInfo *ei = &element_info[i];
1622 /* this pointer might have been changed in the level editor */
1623 ei->change = &ei->change_page[0];
1625 if (!IS_CUSTOM_ELEMENT(i))
1627 ei->change->target_element = EL_EMPTY_SPACE;
1628 ei->change->delay_fixed = 0;
1629 ei->change->delay_random = 0;
1630 ei->change->delay_frames = 1;
1633 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1635 ei->has_change_event[j] = FALSE;
1637 ei->event_page_nr[j] = 0;
1638 ei->event_page[j] = &ei->change_page[0];
1642 /* add changing elements from pre-defined list */
1643 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1645 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1646 struct ElementInfo *ei = &element_info[ch_delay->element];
1648 ei->change->target_element = ch_delay->target_element;
1649 ei->change->delay_fixed = ch_delay->change_delay;
1651 ei->change->pre_change_function = ch_delay->pre_change_function;
1652 ei->change->change_function = ch_delay->change_function;
1653 ei->change->post_change_function = ch_delay->post_change_function;
1655 ei->change->can_change = TRUE;
1656 ei->change->can_change_or_has_action = TRUE;
1658 ei->has_change_event[CE_DELAY] = TRUE;
1660 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1661 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1664 /* ---------- initialize internal run-time variables ------------- */
1666 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1668 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1670 for (j = 0; j < ei->num_change_pages; j++)
1672 ei->change_page[j].can_change_or_has_action =
1673 (ei->change_page[j].can_change |
1674 ei->change_page[j].has_action);
1678 /* add change events from custom element configuration */
1679 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1681 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1683 for (j = 0; j < ei->num_change_pages; j++)
1685 if (!ei->change_page[j].can_change_or_has_action)
1688 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1690 /* only add event page for the first page found with this event */
1691 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1693 ei->has_change_event[k] = TRUE;
1695 ei->event_page_nr[k] = j;
1696 ei->event_page[k] = &ei->change_page[j];
1702 /* ---------- initialize run-time trigger player and element ------------- */
1704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1706 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1708 for (j = 0; j < ei->num_change_pages; j++)
1710 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1711 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1712 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1713 ei->change_page[j].actual_trigger_ce_value = 0;
1714 ei->change_page[j].actual_trigger_ce_score = 0;
1718 /* ---------- initialize trigger events ---------------------------------- */
1720 /* initialize trigger events information */
1721 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1722 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1723 trigger_events[i][j] = FALSE;
1725 /* add trigger events from element change event properties */
1726 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1728 struct ElementInfo *ei = &element_info[i];
1730 for (j = 0; j < ei->num_change_pages; j++)
1732 if (!ei->change_page[j].can_change_or_has_action)
1735 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1737 int trigger_element = ei->change_page[j].trigger_element;
1739 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1741 if (ei->change_page[j].has_event[k])
1743 if (IS_GROUP_ELEMENT(trigger_element))
1745 struct ElementGroupInfo *group =
1746 element_info[trigger_element].group;
1748 for (l = 0; l < group->num_elements_resolved; l++)
1749 trigger_events[group->element_resolved[l]][k] = TRUE;
1752 trigger_events[trigger_element][k] = TRUE;
1759 /* ---------- initialize push delay -------------------------------------- */
1761 /* initialize push delay values to default */
1762 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1764 if (!IS_CUSTOM_ELEMENT(i))
1766 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1767 element_info[i].push_delay_random = game.default_push_delay_random;
1771 /* set push delay value for certain elements from pre-defined list */
1772 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1774 int e = push_delay_list[i].element;
1776 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1777 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1780 /* set push delay value for Supaplex elements for newer engine versions */
1781 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1783 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1785 if (IS_SP_ELEMENT(i))
1787 /* set SP push delay to just enough to push under a falling zonk */
1788 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1790 element_info[i].push_delay_fixed = delay;
1791 element_info[i].push_delay_random = 0;
1796 /* ---------- initialize move stepsize ----------------------------------- */
1798 /* initialize move stepsize values to default */
1799 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1800 if (!IS_CUSTOM_ELEMENT(i))
1801 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1803 /* set move stepsize value for certain elements from pre-defined list */
1804 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1806 int e = move_stepsize_list[i].element;
1808 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1811 /* ---------- initialize collect score ----------------------------------- */
1813 /* initialize collect score values for custom elements from initial value */
1814 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1815 if (IS_CUSTOM_ELEMENT(i))
1816 element_info[i].collect_score = element_info[i].collect_score_initial;
1818 /* ---------- initialize collect count ----------------------------------- */
1820 /* initialize collect count values for non-custom elements */
1821 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1822 if (!IS_CUSTOM_ELEMENT(i))
1823 element_info[i].collect_count_initial = 0;
1825 /* add collect count values for all elements from pre-defined list */
1826 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1827 element_info[collect_count_list[i].element].collect_count_initial =
1828 collect_count_list[i].count;
1830 /* ---------- initialize access direction -------------------------------- */
1832 /* initialize access direction values to default (access from every side) */
1833 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1834 if (!IS_CUSTOM_ELEMENT(i))
1835 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1837 /* set access direction value for certain elements from pre-defined list */
1838 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1839 element_info[access_direction_list[i].element].access_direction =
1840 access_direction_list[i].direction;
1842 /* ---------- initialize explosion content ------------------------------- */
1843 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1845 if (IS_CUSTOM_ELEMENT(i))
1848 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1850 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1852 element_info[i].content.e[x][y] =
1853 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1854 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1855 i == EL_PLAYER_3 ? EL_EMERALD :
1856 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1857 i == EL_MOLE ? EL_EMERALD_RED :
1858 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1859 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1860 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1861 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1862 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1863 i == EL_WALL_EMERALD ? EL_EMERALD :
1864 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1865 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1866 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1867 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1868 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1869 i == EL_WALL_PEARL ? EL_PEARL :
1870 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1876 int get_num_special_action(int element, int action_first, int action_last)
1878 int num_special_action = 0;
1881 for (i = action_first; i <= action_last; i++)
1883 boolean found = FALSE;
1885 for (j = 0; j < NUM_DIRECTIONS; j++)
1886 if (el_act_dir2img(element, i, j) !=
1887 el_act_dir2img(element, ACTION_DEFAULT, j))
1891 num_special_action++;
1897 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1900 return num_special_action;
1904 =============================================================================
1906 -----------------------------------------------------------------------------
1907 initialize and start new game
1908 =============================================================================
1913 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1914 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1915 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1920 /* don't play tapes over network */
1921 network_playing = (options.network && !tape.playing);
1923 for (i = 0; i < MAX_PLAYERS; i++)
1925 struct PlayerInfo *player = &stored_player[i];
1927 player->index_nr = i;
1928 player->index_bit = (1 << i);
1929 player->element_nr = EL_PLAYER_1 + i;
1931 player->present = FALSE;
1932 player->active = FALSE;
1935 player->effective_action = 0;
1936 player->programmed_action = 0;
1939 player->gems_still_needed = level.gems_needed;
1940 player->sokobanfields_still_needed = 0;
1941 player->lights_still_needed = 0;
1942 player->friends_still_needed = 0;
1944 for (j = 0; j < MAX_NUM_KEYS; j++)
1945 player->key[j] = FALSE;
1947 player->dynabomb_count = 0;
1948 player->dynabomb_size = 1;
1949 player->dynabombs_left = 0;
1950 player->dynabomb_xl = FALSE;
1952 player->MovDir = MV_NONE;
1955 player->GfxDir = MV_NONE;
1956 player->GfxAction = ACTION_DEFAULT;
1958 player->StepFrame = 0;
1960 player->use_murphy = FALSE;
1961 player->artwork_element =
1962 (level.use_artwork_element[i] ? level.artwork_element[i] :
1963 player->element_nr);
1965 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1966 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1968 player->gravity = level.initial_player_gravity[i];
1970 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1972 player->actual_frame_counter = 0;
1974 player->step_counter = 0;
1976 player->last_move_dir = MV_NONE;
1978 player->is_waiting = FALSE;
1979 player->is_moving = FALSE;
1980 player->is_auto_moving = FALSE;
1981 player->is_digging = FALSE;
1982 player->is_snapping = FALSE;
1983 player->is_collecting = FALSE;
1984 player->is_pushing = FALSE;
1985 player->is_switching = FALSE;
1986 player->is_dropping = FALSE;
1987 player->is_dropping_pressed = FALSE;
1989 player->is_bored = FALSE;
1990 player->is_sleeping = FALSE;
1992 player->frame_counter_bored = -1;
1993 player->frame_counter_sleeping = -1;
1995 player->anim_delay_counter = 0;
1996 player->post_delay_counter = 0;
1998 player->dir_waiting = MV_NONE;
1999 player->action_waiting = ACTION_DEFAULT;
2000 player->last_action_waiting = ACTION_DEFAULT;
2001 player->special_action_bored = ACTION_DEFAULT;
2002 player->special_action_sleeping = ACTION_DEFAULT;
2005 /* cannot be set here -- could be modified in Init[Player]Field() below */
2007 /* set number of special actions for bored and sleeping animation */
2008 player->num_special_action_bored =
2009 get_num_special_action(player->artwork_element,
2010 ACTION_BORING_1, ACTION_BORING_LAST);
2011 player->num_special_action_sleeping =
2012 get_num_special_action(player->artwork_element,
2013 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2016 player->switch_x = -1;
2017 player->switch_y = -1;
2019 player->drop_x = -1;
2020 player->drop_y = -1;
2022 player->show_envelope = 0;
2025 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
2027 player->move_delay = game.initial_move_delay;
2028 player->move_delay_value = game.initial_move_delay_value;
2030 player->move_delay_value_next = -1;
2032 player->move_delay_reset_counter = 0;
2034 player->cannot_move = FALSE;
2037 player->push_delay = -1; /* initialized when pushing starts */
2038 player->push_delay_value = game.initial_push_delay_value;
2040 player->drop_delay = 0;
2041 player->drop_pressed_delay = 0;
2043 player->last_jx = player->last_jy = 0;
2044 player->jx = player->jy = 0;
2046 player->shield_normal_time_left = 0;
2047 player->shield_deadly_time_left = 0;
2049 player->inventory_infinite_element = EL_UNDEFINED;
2050 player->inventory_size = 0;
2052 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2053 SnapField(player, 0, 0);
2055 player->LevelSolved = FALSE;
2056 player->GameOver = FALSE;
2059 network_player_action_received = FALSE;
2061 #if defined(NETWORK_AVALIABLE)
2062 /* initial null action */
2063 if (network_playing)
2064 SendToServer_MovePlayer(MV_NONE);
2073 TimeLeft = level.time;
2076 ScreenMovDir = MV_NONE;
2080 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2082 AllPlayersGone = FALSE;
2084 game.yamyam_content_nr = 0;
2085 game.magic_wall_active = FALSE;
2086 game.magic_wall_time_left = 0;
2087 game.light_time_left = 0;
2088 game.timegate_time_left = 0;
2089 game.switchgate_pos = 0;
2090 game.wind_direction = level.wind_direction_initial;
2091 game.gravity = level.initial_gravity;
2092 game.explosions_delayed = TRUE;
2094 game.lenses_time_left = 0;
2095 game.magnify_time_left = 0;
2097 game.ball_state = level.ball_state_initial;
2098 game.ball_content_nr = 0;
2100 game.envelope_active = FALSE;
2102 /* set focus to local player for network games, else to all players */
2103 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2104 game.centered_player_nr_next = game.centered_player_nr;
2105 game.set_centered_player = FALSE;
2107 if (network_playing && tape.recording)
2109 /* store client dependent player focus when recording network games */
2110 tape.centered_player_nr_next = game.centered_player_nr_next;
2111 tape.set_centered_player = TRUE;
2115 printf("::: focus set to player %d [%d]\n",
2116 game.centered_player_nr, local_player->index_nr);
2119 for (i = 0; i < NUM_BELTS; i++)
2121 game.belt_dir[i] = MV_NONE;
2122 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2125 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2126 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2129 SCAN_PLAYFIELD(x, y)
2131 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2134 Feld[x][y] = level.field[x][y];
2135 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2136 ChangeDelay[x][y] = 0;
2137 ChangePage[x][y] = -1;
2138 #if USE_NEW_CUSTOM_VALUE
2139 CustomValue[x][y] = 0; /* initialized in InitField() */
2141 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2143 WasJustMoving[x][y] = 0;
2144 WasJustFalling[x][y] = 0;
2145 CheckCollision[x][y] = 0;
2147 Pushed[x][y] = FALSE;
2149 ChangeCount[x][y] = 0;
2150 ChangeEvent[x][y] = -1;
2152 ExplodePhase[x][y] = 0;
2153 ExplodeDelay[x][y] = 0;
2154 ExplodeField[x][y] = EX_TYPE_NONE;
2156 RunnerVisit[x][y] = 0;
2157 PlayerVisit[x][y] = 0;
2160 GfxRandom[x][y] = INIT_GFX_RANDOM();
2161 GfxElement[x][y] = EL_UNDEFINED;
2162 GfxAction[x][y] = ACTION_DEFAULT;
2163 GfxDir[x][y] = MV_NONE;
2167 SCAN_PLAYFIELD(x, y)
2169 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2172 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2174 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2176 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2179 InitField(x, y, TRUE);
2184 for (i = 0; i < MAX_PLAYERS; i++)
2186 struct PlayerInfo *player = &stored_player[i];
2189 /* set number of special actions for bored and sleeping animation */
2190 player->num_special_action_bored =
2191 get_num_special_action(player->artwork_element,
2192 ACTION_BORING_1, ACTION_BORING_LAST);
2193 player->num_special_action_sleeping =
2194 get_num_special_action(player->artwork_element,
2195 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2200 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2201 emulate_sb ? EMU_SOKOBAN :
2202 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2204 #if USE_NEW_ALL_SLIPPERY
2205 /* initialize type of slippery elements */
2206 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2208 if (!IS_CUSTOM_ELEMENT(i))
2210 /* default: elements slip down either to the left or right randomly */
2211 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2213 /* SP style elements prefer to slip down on the left side */
2214 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2215 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2217 /* BD style elements prefer to slip down on the left side */
2218 if (game.emulation == EMU_BOULDERDASH)
2219 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2224 /* initialize explosion and ignition delay */
2225 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2227 if (!IS_CUSTOM_ELEMENT(i))
2230 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2231 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2232 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2233 int last_phase = (num_phase + 1) * delay;
2234 int half_phase = (num_phase / 2) * delay;
2236 element_info[i].explosion_delay = last_phase - 1;
2237 element_info[i].ignition_delay = half_phase;
2239 if (i == EL_BLACK_ORB)
2240 element_info[i].ignition_delay = 1;
2244 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2245 element_info[i].explosion_delay = 1;
2247 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2248 element_info[i].ignition_delay = 1;
2252 /* correct non-moving belts to start moving left */
2253 for (i = 0; i < NUM_BELTS; i++)
2254 if (game.belt_dir[i] == MV_NONE)
2255 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2257 /* check if any connected player was not found in playfield */
2258 for (i = 0; i < MAX_PLAYERS; i++)
2260 struct PlayerInfo *player = &stored_player[i];
2262 if (player->connected && !player->present)
2264 for (j = 0; j < MAX_PLAYERS; j++)
2266 struct PlayerInfo *some_player = &stored_player[j];
2267 int jx = some_player->jx, jy = some_player->jy;
2269 /* assign first free player found that is present in the playfield */
2270 if (some_player->present && !some_player->connected)
2272 player->present = TRUE;
2273 player->active = TRUE;
2275 some_player->present = FALSE;
2276 some_player->active = FALSE;
2279 player->element_nr = some_player->element_nr;
2282 player->artwork_element = some_player->artwork_element;
2284 player->block_last_field = some_player->block_last_field;
2285 player->block_delay_adjustment = some_player->block_delay_adjustment;
2287 StorePlayer[jx][jy] = player->element_nr;
2288 player->jx = player->last_jx = jx;
2289 player->jy = player->last_jy = jy;
2299 /* when playing a tape, eliminate all players who do not participate */
2301 for (i = 0; i < MAX_PLAYERS; i++)
2303 if (stored_player[i].active && !tape.player_participates[i])
2305 struct PlayerInfo *player = &stored_player[i];
2306 int jx = player->jx, jy = player->jy;
2308 player->active = FALSE;
2309 StorePlayer[jx][jy] = 0;
2310 Feld[jx][jy] = EL_EMPTY;
2314 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2316 /* when in single player mode, eliminate all but the first active player */
2318 for (i = 0; i < MAX_PLAYERS; i++)
2320 if (stored_player[i].active)
2322 for (j = i + 1; j < MAX_PLAYERS; j++)
2324 if (stored_player[j].active)
2326 struct PlayerInfo *player = &stored_player[j];
2327 int jx = player->jx, jy = player->jy;
2329 player->active = FALSE;
2330 player->present = FALSE;
2332 StorePlayer[jx][jy] = 0;
2333 Feld[jx][jy] = EL_EMPTY;
2340 /* when recording the game, store which players take part in the game */
2343 for (i = 0; i < MAX_PLAYERS; i++)
2344 if (stored_player[i].active)
2345 tape.player_participates[i] = TRUE;
2350 for (i = 0; i < MAX_PLAYERS; i++)
2352 struct PlayerInfo *player = &stored_player[i];
2354 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2359 if (local_player == player)
2360 printf("Player %d is local player.\n", i+1);
2364 if (BorderElement == EL_EMPTY)
2367 SBX_Right = lev_fieldx - SCR_FIELDX;
2369 SBY_Lower = lev_fieldy - SCR_FIELDY;
2374 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2376 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2379 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2380 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2382 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2383 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2385 /* if local player not found, look for custom element that might create
2386 the player (make some assumptions about the right custom element) */
2387 if (!local_player->present)
2389 int start_x = 0, start_y = 0;
2390 int found_rating = 0;
2391 int found_element = EL_UNDEFINED;
2392 int player_nr = local_player->index_nr;
2395 SCAN_PLAYFIELD(x, y)
2397 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2400 int element = Feld[x][y];
2405 if (level.use_start_element[player_nr] &&
2406 level.start_element[player_nr] == element &&
2413 found_element = element;
2416 if (!IS_CUSTOM_ELEMENT(element))
2419 if (CAN_CHANGE(element))
2421 for (i = 0; i < element_info[element].num_change_pages; i++)
2423 /* check for player created from custom element as single target */
2424 content = element_info[element].change_page[i].target_element;
2425 is_player = ELEM_IS_PLAYER(content);
2427 if (is_player && (found_rating < 3 || element < found_element))
2433 found_element = element;
2438 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2440 /* check for player created from custom element as explosion content */
2441 content = element_info[element].content.e[xx][yy];
2442 is_player = ELEM_IS_PLAYER(content);
2444 if (is_player && (found_rating < 2 || element < found_element))
2446 start_x = x + xx - 1;
2447 start_y = y + yy - 1;
2450 found_element = element;
2453 if (!CAN_CHANGE(element))
2456 for (i = 0; i < element_info[element].num_change_pages; i++)
2458 /* check for player created from custom element as extended target */
2460 element_info[element].change_page[i].target_content.e[xx][yy];
2462 is_player = ELEM_IS_PLAYER(content);
2464 if (is_player && (found_rating < 1 || element < found_element))
2466 start_x = x + xx - 1;
2467 start_y = y + yy - 1;
2470 found_element = element;
2476 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2477 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2480 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2481 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2486 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2487 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2488 local_player->jx - MIDPOSX);
2490 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2491 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2492 local_player->jy - MIDPOSY);
2495 if (!game.restart_level)
2496 CloseDoor(DOOR_CLOSE_1);
2498 /* !!! FIX THIS (START) !!! */
2499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2501 InitGameEngine_EM();
2508 /* after drawing the level, correct some elements */
2509 if (game.timegate_time_left == 0)
2510 CloseAllOpenTimegates();
2512 if (setup.soft_scrolling)
2513 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2515 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2518 /* !!! FIX THIS (END) !!! */
2520 if (!game.restart_level)
2522 /* copy default game door content to main double buffer */
2523 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2524 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2527 DrawGameDoorValues();
2529 if (!game.restart_level)
2533 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2534 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2535 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2539 /* copy actual game door content to door double buffer for OpenDoor() */
2540 BlitBitmap(drawto, bitmap_db_door,
2541 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2543 OpenDoor(DOOR_OPEN_ALL);
2545 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2547 if (setup.sound_music)
2550 KeyboardAutoRepeatOffUnlessAutoplay();
2554 for (i = 0; i < MAX_PLAYERS; i++)
2555 printf("Player %d %sactive.\n",
2556 i + 1, (stored_player[i].active ? "" : "not "));
2560 game.restart_level = FALSE;
2563 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2565 /* this is used for non-R'n'D game engines to update certain engine values */
2567 /* needed to determine if sounds are played within the visible screen area */
2568 scroll_x = actual_scroll_x;
2569 scroll_y = actual_scroll_y;
2572 void InitMovDir(int x, int y)
2574 int i, element = Feld[x][y];
2575 static int xy[4][2] =
2582 static int direction[3][4] =
2584 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2585 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2586 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2595 Feld[x][y] = EL_BUG;
2596 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2599 case EL_SPACESHIP_RIGHT:
2600 case EL_SPACESHIP_UP:
2601 case EL_SPACESHIP_LEFT:
2602 case EL_SPACESHIP_DOWN:
2603 Feld[x][y] = EL_SPACESHIP;
2604 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2607 case EL_BD_BUTTERFLY_RIGHT:
2608 case EL_BD_BUTTERFLY_UP:
2609 case EL_BD_BUTTERFLY_LEFT:
2610 case EL_BD_BUTTERFLY_DOWN:
2611 Feld[x][y] = EL_BD_BUTTERFLY;
2612 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2615 case EL_BD_FIREFLY_RIGHT:
2616 case EL_BD_FIREFLY_UP:
2617 case EL_BD_FIREFLY_LEFT:
2618 case EL_BD_FIREFLY_DOWN:
2619 Feld[x][y] = EL_BD_FIREFLY;
2620 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2623 case EL_PACMAN_RIGHT:
2625 case EL_PACMAN_LEFT:
2626 case EL_PACMAN_DOWN:
2627 Feld[x][y] = EL_PACMAN;
2628 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2631 case EL_YAMYAM_LEFT:
2632 case EL_YAMYAM_RIGHT:
2634 case EL_YAMYAM_DOWN:
2635 Feld[x][y] = EL_YAMYAM;
2636 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2639 case EL_SP_SNIKSNAK:
2640 MovDir[x][y] = MV_UP;
2643 case EL_SP_ELECTRON:
2644 MovDir[x][y] = MV_LEFT;
2651 Feld[x][y] = EL_MOLE;
2652 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2656 if (IS_CUSTOM_ELEMENT(element))
2658 struct ElementInfo *ei = &element_info[element];
2659 int move_direction_initial = ei->move_direction_initial;
2660 int move_pattern = ei->move_pattern;
2662 if (move_direction_initial == MV_START_PREVIOUS)
2664 if (MovDir[x][y] != MV_NONE)
2667 move_direction_initial = MV_START_AUTOMATIC;
2670 if (move_direction_initial == MV_START_RANDOM)
2671 MovDir[x][y] = 1 << RND(4);
2672 else if (move_direction_initial & MV_ANY_DIRECTION)
2673 MovDir[x][y] = move_direction_initial;
2674 else if (move_pattern == MV_ALL_DIRECTIONS ||
2675 move_pattern == MV_TURNING_LEFT ||
2676 move_pattern == MV_TURNING_RIGHT ||
2677 move_pattern == MV_TURNING_LEFT_RIGHT ||
2678 move_pattern == MV_TURNING_RIGHT_LEFT ||
2679 move_pattern == MV_TURNING_RANDOM)
2680 MovDir[x][y] = 1 << RND(4);
2681 else if (move_pattern == MV_HORIZONTAL)
2682 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2683 else if (move_pattern == MV_VERTICAL)
2684 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2685 else if (move_pattern & MV_ANY_DIRECTION)
2686 MovDir[x][y] = element_info[element].move_pattern;
2687 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2688 move_pattern == MV_ALONG_RIGHT_SIDE)
2690 /* use random direction as default start direction */
2691 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2692 MovDir[x][y] = 1 << RND(4);
2694 for (i = 0; i < NUM_DIRECTIONS; i++)
2696 int x1 = x + xy[i][0];
2697 int y1 = y + xy[i][1];
2699 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2701 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2702 MovDir[x][y] = direction[0][i];
2704 MovDir[x][y] = direction[1][i];
2713 MovDir[x][y] = 1 << RND(4);
2715 if (element != EL_BUG &&
2716 element != EL_SPACESHIP &&
2717 element != EL_BD_BUTTERFLY &&
2718 element != EL_BD_FIREFLY)
2721 for (i = 0; i < NUM_DIRECTIONS; i++)
2723 int x1 = x + xy[i][0];
2724 int y1 = y + xy[i][1];
2726 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2728 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2730 MovDir[x][y] = direction[0][i];
2733 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2734 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2736 MovDir[x][y] = direction[1][i];
2745 GfxDir[x][y] = MovDir[x][y];
2748 void InitAmoebaNr(int x, int y)
2751 int group_nr = AmoebeNachbarNr(x, y);
2755 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2757 if (AmoebaCnt[i] == 0)
2765 AmoebaNr[x][y] = group_nr;
2766 AmoebaCnt[group_nr]++;
2767 AmoebaCnt2[group_nr]++;
2773 boolean raise_level = FALSE;
2775 if (local_player->MovPos)
2778 if (tape.auto_play) /* tape might already be stopped here */
2779 tape.auto_play_level_solved = TRUE;
2781 local_player->LevelSolved = FALSE;
2783 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2787 if (!tape.playing && setup.sound_loops)
2788 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2789 SND_CTRL_PLAY_LOOP);
2791 while (TimeLeft > 0)
2793 if (!tape.playing && !setup.sound_loops)
2794 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2796 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2799 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2804 RaiseScore(level.score[SC_TIME_BONUS]);
2807 DrawGameValue_Time(TimeLeft);
2815 if (!tape.playing && setup.sound_loops)
2816 StopSound(SND_GAME_LEVELTIME_BONUS);
2818 else if (level.time == 0) /* level without time limit */
2820 if (!tape.playing && setup.sound_loops)
2821 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2822 SND_CTRL_PLAY_LOOP);
2824 while (TimePlayed < 999)
2826 if (!tape.playing && !setup.sound_loops)
2827 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2829 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2832 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2837 RaiseScore(level.score[SC_TIME_BONUS]);
2840 DrawGameValue_Time(TimePlayed);
2848 if (!tape.playing && setup.sound_loops)
2849 StopSound(SND_GAME_LEVELTIME_BONUS);
2852 /* close exit door after last player */
2853 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2854 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2855 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2857 int element = Feld[ExitX][ExitY];
2859 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2860 EL_SP_EXIT_CLOSING);
2862 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2865 /* player disappears */
2866 if (ExitX >= 0 && ExitY >= 0)
2867 DrawLevelField(ExitX, ExitY);
2873 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2875 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2881 CloseDoor(DOOR_CLOSE_1);
2886 SaveTape(tape.level_nr); /* Ask to save tape */
2889 if (level_nr == leveldir_current->handicap_level)
2891 leveldir_current->handicap_level++;
2892 SaveLevelSetup_SeriesInfo();
2895 if (level_editor_test_game)
2896 local_player->score = -1; /* no highscore when playing from editor */
2897 else if (level_nr < leveldir_current->last_level)
2898 raise_level = TRUE; /* advance to next level */
2900 if ((hi_pos = NewHiScore()) >= 0)
2902 game_status = GAME_MODE_SCORES;
2903 DrawHallOfFame(hi_pos);
2912 game_status = GAME_MODE_MAIN;
2929 LoadScore(level_nr);
2931 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2932 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2935 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2937 if (local_player->score > highscore[k].Score)
2939 /* player has made it to the hall of fame */
2941 if (k < MAX_SCORE_ENTRIES - 1)
2943 int m = MAX_SCORE_ENTRIES - 1;
2946 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2947 if (strEqual(setup.player_name, highscore[l].Name))
2949 if (m == k) /* player's new highscore overwrites his old one */
2953 for (l = m; l > k; l--)
2955 strcpy(highscore[l].Name, highscore[l - 1].Name);
2956 highscore[l].Score = highscore[l - 1].Score;
2963 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2964 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2965 highscore[k].Score = local_player->score;
2971 else if (!strncmp(setup.player_name, highscore[k].Name,
2972 MAX_PLAYER_NAME_LEN))
2973 break; /* player already there with a higher score */
2979 SaveScore(level_nr);
2984 inline static int getElementMoveStepsize(int x, int y)
2986 int element = Feld[x][y];
2987 int direction = MovDir[x][y];
2988 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2989 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2990 int horiz_move = (dx != 0);
2991 int sign = (horiz_move ? dx : dy);
2992 int step = sign * element_info[element].move_stepsize;
2994 /* special values for move stepsize for spring and things on conveyor belt */
2998 if (element == EL_SPRING)
2999 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3000 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3001 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3002 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3004 if (CAN_FALL(element) &&
3005 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3006 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3007 else if (element == EL_SPRING)
3008 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3015 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3017 if (player->GfxAction != action || player->GfxDir != dir)
3020 printf("Player frame reset! (%d => %d, %d => %d)\n",
3021 player->GfxAction, action, player->GfxDir, dir);
3024 player->GfxAction = action;
3025 player->GfxDir = dir;
3027 player->StepFrame = 0;
3031 #if USE_GFX_RESET_GFX_ANIMATION
3032 static void ResetGfxFrame(int x, int y, boolean redraw)
3034 int element = Feld[x][y];
3035 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3036 int last_gfx_frame = GfxFrame[x][y];
3038 if (graphic_info[graphic].anim_global_sync)
3039 GfxFrame[x][y] = FrameCounter;
3040 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3041 GfxFrame[x][y] = CustomValue[x][y];
3042 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3043 GfxFrame[x][y] = element_info[element].collect_score;
3044 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3045 GfxFrame[x][y] = ChangeDelay[x][y];
3047 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3048 DrawLevelGraphicAnimation(x, y, graphic);
3052 static void ResetGfxAnimation(int x, int y)
3055 int element, graphic;
3058 GfxAction[x][y] = ACTION_DEFAULT;
3059 GfxDir[x][y] = MovDir[x][y];
3063 element = Feld[x][y];
3064 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3066 if (graphic_info[graphic].anim_global_sync)
3067 GfxFrame[x][y] = FrameCounter;
3068 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3069 GfxFrame[x][y] = CustomValue[x][y];
3070 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3071 GfxFrame[x][y] = element_info[element].collect_score;
3072 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3073 GfxFrame[x][y] = ChangeDelay[x][y];
3076 #if USE_GFX_RESET_GFX_ANIMATION
3077 ResetGfxFrame(x, y, FALSE);
3081 static void ResetRandomAnimationValue(int x, int y)
3083 GfxRandom[x][y] = INIT_GFX_RANDOM();
3086 void InitMovingField(int x, int y, int direction)
3088 int element = Feld[x][y];
3092 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3093 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3097 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3098 ResetGfxAnimation(x, y);
3100 MovDir[x][y] = direction;
3101 GfxDir[x][y] = direction;
3102 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3103 ACTION_FALLING : ACTION_MOVING);
3106 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3108 if (graphic_info[graphic].anim_global_sync)
3109 GfxFrame[x][y] = FrameCounter;
3110 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3111 GfxFrame[x][y] = CustomValue[x][y];
3112 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3113 GfxFrame[x][y] = element_info[element].collect_score;
3114 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3115 GfxFrame[x][y] = ChangeDelay[x][y];
3118 /* this is needed for CEs with property "can move" / "not moving" */
3120 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3122 if (Feld[newx][newy] == EL_EMPTY)
3123 Feld[newx][newy] = EL_BLOCKED;
3125 MovDir[newx][newy] = MovDir[x][y];
3127 #if USE_NEW_CUSTOM_VALUE
3128 CustomValue[newx][newy] = CustomValue[x][y];
3131 GfxFrame[newx][newy] = GfxFrame[x][y];
3132 GfxRandom[newx][newy] = GfxRandom[x][y];
3133 GfxAction[newx][newy] = GfxAction[x][y];
3134 GfxDir[newx][newy] = GfxDir[x][y];
3138 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3140 int direction = MovDir[x][y];
3142 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3143 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3145 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3146 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3153 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3155 int oldx = x, oldy = y;
3156 int direction = MovDir[x][y];
3158 if (direction == MV_LEFT)
3160 else if (direction == MV_RIGHT)
3162 else if (direction == MV_UP)
3164 else if (direction == MV_DOWN)
3167 *comes_from_x = oldx;
3168 *comes_from_y = oldy;
3171 int MovingOrBlocked2Element(int x, int y)
3173 int element = Feld[x][y];
3175 if (element == EL_BLOCKED)
3179 Blocked2Moving(x, y, &oldx, &oldy);
3180 return Feld[oldx][oldy];
3186 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3188 /* like MovingOrBlocked2Element(), but if element is moving
3189 and (x,y) is the field the moving element is just leaving,
3190 return EL_BLOCKED instead of the element value */
3191 int element = Feld[x][y];
3193 if (IS_MOVING(x, y))
3195 if (element == EL_BLOCKED)
3199 Blocked2Moving(x, y, &oldx, &oldy);
3200 return Feld[oldx][oldy];
3209 static void RemoveField(int x, int y)
3211 Feld[x][y] = EL_EMPTY;
3217 #if USE_NEW_CUSTOM_VALUE
3218 CustomValue[x][y] = 0;
3222 ChangeDelay[x][y] = 0;
3223 ChangePage[x][y] = -1;
3224 Pushed[x][y] = FALSE;
3227 ExplodeField[x][y] = EX_TYPE_NONE;
3230 GfxElement[x][y] = EL_UNDEFINED;
3231 GfxAction[x][y] = ACTION_DEFAULT;
3232 GfxDir[x][y] = MV_NONE;
3235 void RemoveMovingField(int x, int y)
3237 int oldx = x, oldy = y, newx = x, newy = y;
3238 int element = Feld[x][y];
3239 int next_element = EL_UNDEFINED;
3241 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3244 if (IS_MOVING(x, y))
3246 Moving2Blocked(x, y, &newx, &newy);
3248 if (Feld[newx][newy] != EL_BLOCKED)
3250 /* element is moving, but target field is not free (blocked), but
3251 already occupied by something different (example: acid pool);
3252 in this case, only remove the moving field, but not the target */
3254 RemoveField(oldx, oldy);
3256 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3258 DrawLevelField(oldx, oldy);
3263 else if (element == EL_BLOCKED)
3265 Blocked2Moving(x, y, &oldx, &oldy);
3266 if (!IS_MOVING(oldx, oldy))
3270 if (element == EL_BLOCKED &&
3271 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3272 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3273 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3274 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3275 next_element = get_next_element(Feld[oldx][oldy]);
3277 RemoveField(oldx, oldy);
3278 RemoveField(newx, newy);
3280 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3282 if (next_element != EL_UNDEFINED)
3283 Feld[oldx][oldy] = next_element;
3285 DrawLevelField(oldx, oldy);
3286 DrawLevelField(newx, newy);
3289 void DrawDynamite(int x, int y)
3291 int sx = SCREENX(x), sy = SCREENY(y);
3292 int graphic = el2img(Feld[x][y]);
3295 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3298 if (IS_WALKABLE_INSIDE(Back[x][y]))
3302 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3303 else if (Store[x][y])
3304 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3306 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3308 if (Back[x][y] || Store[x][y])
3309 DrawGraphicThruMask(sx, sy, graphic, frame);
3311 DrawGraphic(sx, sy, graphic, frame);
3314 void CheckDynamite(int x, int y)
3316 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3320 if (MovDelay[x][y] != 0)
3323 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3329 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3336 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3338 boolean num_checked_players = 0;
3341 for (i = 0; i < MAX_PLAYERS; i++)
3343 if (stored_player[i].active)
3345 int sx = stored_player[i].jx;
3346 int sy = stored_player[i].jy;
3348 if (num_checked_players == 0)
3355 *sx1 = MIN(*sx1, sx);
3356 *sy1 = MIN(*sy1, sy);
3357 *sx2 = MAX(*sx2, sx);
3358 *sy2 = MAX(*sy2, sy);
3361 num_checked_players++;
3366 static boolean checkIfAllPlayersFitToScreen_RND()
3368 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3370 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3372 return (sx2 - sx1 < SCR_FIELDX &&
3373 sy2 - sy1 < SCR_FIELDY);
3376 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3378 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3380 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3382 *sx = (sx1 + sx2) / 2;
3383 *sy = (sy1 + sy2) / 2;
3387 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3388 int center_x, int center_y)
3390 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3392 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3394 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3395 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3398 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3402 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3404 return (max_dx <= SCR_FIELDX / 2 &&
3405 max_dy <= SCR_FIELDY / 2);
3413 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3414 boolean quick_relocation)
3416 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3417 boolean no_delay = (tape.warp_forward);
3418 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3419 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3421 if (quick_relocation)
3423 int offset = (setup.scroll_delay ? 3 : 0);
3430 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3432 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3433 x > SBX_Right + MIDPOSX ? SBX_Right :
3436 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3437 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3442 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3443 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3444 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3446 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3447 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3448 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3450 /* don't scroll over playfield boundaries */
3451 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3452 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3454 /* don't scroll over playfield boundaries */
3455 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3456 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3459 RedrawPlayfield(TRUE, 0,0,0,0);
3463 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3464 x > SBX_Right + MIDPOSX ? SBX_Right :
3467 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3468 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3471 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3473 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3476 int fx = FX, fy = FY;
3478 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3479 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3481 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3487 fx += dx * TILEX / 2;
3488 fy += dy * TILEY / 2;
3490 ScrollLevel(dx, dy);
3493 /* scroll in two steps of half tile size to make things smoother */
3494 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3496 Delay(wait_delay_value);
3498 /* scroll second step to align at full tile size */
3500 Delay(wait_delay_value);
3505 Delay(wait_delay_value);
3511 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3513 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3514 boolean no_delay = (tape.warp_forward);
3515 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3516 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3517 int jx = player->jx;
3518 int jy = player->jy;
3520 if (quick_relocation)
3522 int offset = (setup.scroll_delay ? 3 : 0);
3524 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3526 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3527 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3528 player->jx - MIDPOSX);
3530 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3531 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3532 player->jy - MIDPOSY);
3536 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3537 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3538 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3540 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3541 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3542 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3544 /* don't scroll over playfield boundaries */
3545 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3546 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3548 /* don't scroll over playfield boundaries */
3549 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3550 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3553 RedrawPlayfield(TRUE, 0,0,0,0);
3557 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3558 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3559 player->jx - MIDPOSX);
3561 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3562 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3563 player->jy - MIDPOSY);
3565 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3567 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3570 int fx = FX, fy = FY;
3572 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3573 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3575 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3581 fx += dx * TILEX / 2;
3582 fy += dy * TILEY / 2;
3584 ScrollLevel(dx, dy);
3587 /* scroll in two steps of half tile size to make things smoother */
3588 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3590 Delay(wait_delay_value);
3592 /* scroll second step to align at full tile size */
3594 Delay(wait_delay_value);
3599 Delay(wait_delay_value);
3605 void RelocatePlayer(int jx, int jy, int el_player_raw)
3607 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3608 int player_nr = GET_PLAYER_NR(el_player);
3609 struct PlayerInfo *player = &stored_player[player_nr];
3610 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3611 boolean no_delay = (tape.warp_forward);
3612 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3613 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3614 int old_jx = player->jx;
3615 int old_jy = player->jy;
3616 int old_element = Feld[old_jx][old_jy];
3617 int element = Feld[jx][jy];
3618 boolean player_relocated = (old_jx != jx || old_jy != jy);
3620 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3621 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3622 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3623 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3624 int leave_side_horiz = move_dir_horiz;
3625 int leave_side_vert = move_dir_vert;
3626 int enter_side = enter_side_horiz | enter_side_vert;
3627 int leave_side = leave_side_horiz | leave_side_vert;
3629 if (player->GameOver) /* do not reanimate dead player */
3632 if (!player_relocated) /* no need to relocate the player */
3635 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3637 RemoveField(jx, jy); /* temporarily remove newly placed player */
3638 DrawLevelField(jx, jy);
3641 if (player->present)
3643 while (player->MovPos)
3645 ScrollPlayer(player, SCROLL_GO_ON);
3646 ScrollScreen(NULL, SCROLL_GO_ON);
3648 AdvanceFrameAndPlayerCounters(player->index_nr);
3653 Delay(wait_delay_value);
3656 DrawPlayer(player); /* needed here only to cleanup last field */
3657 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3659 player->is_moving = FALSE;
3662 if (IS_CUSTOM_ELEMENT(old_element))
3663 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3665 player->index_bit, leave_side);
3667 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3669 player->index_bit, leave_side);
3671 Feld[jx][jy] = el_player;
3672 InitPlayerField(jx, jy, el_player, TRUE);
3674 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3676 Feld[jx][jy] = element;
3677 InitField(jx, jy, FALSE);
3681 /* only visually relocate centered player */
3683 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3684 level.instant_relocation);
3686 if (player->index_nr == game.centered_player_nr)
3687 DrawRelocatePlayer(player, level.instant_relocation);
3690 if (player == local_player) /* only visually relocate local player */
3691 DrawRelocatePlayer(player, level.instant_relocation);
3694 TestIfPlayerTouchesBadThing(jx, jy);
3695 TestIfPlayerTouchesCustomElement(jx, jy);
3697 if (IS_CUSTOM_ELEMENT(element))
3698 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3699 player->index_bit, enter_side);
3701 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3702 player->index_bit, enter_side);
3705 void Explode(int ex, int ey, int phase, int mode)
3711 /* !!! eliminate this variable !!! */
3712 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3714 if (game.explosions_delayed)
3716 ExplodeField[ex][ey] = mode;
3720 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3722 int center_element = Feld[ex][ey];
3723 int artwork_element, explosion_element; /* set these values later */
3726 /* --- This is only really needed (and now handled) in "Impact()". --- */
3727 /* do not explode moving elements that left the explode field in time */
3728 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3729 center_element == EL_EMPTY &&
3730 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3735 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3736 if (mode == EX_TYPE_NORMAL ||
3737 mode == EX_TYPE_CENTER ||
3738 mode == EX_TYPE_CROSS)
3739 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3742 /* remove things displayed in background while burning dynamite */
3743 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3746 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3748 /* put moving element to center field (and let it explode there) */
3749 center_element = MovingOrBlocked2Element(ex, ey);
3750 RemoveMovingField(ex, ey);
3751 Feld[ex][ey] = center_element;
3754 /* now "center_element" is finally determined -- set related values now */
3755 artwork_element = center_element; /* for custom player artwork */
3756 explosion_element = center_element; /* for custom player artwork */
3758 if (IS_PLAYER(ex, ey))
3760 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3762 artwork_element = stored_player[player_nr].artwork_element;
3764 if (level.use_explosion_element[player_nr])
3766 explosion_element = level.explosion_element[player_nr];
3767 artwork_element = explosion_element;
3772 if (mode == EX_TYPE_NORMAL ||
3773 mode == EX_TYPE_CENTER ||
3774 mode == EX_TYPE_CROSS)
3775 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3779 last_phase = element_info[explosion_element].explosion_delay + 1;
3781 last_phase = element_info[center_element].explosion_delay + 1;
3784 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3786 int xx = x - ex + 1;
3787 int yy = y - ey + 1;
3790 if (!IN_LEV_FIELD(x, y) ||
3791 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3792 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3795 element = Feld[x][y];
3797 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3799 element = MovingOrBlocked2Element(x, y);
3801 if (!IS_EXPLOSION_PROOF(element))
3802 RemoveMovingField(x, y);
3805 /* indestructible elements can only explode in center (but not flames) */
3806 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3807 mode == EX_TYPE_BORDER)) ||
3808 element == EL_FLAMES)
3811 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3812 behaviour, for example when touching a yamyam that explodes to rocks
3813 with active deadly shield, a rock is created under the player !!! */
3814 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3816 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3817 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3818 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3820 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3823 if (IS_ACTIVE_BOMB(element))
3825 /* re-activate things under the bomb like gate or penguin */
3826 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3833 /* save walkable background elements while explosion on same tile */
3834 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3835 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3836 Back[x][y] = element;
3838 /* ignite explodable elements reached by other explosion */
3839 if (element == EL_EXPLOSION)
3840 element = Store2[x][y];
3842 if (AmoebaNr[x][y] &&
3843 (element == EL_AMOEBA_FULL ||
3844 element == EL_BD_AMOEBA ||
3845 element == EL_AMOEBA_GROWING))
3847 AmoebaCnt[AmoebaNr[x][y]]--;
3848 AmoebaCnt2[AmoebaNr[x][y]]--;
3853 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3856 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3858 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3860 switch(StorePlayer[ex][ey])
3863 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3866 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3869 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3873 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3878 if (PLAYERINFO(ex, ey)->use_murphy)
3879 Store[x][y] = EL_EMPTY;
3882 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3883 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3884 else if (ELEM_IS_PLAYER(center_element))
3885 Store[x][y] = EL_EMPTY;
3886 else if (center_element == EL_YAMYAM)
3887 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3888 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3889 Store[x][y] = element_info[center_element].content.e[xx][yy];
3891 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3892 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3893 otherwise) -- FIX THIS !!! */
3894 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3895 Store[x][y] = element_info[element].content.e[1][1];
3897 else if (!CAN_EXPLODE(element))
3898 Store[x][y] = element_info[element].content.e[1][1];
3901 Store[x][y] = EL_EMPTY;
3903 else if (center_element == EL_MOLE)
3904 Store[x][y] = EL_EMERALD_RED;
3905 else if (center_element == EL_PENGUIN)
3906 Store[x][y] = EL_EMERALD_PURPLE;
3907 else if (center_element == EL_BUG)
3908 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3909 else if (center_element == EL_BD_BUTTERFLY)
3910 Store[x][y] = EL_BD_DIAMOND;
3911 else if (center_element == EL_SP_ELECTRON)
3912 Store[x][y] = EL_SP_INFOTRON;
3913 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3914 Store[x][y] = level.amoeba_content;
3915 else if (center_element == EL_YAMYAM)
3916 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3917 else if (IS_CUSTOM_ELEMENT(center_element) &&
3918 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3919 Store[x][y] = element_info[center_element].content.e[xx][yy];
3920 else if (element == EL_WALL_EMERALD)
3921 Store[x][y] = EL_EMERALD;
3922 else if (element == EL_WALL_DIAMOND)
3923 Store[x][y] = EL_DIAMOND;
3924 else if (element == EL_WALL_BD_DIAMOND)
3925 Store[x][y] = EL_BD_DIAMOND;
3926 else if (element == EL_WALL_EMERALD_YELLOW)
3927 Store[x][y] = EL_EMERALD_YELLOW;
3928 else if (element == EL_WALL_EMERALD_RED)
3929 Store[x][y] = EL_EMERALD_RED;
3930 else if (element == EL_WALL_EMERALD_PURPLE)
3931 Store[x][y] = EL_EMERALD_PURPLE;
3932 else if (element == EL_WALL_PEARL)
3933 Store[x][y] = EL_PEARL;
3934 else if (element == EL_WALL_CRYSTAL)
3935 Store[x][y] = EL_CRYSTAL;
3936 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3937 Store[x][y] = element_info[element].content.e[1][1];
3939 Store[x][y] = EL_EMPTY;
3942 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3943 center_element == EL_AMOEBA_TO_DIAMOND)
3944 Store2[x][y] = element;
3946 Feld[x][y] = EL_EXPLOSION;
3947 GfxElement[x][y] = artwork_element;
3950 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3951 x, y, artwork_element, EL_NAME(artwork_element));
3954 ExplodePhase[x][y] = 1;
3955 ExplodeDelay[x][y] = last_phase;
3960 if (center_element == EL_YAMYAM)
3961 game.yamyam_content_nr =
3962 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3974 GfxFrame[x][y] = 0; /* restart explosion animation */
3976 last_phase = ExplodeDelay[x][y];
3978 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3982 /* activate this even in non-DEBUG version until cause for crash in
3983 getGraphicAnimationFrame() (see below) is found and eliminated */
3989 /* this can happen if the player leaves an explosion just in time */
3990 if (GfxElement[x][y] == EL_UNDEFINED)
3991 GfxElement[x][y] = EL_EMPTY;
3993 if (GfxElement[x][y] == EL_UNDEFINED)
3996 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3997 printf("Explode(): This should never happen!\n");
4000 GfxElement[x][y] = EL_EMPTY;
4006 border_element = Store2[x][y];
4007 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4008 border_element = StorePlayer[x][y];
4010 if (phase == element_info[border_element].ignition_delay ||
4011 phase == last_phase)
4013 boolean border_explosion = FALSE;
4015 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4016 !PLAYER_EXPLOSION_PROTECTED(x, y))
4018 KillPlayerUnlessExplosionProtected(x, y);
4019 border_explosion = TRUE;
4021 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4023 Feld[x][y] = Store2[x][y];
4026 border_explosion = TRUE;
4028 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4030 AmoebeUmwandeln(x, y);
4032 border_explosion = TRUE;
4035 /* if an element just explodes due to another explosion (chain-reaction),
4036 do not immediately end the new explosion when it was the last frame of
4037 the explosion (as it would be done in the following "if"-statement!) */
4038 if (border_explosion && phase == last_phase)
4042 if (phase == last_phase)
4046 element = Feld[x][y] = Store[x][y];
4047 Store[x][y] = Store2[x][y] = 0;
4048 GfxElement[x][y] = EL_UNDEFINED;
4050 /* player can escape from explosions and might therefore be still alive */
4051 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4052 element <= EL_PLAYER_IS_EXPLODING_4)
4054 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4055 int explosion_element = EL_PLAYER_1 + player_nr;
4056 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4057 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4059 if (level.use_explosion_element[player_nr])
4060 explosion_element = level.explosion_element[player_nr];
4062 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4063 element_info[explosion_element].content.e[xx][yy]);
4066 /* restore probably existing indestructible background element */
4067 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4068 element = Feld[x][y] = Back[x][y];
4071 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4072 GfxDir[x][y] = MV_NONE;
4073 ChangeDelay[x][y] = 0;
4074 ChangePage[x][y] = -1;
4076 #if USE_NEW_CUSTOM_VALUE
4077 CustomValue[x][y] = 0;
4080 InitField_WithBug2(x, y, FALSE);
4082 DrawLevelField(x, y);
4084 TestIfElementTouchesCustomElement(x, y);
4086 if (GFX_CRUMBLED(element))
4087 DrawLevelFieldCrumbledSandNeighbours(x, y);
4089 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4090 StorePlayer[x][y] = 0;
4092 if (ELEM_IS_PLAYER(element))
4093 RelocatePlayer(x, y, element);
4095 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4097 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4098 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4101 DrawLevelFieldCrumbledSand(x, y);
4103 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4105 DrawLevelElement(x, y, Back[x][y]);
4106 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4108 else if (IS_WALKABLE_UNDER(Back[x][y]))
4110 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4111 DrawLevelElementThruMask(x, y, Back[x][y]);
4113 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4114 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4118 void DynaExplode(int ex, int ey)
4121 int dynabomb_element = Feld[ex][ey];
4122 int dynabomb_size = 1;
4123 boolean dynabomb_xl = FALSE;
4124 struct PlayerInfo *player;
4125 static int xy[4][2] =
4133 if (IS_ACTIVE_BOMB(dynabomb_element))
4135 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4136 dynabomb_size = player->dynabomb_size;
4137 dynabomb_xl = player->dynabomb_xl;
4138 player->dynabombs_left++;
4141 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4143 for (i = 0; i < NUM_DIRECTIONS; i++)
4145 for (j = 1; j <= dynabomb_size; j++)
4147 int x = ex + j * xy[i][0];
4148 int y = ey + j * xy[i][1];
4151 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4154 element = Feld[x][y];
4156 /* do not restart explosions of fields with active bombs */
4157 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4160 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4162 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4163 !IS_DIGGABLE(element) && !dynabomb_xl)
4169 void Bang(int x, int y)
4171 int element = MovingOrBlocked2Element(x, y);
4172 int explosion_type = EX_TYPE_NORMAL;
4174 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4176 struct PlayerInfo *player = PLAYERINFO(x, y);
4178 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4179 player->element_nr);
4181 if (level.use_explosion_element[player->index_nr])
4183 int explosion_element = level.explosion_element[player->index_nr];
4185 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4186 explosion_type = EX_TYPE_CROSS;
4187 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4188 explosion_type = EX_TYPE_CENTER;
4196 case EL_BD_BUTTERFLY:
4199 case EL_DARK_YAMYAM:
4203 RaiseScoreElement(element);
4206 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4207 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4208 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4209 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4210 case EL_DYNABOMB_INCREASE_NUMBER:
4211 case EL_DYNABOMB_INCREASE_SIZE:
4212 case EL_DYNABOMB_INCREASE_POWER:
4213 explosion_type = EX_TYPE_DYNA;
4218 case EL_LAMP_ACTIVE:
4219 case EL_AMOEBA_TO_DIAMOND:
4220 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4221 explosion_type = EX_TYPE_CENTER;
4225 if (element_info[element].explosion_type == EXPLODES_CROSS)
4226 explosion_type = EX_TYPE_CROSS;
4227 else if (element_info[element].explosion_type == EXPLODES_1X1)
4228 explosion_type = EX_TYPE_CENTER;
4232 if (explosion_type == EX_TYPE_DYNA)
4235 Explode(x, y, EX_PHASE_START, explosion_type);
4237 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4240 void SplashAcid(int x, int y)
4242 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4243 (!IN_LEV_FIELD(x - 1, y - 2) ||
4244 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4245 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4247 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4248 (!IN_LEV_FIELD(x + 1, y - 2) ||
4249 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4250 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4252 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4255 static void InitBeltMovement()
4257 static int belt_base_element[4] =
4259 EL_CONVEYOR_BELT_1_LEFT,
4260 EL_CONVEYOR_BELT_2_LEFT,
4261 EL_CONVEYOR_BELT_3_LEFT,
4262 EL_CONVEYOR_BELT_4_LEFT
4264 static int belt_base_active_element[4] =
4266 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4267 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4268 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4269 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4274 /* set frame order for belt animation graphic according to belt direction */
4275 for (i = 0; i < NUM_BELTS; i++)
4279 for (j = 0; j < NUM_BELT_PARTS; j++)
4281 int element = belt_base_active_element[belt_nr] + j;
4282 int graphic = el2img(element);
4284 if (game.belt_dir[i] == MV_LEFT)
4285 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4287 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4292 SCAN_PLAYFIELD(x, y)
4294 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4297 int element = Feld[x][y];
4299 for (i = 0; i < NUM_BELTS; i++)
4301 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4303 int e_belt_nr = getBeltNrFromBeltElement(element);
4306 if (e_belt_nr == belt_nr)
4308 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4310 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4317 static void ToggleBeltSwitch(int x, int y)
4319 static int belt_base_element[4] =
4321 EL_CONVEYOR_BELT_1_LEFT,
4322 EL_CONVEYOR_BELT_2_LEFT,
4323 EL_CONVEYOR_BELT_3_LEFT,
4324 EL_CONVEYOR_BELT_4_LEFT
4326 static int belt_base_active_element[4] =
4328 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4329 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4330 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4331 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4333 static int belt_base_switch_element[4] =
4335 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4336 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4337 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4338 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4340 static int belt_move_dir[4] =
4348 int element = Feld[x][y];
4349 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4350 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4351 int belt_dir = belt_move_dir[belt_dir_nr];
4354 if (!IS_BELT_SWITCH(element))
4357 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4358 game.belt_dir[belt_nr] = belt_dir;
4360 if (belt_dir_nr == 3)
4363 /* set frame order for belt animation graphic according to belt direction */
4364 for (i = 0; i < NUM_BELT_PARTS; i++)
4366 int element = belt_base_active_element[belt_nr] + i;
4367 int graphic = el2img(element);
4369 if (belt_dir == MV_LEFT)
4370 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4372 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4376 SCAN_PLAYFIELD(xx, yy)
4378 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4381 int element = Feld[xx][yy];
4383 if (IS_BELT_SWITCH(element))
4385 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4387 if (e_belt_nr == belt_nr)
4389 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4390 DrawLevelField(xx, yy);
4393 else if (IS_BELT(element) && belt_dir != MV_NONE)
4395 int e_belt_nr = getBeltNrFromBeltElement(element);
4397 if (e_belt_nr == belt_nr)
4399 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4401 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4402 DrawLevelField(xx, yy);
4405 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4407 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4409 if (e_belt_nr == belt_nr)
4411 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4413 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4414 DrawLevelField(xx, yy);
4420 static void ToggleSwitchgateSwitch(int x, int y)
4424 game.switchgate_pos = !game.switchgate_pos;
4427 SCAN_PLAYFIELD(xx, yy)
4429 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4432 int element = Feld[xx][yy];
4434 #if !USE_BOTH_SWITCHGATE_SWITCHES
4435 if (element == EL_SWITCHGATE_SWITCH_UP ||
4436 element == EL_SWITCHGATE_SWITCH_DOWN)
4438 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4439 DrawLevelField(xx, yy);
4442 if (element == EL_SWITCHGATE_SWITCH_UP)
4444 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4445 DrawLevelField(xx, yy);
4447 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4449 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4450 DrawLevelField(xx, yy);
4453 else if (element == EL_SWITCHGATE_OPEN ||
4454 element == EL_SWITCHGATE_OPENING)
4456 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4458 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4460 else if (element == EL_SWITCHGATE_CLOSED ||
4461 element == EL_SWITCHGATE_CLOSING)
4463 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4465 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4470 static int getInvisibleActiveFromInvisibleElement(int element)
4472 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4473 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4474 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4478 static int getInvisibleFromInvisibleActiveElement(int element)
4480 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4481 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4482 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4486 static void RedrawAllLightSwitchesAndInvisibleElements()
4491 SCAN_PLAYFIELD(x, y)
4493 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4496 int element = Feld[x][y];
4498 if (element == EL_LIGHT_SWITCH &&
4499 game.light_time_left > 0)
4501 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4502 DrawLevelField(x, y);
4504 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4505 game.light_time_left == 0)
4507 Feld[x][y] = EL_LIGHT_SWITCH;
4508 DrawLevelField(x, y);
4510 else if (element == EL_EMC_DRIPPER &&
4511 game.light_time_left > 0)
4513 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4514 DrawLevelField(x, y);
4516 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4517 game.light_time_left == 0)
4519 Feld[x][y] = EL_EMC_DRIPPER;
4520 DrawLevelField(x, y);
4522 else if (element == EL_INVISIBLE_STEELWALL ||
4523 element == EL_INVISIBLE_WALL ||
4524 element == EL_INVISIBLE_SAND)
4526 if (game.light_time_left > 0)
4527 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4529 DrawLevelField(x, y);
4531 /* uncrumble neighbour fields, if needed */
4532 if (element == EL_INVISIBLE_SAND)
4533 DrawLevelFieldCrumbledSandNeighbours(x, y);
4535 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4536 element == EL_INVISIBLE_WALL_ACTIVE ||
4537 element == EL_INVISIBLE_SAND_ACTIVE)
4539 if (game.light_time_left == 0)
4540 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4542 DrawLevelField(x, y);
4544 /* re-crumble neighbour fields, if needed */
4545 if (element == EL_INVISIBLE_SAND)
4546 DrawLevelFieldCrumbledSandNeighbours(x, y);
4551 static void RedrawAllInvisibleElementsForLenses()
4556 SCAN_PLAYFIELD(x, y)
4558 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4561 int element = Feld[x][y];
4563 if (element == EL_EMC_DRIPPER &&
4564 game.lenses_time_left > 0)
4566 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4567 DrawLevelField(x, y);
4569 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4570 game.lenses_time_left == 0)
4572 Feld[x][y] = EL_EMC_DRIPPER;
4573 DrawLevelField(x, y);
4575 else if (element == EL_INVISIBLE_STEELWALL ||
4576 element == EL_INVISIBLE_WALL ||
4577 element == EL_INVISIBLE_SAND)
4579 if (game.lenses_time_left > 0)
4580 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4582 DrawLevelField(x, y);
4584 /* uncrumble neighbour fields, if needed */
4585 if (element == EL_INVISIBLE_SAND)
4586 DrawLevelFieldCrumbledSandNeighbours(x, y);
4588 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4589 element == EL_INVISIBLE_WALL_ACTIVE ||
4590 element == EL_INVISIBLE_SAND_ACTIVE)
4592 if (game.lenses_time_left == 0)
4593 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4595 DrawLevelField(x, y);
4597 /* re-crumble neighbour fields, if needed */
4598 if (element == EL_INVISIBLE_SAND)
4599 DrawLevelFieldCrumbledSandNeighbours(x, y);
4604 static void RedrawAllInvisibleElementsForMagnifier()
4609 SCAN_PLAYFIELD(x, y)
4611 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4614 int element = Feld[x][y];
4616 if (element == EL_EMC_FAKE_GRASS &&
4617 game.magnify_time_left > 0)
4619 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4620 DrawLevelField(x, y);
4622 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4623 game.magnify_time_left == 0)
4625 Feld[x][y] = EL_EMC_FAKE_GRASS;
4626 DrawLevelField(x, y);
4628 else if (IS_GATE_GRAY(element) &&
4629 game.magnify_time_left > 0)
4631 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4632 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4633 IS_EM_GATE_GRAY(element) ?
4634 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4635 IS_EMC_GATE_GRAY(element) ?
4636 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4638 DrawLevelField(x, y);
4640 else if (IS_GATE_GRAY_ACTIVE(element) &&
4641 game.magnify_time_left == 0)
4643 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4644 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4645 IS_EM_GATE_GRAY_ACTIVE(element) ?
4646 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4647 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4648 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4650 DrawLevelField(x, y);
4655 static void ToggleLightSwitch(int x, int y)
4657 int element = Feld[x][y];
4659 game.light_time_left =
4660 (element == EL_LIGHT_SWITCH ?
4661 level.time_light * FRAMES_PER_SECOND : 0);
4663 RedrawAllLightSwitchesAndInvisibleElements();
4666 static void ActivateTimegateSwitch(int x, int y)
4670 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4673 SCAN_PLAYFIELD(xx, yy)
4675 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4678 int element = Feld[xx][yy];
4680 if (element == EL_TIMEGATE_CLOSED ||
4681 element == EL_TIMEGATE_CLOSING)
4683 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4684 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4688 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4690 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4691 DrawLevelField(xx, yy);
4697 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4700 void Impact(int x, int y)
4702 boolean last_line = (y == lev_fieldy - 1);
4703 boolean object_hit = FALSE;
4704 boolean impact = (last_line || object_hit);
4705 int element = Feld[x][y];
4706 int smashed = EL_STEELWALL;
4708 if (!last_line) /* check if element below was hit */
4710 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4713 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4714 MovDir[x][y + 1] != MV_DOWN ||
4715 MovPos[x][y + 1] <= TILEY / 2));
4717 /* do not smash moving elements that left the smashed field in time */
4718 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4719 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4722 #if USE_QUICKSAND_IMPACT_BUGFIX
4723 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4725 RemoveMovingField(x, y + 1);
4726 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4727 Feld[x][y + 2] = EL_ROCK;
4728 DrawLevelField(x, y + 2);
4735 smashed = MovingOrBlocked2Element(x, y + 1);
4737 impact = (last_line || object_hit);
4740 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4742 SplashAcid(x, y + 1);
4746 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4747 /* only reset graphic animation if graphic really changes after impact */
4749 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4751 ResetGfxAnimation(x, y);
4752 DrawLevelField(x, y);
4755 if (impact && CAN_EXPLODE_IMPACT(element))
4760 else if (impact && element == EL_PEARL)
4762 ResetGfxAnimation(x, y);
4764 Feld[x][y] = EL_PEARL_BREAKING;
4765 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4768 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4770 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4775 if (impact && element == EL_AMOEBA_DROP)
4777 if (object_hit && IS_PLAYER(x, y + 1))
4778 KillPlayerUnlessEnemyProtected(x, y + 1);
4779 else if (object_hit && smashed == EL_PENGUIN)
4783 Feld[x][y] = EL_AMOEBA_GROWING;
4784 Store[x][y] = EL_AMOEBA_WET;
4786 ResetRandomAnimationValue(x, y);
4791 if (object_hit) /* check which object was hit */
4793 if (CAN_PASS_MAGIC_WALL(element) &&
4794 (smashed == EL_MAGIC_WALL ||
4795 smashed == EL_BD_MAGIC_WALL))
4798 int activated_magic_wall =
4799 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4800 EL_BD_MAGIC_WALL_ACTIVE);
4802 /* activate magic wall / mill */
4804 SCAN_PLAYFIELD(xx, yy)
4806 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4808 if (Feld[xx][yy] == smashed)
4809 Feld[xx][yy] = activated_magic_wall;
4811 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4812 game.magic_wall_active = TRUE;
4814 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4815 SND_MAGIC_WALL_ACTIVATING :
4816 SND_BD_MAGIC_WALL_ACTIVATING));
4819 if (IS_PLAYER(x, y + 1))
4821 if (CAN_SMASH_PLAYER(element))
4823 KillPlayerUnlessEnemyProtected(x, y + 1);
4827 else if (smashed == EL_PENGUIN)
4829 if (CAN_SMASH_PLAYER(element))
4835 else if (element == EL_BD_DIAMOND)
4837 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4843 else if (((element == EL_SP_INFOTRON ||
4844 element == EL_SP_ZONK) &&
4845 (smashed == EL_SP_SNIKSNAK ||
4846 smashed == EL_SP_ELECTRON ||
4847 smashed == EL_SP_DISK_ORANGE)) ||
4848 (element == EL_SP_INFOTRON &&
4849 smashed == EL_SP_DISK_YELLOW))
4854 else if (CAN_SMASH_EVERYTHING(element))
4856 if (IS_CLASSIC_ENEMY(smashed) ||
4857 CAN_EXPLODE_SMASHED(smashed))
4862 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4864 if (smashed == EL_LAMP ||
4865 smashed == EL_LAMP_ACTIVE)
4870 else if (smashed == EL_NUT)
4872 Feld[x][y + 1] = EL_NUT_BREAKING;
4873 PlayLevelSound(x, y, SND_NUT_BREAKING);
4874 RaiseScoreElement(EL_NUT);
4877 else if (smashed == EL_PEARL)
4879 ResetGfxAnimation(x, y);
4881 Feld[x][y + 1] = EL_PEARL_BREAKING;
4882 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4885 else if (smashed == EL_DIAMOND)
4887 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4888 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4891 else if (IS_BELT_SWITCH(smashed))
4893 ToggleBeltSwitch(x, y + 1);
4895 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4896 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4898 ToggleSwitchgateSwitch(x, y + 1);
4900 else if (smashed == EL_LIGHT_SWITCH ||
4901 smashed == EL_LIGHT_SWITCH_ACTIVE)
4903 ToggleLightSwitch(x, y + 1);
4908 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4911 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4913 CheckElementChangeBySide(x, y + 1, smashed, element,
4914 CE_SWITCHED, CH_SIDE_TOP);
4915 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4921 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4926 /* play sound of magic wall / mill */
4928 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4929 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4931 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4932 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4933 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4934 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4939 /* play sound of object that hits the ground */
4940 if (last_line || object_hit)
4941 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4944 inline static void TurnRoundExt(int x, int y)
4956 { 0, 0 }, { 0, 0 }, { 0, 0 },
4961 int left, right, back;
4965 { MV_DOWN, MV_UP, MV_RIGHT },
4966 { MV_UP, MV_DOWN, MV_LEFT },
4968 { MV_LEFT, MV_RIGHT, MV_DOWN },
4972 { MV_RIGHT, MV_LEFT, MV_UP }
4975 int element = Feld[x][y];
4976 int move_pattern = element_info[element].move_pattern;
4978 int old_move_dir = MovDir[x][y];
4979 int left_dir = turn[old_move_dir].left;
4980 int right_dir = turn[old_move_dir].right;
4981 int back_dir = turn[old_move_dir].back;
4983 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4984 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4985 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4986 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4988 int left_x = x + left_dx, left_y = y + left_dy;
4989 int right_x = x + right_dx, right_y = y + right_dy;
4990 int move_x = x + move_dx, move_y = y + move_dy;
4994 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4996 TestIfBadThingTouchesOtherBadThing(x, y);
4998 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4999 MovDir[x][y] = right_dir;
5000 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5001 MovDir[x][y] = left_dir;
5003 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5005 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5008 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5010 TestIfBadThingTouchesOtherBadThing(x, y);
5012 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5013 MovDir[x][y] = left_dir;
5014 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5015 MovDir[x][y] = right_dir;
5017 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5019 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5022 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5024 TestIfBadThingTouchesOtherBadThing(x, y);
5026 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5027 MovDir[x][y] = left_dir;
5028 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5029 MovDir[x][y] = right_dir;
5031 if (MovDir[x][y] != old_move_dir)
5034 else if (element == EL_YAMYAM)
5036 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5037 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5039 if (can_turn_left && can_turn_right)
5040 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5041 else if (can_turn_left)
5042 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5043 else if (can_turn_right)
5044 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5046 MovDir[x][y] = back_dir;
5048 MovDelay[x][y] = 16 + 16 * RND(3);
5050 else if (element == EL_DARK_YAMYAM)
5052 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5054 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5057 if (can_turn_left && can_turn_right)
5058 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5059 else if (can_turn_left)
5060 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5061 else if (can_turn_right)
5062 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5064 MovDir[x][y] = back_dir;
5066 MovDelay[x][y] = 16 + 16 * RND(3);
5068 else if (element == EL_PACMAN)
5070 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5071 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5073 if (can_turn_left && can_turn_right)
5074 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5075 else if (can_turn_left)
5076 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5077 else if (can_turn_right)
5078 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5080 MovDir[x][y] = back_dir;
5082 MovDelay[x][y] = 6 + RND(40);
5084 else if (element == EL_PIG)
5086 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5087 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5088 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5089 boolean should_turn_left, should_turn_right, should_move_on;
5091 int rnd = RND(rnd_value);
5093 should_turn_left = (can_turn_left &&
5095 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5096 y + back_dy + left_dy)));
5097 should_turn_right = (can_turn_right &&
5099 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5100 y + back_dy + right_dy)));
5101 should_move_on = (can_move_on &&
5104 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5105 y + move_dy + left_dy) ||
5106 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5107 y + move_dy + right_dy)));
5109 if (should_turn_left || should_turn_right || should_move_on)
5111 if (should_turn_left && should_turn_right && should_move_on)
5112 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5113 rnd < 2 * rnd_value / 3 ? right_dir :
5115 else if (should_turn_left && should_turn_right)
5116 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5117 else if (should_turn_left && should_move_on)
5118 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5119 else if (should_turn_right && should_move_on)
5120 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5121 else if (should_turn_left)
5122 MovDir[x][y] = left_dir;
5123 else if (should_turn_right)
5124 MovDir[x][y] = right_dir;
5125 else if (should_move_on)
5126 MovDir[x][y] = old_move_dir;
5128 else if (can_move_on && rnd > rnd_value / 8)
5129 MovDir[x][y] = old_move_dir;
5130 else if (can_turn_left && can_turn_right)
5131 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5132 else if (can_turn_left && rnd > rnd_value / 8)
5133 MovDir[x][y] = left_dir;
5134 else if (can_turn_right && rnd > rnd_value/8)
5135 MovDir[x][y] = right_dir;
5137 MovDir[x][y] = back_dir;
5139 xx = x + move_xy[MovDir[x][y]].dx;
5140 yy = y + move_xy[MovDir[x][y]].dy;
5142 if (!IN_LEV_FIELD(xx, yy) ||
5143 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5144 MovDir[x][y] = old_move_dir;
5148 else if (element == EL_DRAGON)
5150 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5151 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5152 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5154 int rnd = RND(rnd_value);
5156 if (can_move_on && rnd > rnd_value / 8)
5157 MovDir[x][y] = old_move_dir;
5158 else if (can_turn_left && can_turn_right)
5159 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5160 else if (can_turn_left && rnd > rnd_value / 8)
5161 MovDir[x][y] = left_dir;
5162 else if (can_turn_right && rnd > rnd_value / 8)
5163 MovDir[x][y] = right_dir;
5165 MovDir[x][y] = back_dir;
5167 xx = x + move_xy[MovDir[x][y]].dx;
5168 yy = y + move_xy[MovDir[x][y]].dy;
5170 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5171 MovDir[x][y] = old_move_dir;
5175 else if (element == EL_MOLE)
5177 boolean can_move_on =
5178 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5179 IS_AMOEBOID(Feld[move_x][move_y]) ||
5180 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5183 boolean can_turn_left =
5184 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5185 IS_AMOEBOID(Feld[left_x][left_y])));
5187 boolean can_turn_right =
5188 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5189 IS_AMOEBOID(Feld[right_x][right_y])));
5191 if (can_turn_left && can_turn_right)
5192 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5193 else if (can_turn_left)
5194 MovDir[x][y] = left_dir;
5196 MovDir[x][y] = right_dir;
5199 if (MovDir[x][y] != old_move_dir)
5202 else if (element == EL_BALLOON)
5204 MovDir[x][y] = game.wind_direction;
5207 else if (element == EL_SPRING)
5209 #if USE_NEW_SPRING_BUMPER
5210 if (MovDir[x][y] & MV_HORIZONTAL)
5212 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5213 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5215 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5216 ResetGfxAnimation(move_x, move_y);
5217 DrawLevelField(move_x, move_y);
5219 MovDir[x][y] = back_dir;
5221 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5222 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5223 MovDir[x][y] = MV_NONE;
5226 if (MovDir[x][y] & MV_HORIZONTAL &&
5227 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5228 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5229 MovDir[x][y] = MV_NONE;
5234 else if (element == EL_ROBOT ||
5235 element == EL_SATELLITE ||
5236 element == EL_PENGUIN ||
5237 element == EL_EMC_ANDROID)
5239 int attr_x = -1, attr_y = -1;
5250 for (i = 0; i < MAX_PLAYERS; i++)
5252 struct PlayerInfo *player = &stored_player[i];
5253 int jx = player->jx, jy = player->jy;
5255 if (!player->active)
5259 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5267 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5268 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5269 game.engine_version < VERSION_IDENT(3,1,0,0)))
5275 if (element == EL_PENGUIN)
5278 static int xy[4][2] =
5286 for (i = 0; i < NUM_DIRECTIONS; i++)
5288 int ex = x + xy[i][0];
5289 int ey = y + xy[i][1];
5291 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5300 MovDir[x][y] = MV_NONE;
5302 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5303 else if (attr_x > x)
5304 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5306 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5307 else if (attr_y > y)
5308 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5310 if (element == EL_ROBOT)
5314 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5315 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5316 Moving2Blocked(x, y, &newx, &newy);
5318 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5319 MovDelay[x][y] = 8 + 8 * !RND(3);
5321 MovDelay[x][y] = 16;
5323 else if (element == EL_PENGUIN)
5329 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5331 boolean first_horiz = RND(2);
5332 int new_move_dir = MovDir[x][y];
5335 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5336 Moving2Blocked(x, y, &newx, &newy);
5338 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5342 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5343 Moving2Blocked(x, y, &newx, &newy);
5345 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5348 MovDir[x][y] = old_move_dir;
5352 else if (element == EL_SATELLITE)
5358 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5360 boolean first_horiz = RND(2);
5361 int new_move_dir = MovDir[x][y];
5364 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5365 Moving2Blocked(x, y, &newx, &newy);
5367 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5371 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5372 Moving2Blocked(x, y, &newx, &newy);
5374 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5377 MovDir[x][y] = old_move_dir;
5381 else if (element == EL_EMC_ANDROID)
5383 static int check_pos[16] =
5385 -1, /* 0 => (invalid) */
5386 7, /* 1 => MV_LEFT */
5387 3, /* 2 => MV_RIGHT */
5388 -1, /* 3 => (invalid) */
5390 0, /* 5 => MV_LEFT | MV_UP */
5391 2, /* 6 => MV_RIGHT | MV_UP */
5392 -1, /* 7 => (invalid) */
5393 5, /* 8 => MV_DOWN */
5394 6, /* 9 => MV_LEFT | MV_DOWN */
5395 4, /* 10 => MV_RIGHT | MV_DOWN */
5396 -1, /* 11 => (invalid) */
5397 -1, /* 12 => (invalid) */
5398 -1, /* 13 => (invalid) */
5399 -1, /* 14 => (invalid) */
5400 -1, /* 15 => (invalid) */
5408 { -1, -1, MV_LEFT | MV_UP },
5410 { +1, -1, MV_RIGHT | MV_UP },
5411 { +1, 0, MV_RIGHT },
5412 { +1, +1, MV_RIGHT | MV_DOWN },
5414 { -1, +1, MV_LEFT | MV_DOWN },
5417 int start_pos, check_order;
5418 boolean can_clone = FALSE;
5421 /* check if there is any free field around current position */
5422 for (i = 0; i < 8; i++)
5424 int newx = x + check_xy[i].dx;
5425 int newy = y + check_xy[i].dy;
5427 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5435 if (can_clone) /* randomly find an element to clone */
5439 start_pos = check_pos[RND(8)];
5440 check_order = (RND(2) ? -1 : +1);
5442 for (i = 0; i < 8; i++)
5444 int pos_raw = start_pos + i * check_order;
5445 int pos = (pos_raw + 8) % 8;
5446 int newx = x + check_xy[pos].dx;
5447 int newy = y + check_xy[pos].dy;
5449 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5451 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5452 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5454 Store[x][y] = Feld[newx][newy];
5463 if (can_clone) /* randomly find a direction to move */
5467 start_pos = check_pos[RND(8)];
5468 check_order = (RND(2) ? -1 : +1);
5470 for (i = 0; i < 8; i++)
5472 int pos_raw = start_pos + i * check_order;
5473 int pos = (pos_raw + 8) % 8;
5474 int newx = x + check_xy[pos].dx;
5475 int newy = y + check_xy[pos].dy;
5476 int new_move_dir = check_xy[pos].dir;
5478 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5480 MovDir[x][y] = new_move_dir;
5481 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5490 if (can_clone) /* cloning and moving successful */
5493 /* cannot clone -- try to move towards player */
5495 start_pos = check_pos[MovDir[x][y] & 0x0f];
5496 check_order = (RND(2) ? -1 : +1);
5498 for (i = 0; i < 3; i++)
5500 /* first check start_pos, then previous/next or (next/previous) pos */
5501 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5502 int pos = (pos_raw + 8) % 8;
5503 int newx = x + check_xy[pos].dx;
5504 int newy = y + check_xy[pos].dy;
5505 int new_move_dir = check_xy[pos].dir;
5507 if (IS_PLAYER(newx, newy))
5510 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5512 MovDir[x][y] = new_move_dir;
5513 MovDelay[x][y] = level.android_move_time * 8 + 1;
5520 else if (move_pattern == MV_TURNING_LEFT ||
5521 move_pattern == MV_TURNING_RIGHT ||
5522 move_pattern == MV_TURNING_LEFT_RIGHT ||
5523 move_pattern == MV_TURNING_RIGHT_LEFT ||
5524 move_pattern == MV_TURNING_RANDOM ||
5525 move_pattern == MV_ALL_DIRECTIONS)
5527 boolean can_turn_left =
5528 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5529 boolean can_turn_right =
5530 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5532 if (element_info[element].move_stepsize == 0) /* "not moving" */
5535 if (move_pattern == MV_TURNING_LEFT)
5536 MovDir[x][y] = left_dir;
5537 else if (move_pattern == MV_TURNING_RIGHT)
5538 MovDir[x][y] = right_dir;
5539 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5540 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5541 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5542 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5543 else if (move_pattern == MV_TURNING_RANDOM)
5544 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5545 can_turn_right && !can_turn_left ? right_dir :
5546 RND(2) ? left_dir : right_dir);
5547 else if (can_turn_left && can_turn_right)
5548 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5549 else if (can_turn_left)
5550 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5551 else if (can_turn_right)
5552 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5554 MovDir[x][y] = back_dir;
5556 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5558 else if (move_pattern == MV_HORIZONTAL ||
5559 move_pattern == MV_VERTICAL)
5561 if (move_pattern & old_move_dir)
5562 MovDir[x][y] = back_dir;
5563 else if (move_pattern == MV_HORIZONTAL)
5564 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5565 else if (move_pattern == MV_VERTICAL)
5566 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5568 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5570 else if (move_pattern & MV_ANY_DIRECTION)
5572 MovDir[x][y] = move_pattern;
5573 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5575 else if (move_pattern & MV_WIND_DIRECTION)
5577 MovDir[x][y] = game.wind_direction;
5578 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5580 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5582 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5583 MovDir[x][y] = left_dir;
5584 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5585 MovDir[x][y] = right_dir;
5587 if (MovDir[x][y] != old_move_dir)
5588 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5590 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5592 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5593 MovDir[x][y] = right_dir;
5594 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5595 MovDir[x][y] = left_dir;
5597 if (MovDir[x][y] != old_move_dir)
5598 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5600 else if (move_pattern == MV_TOWARDS_PLAYER ||
5601 move_pattern == MV_AWAY_FROM_PLAYER)
5603 int attr_x = -1, attr_y = -1;
5605 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5616 for (i = 0; i < MAX_PLAYERS; i++)
5618 struct PlayerInfo *player = &stored_player[i];
5619 int jx = player->jx, jy = player->jy;
5621 if (!player->active)
5625 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5633 MovDir[x][y] = MV_NONE;
5635 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5636 else if (attr_x > x)
5637 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5639 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5640 else if (attr_y > y)
5641 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5643 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5645 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5647 boolean first_horiz = RND(2);
5648 int new_move_dir = MovDir[x][y];
5650 if (element_info[element].move_stepsize == 0) /* "not moving" */
5652 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5653 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5659 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5660 Moving2Blocked(x, y, &newx, &newy);
5662 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5666 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5667 Moving2Blocked(x, y, &newx, &newy);
5669 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5672 MovDir[x][y] = old_move_dir;
5675 else if (move_pattern == MV_WHEN_PUSHED ||
5676 move_pattern == MV_WHEN_DROPPED)
5678 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5679 MovDir[x][y] = MV_NONE;
5683 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5685 static int test_xy[7][2] =
5695 static int test_dir[7] =
5705 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5706 int move_preference = -1000000; /* start with very low preference */
5707 int new_move_dir = MV_NONE;
5708 int start_test = RND(4);
5711 for (i = 0; i < NUM_DIRECTIONS; i++)
5713 int move_dir = test_dir[start_test + i];
5714 int move_dir_preference;
5716 xx = x + test_xy[start_test + i][0];
5717 yy = y + test_xy[start_test + i][1];
5719 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5720 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5722 new_move_dir = move_dir;
5727 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5730 move_dir_preference = -1 * RunnerVisit[xx][yy];
5731 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5732 move_dir_preference = PlayerVisit[xx][yy];
5734 if (move_dir_preference > move_preference)
5736 /* prefer field that has not been visited for the longest time */
5737 move_preference = move_dir_preference;
5738 new_move_dir = move_dir;
5740 else if (move_dir_preference == move_preference &&
5741 move_dir == old_move_dir)
5743 /* prefer last direction when all directions are preferred equally */
5744 move_preference = move_dir_preference;
5745 new_move_dir = move_dir;
5749 MovDir[x][y] = new_move_dir;
5750 if (old_move_dir != new_move_dir)
5751 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5755 static void TurnRound(int x, int y)
5757 int direction = MovDir[x][y];
5759 int element, graphic;
5764 GfxDir[x][y] = MovDir[x][y];
5766 if (direction != MovDir[x][y])
5770 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5773 ResetGfxFrame(x, y, FALSE);
5775 element = Feld[x][y];
5776 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5778 if (graphic_info[graphic].anim_global_sync)
5779 GfxFrame[x][y] = FrameCounter;
5780 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5781 GfxFrame[x][y] = CustomValue[x][y];
5782 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5783 GfxFrame[x][y] = element_info[element].collect_score;
5784 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5785 GfxFrame[x][y] = ChangeDelay[x][y];
5789 static boolean JustBeingPushed(int x, int y)
5793 for (i = 0; i < MAX_PLAYERS; i++)
5795 struct PlayerInfo *player = &stored_player[i];
5797 if (player->active && player->is_pushing && player->MovPos)
5799 int next_jx = player->jx + (player->jx - player->last_jx);
5800 int next_jy = player->jy + (player->jy - player->last_jy);
5802 if (x == next_jx && y == next_jy)
5810 void StartMoving(int x, int y)
5812 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5813 int element = Feld[x][y];
5818 if (MovDelay[x][y] == 0)
5819 GfxAction[x][y] = ACTION_DEFAULT;
5821 if (CAN_FALL(element) && y < lev_fieldy - 1)
5823 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5824 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5825 if (JustBeingPushed(x, y))
5828 if (element == EL_QUICKSAND_FULL)
5830 if (IS_FREE(x, y + 1))
5832 InitMovingField(x, y, MV_DOWN);
5833 started_moving = TRUE;
5835 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5836 Store[x][y] = EL_ROCK;
5838 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5840 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5842 if (!MovDelay[x][y])
5843 MovDelay[x][y] = TILEY + 1;
5852 Feld[x][y] = EL_QUICKSAND_EMPTY;
5853 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5854 Store[x][y + 1] = Store[x][y];
5857 PlayLevelSoundAction(x, y, ACTION_FILLING);
5860 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5861 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5863 InitMovingField(x, y, MV_DOWN);
5864 started_moving = TRUE;
5866 Feld[x][y] = EL_QUICKSAND_FILLING;
5867 Store[x][y] = element;
5869 PlayLevelSoundAction(x, y, ACTION_FILLING);
5871 else if (element == EL_MAGIC_WALL_FULL)
5873 if (IS_FREE(x, y + 1))
5875 InitMovingField(x, y, MV_DOWN);
5876 started_moving = TRUE;
5878 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5879 Store[x][y] = EL_CHANGED(Store[x][y]);
5881 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5883 if (!MovDelay[x][y])
5884 MovDelay[x][y] = TILEY/4 + 1;
5893 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5894 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5895 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5899 else if (element == EL_BD_MAGIC_WALL_FULL)
5901 if (IS_FREE(x, y + 1))
5903 InitMovingField(x, y, MV_DOWN);
5904 started_moving = TRUE;
5906 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5907 Store[x][y] = EL_CHANGED2(Store[x][y]);
5909 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5911 if (!MovDelay[x][y])
5912 MovDelay[x][y] = TILEY/4 + 1;
5921 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5922 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5923 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5927 else if (CAN_PASS_MAGIC_WALL(element) &&
5928 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5929 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5931 InitMovingField(x, y, MV_DOWN);
5932 started_moving = TRUE;
5935 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5936 EL_BD_MAGIC_WALL_FILLING);
5937 Store[x][y] = element;
5939 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5941 SplashAcid(x, y + 1);
5943 InitMovingField(x, y, MV_DOWN);
5944 started_moving = TRUE;
5946 Store[x][y] = EL_ACID;
5948 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5949 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5951 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5952 CAN_FALL(element) && WasJustFalling[x][y] &&
5953 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5955 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5956 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5957 (Feld[x][y + 1] == EL_BLOCKED)))
5959 /* this is needed for a special case not covered by calling "Impact()"
5960 from "ContinueMoving()": if an element moves to a tile directly below
5961 another element which was just falling on that tile (which was empty
5962 in the previous frame), the falling element above would just stop
5963 instead of smashing the element below (in previous version, the above
5964 element was just checked for "moving" instead of "falling", resulting
5965 in incorrect smashes caused by horizontal movement of the above
5966 element; also, the case of the player being the element to smash was
5967 simply not covered here... :-/ ) */
5969 CheckCollision[x][y] = 0;
5973 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5975 if (MovDir[x][y] == MV_NONE)
5977 InitMovingField(x, y, MV_DOWN);
5978 started_moving = TRUE;
5981 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5983 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5984 MovDir[x][y] = MV_DOWN;
5986 InitMovingField(x, y, MV_DOWN);
5987 started_moving = TRUE;
5989 else if (element == EL_AMOEBA_DROP)
5991 Feld[x][y] = EL_AMOEBA_GROWING;
5992 Store[x][y] = EL_AMOEBA_WET;
5994 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5995 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5996 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5997 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5999 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6000 (IS_FREE(x - 1, y + 1) ||
6001 Feld[x - 1][y + 1] == EL_ACID));
6002 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6003 (IS_FREE(x + 1, y + 1) ||
6004 Feld[x + 1][y + 1] == EL_ACID));
6005 boolean can_fall_any = (can_fall_left || can_fall_right);
6006 boolean can_fall_both = (can_fall_left && can_fall_right);
6007 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6009 #if USE_NEW_ALL_SLIPPERY
6010 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6012 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6013 can_fall_right = FALSE;
6014 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6015 can_fall_left = FALSE;
6016 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6017 can_fall_right = FALSE;
6018 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6019 can_fall_left = FALSE;
6021 can_fall_any = (can_fall_left || can_fall_right);
6022 can_fall_both = FALSE;
6025 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6027 if (slippery_type == SLIPPERY_ONLY_LEFT)
6028 can_fall_right = FALSE;
6029 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6030 can_fall_left = FALSE;
6031 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6032 can_fall_right = FALSE;
6033 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6034 can_fall_left = FALSE;
6036 can_fall_any = (can_fall_left || can_fall_right);
6037 can_fall_both = (can_fall_left && can_fall_right);
6041 #if USE_NEW_ALL_SLIPPERY
6043 #if USE_NEW_SP_SLIPPERY
6044 /* !!! better use the same properties as for custom elements here !!! */
6045 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6046 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6048 can_fall_right = FALSE; /* slip down on left side */
6049 can_fall_both = FALSE;
6054 #if USE_NEW_ALL_SLIPPERY
6057 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6058 can_fall_right = FALSE; /* slip down on left side */
6060 can_fall_left = !(can_fall_right = RND(2));
6062 can_fall_both = FALSE;
6067 if (game.emulation == EMU_BOULDERDASH ||
6068 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;
6079 /* if not determined otherwise, prefer left side for slipping down */
6080 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6081 started_moving = TRUE;
6085 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6087 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6090 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6091 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6092 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6093 int belt_dir = game.belt_dir[belt_nr];
6095 if ((belt_dir == MV_LEFT && left_is_free) ||
6096 (belt_dir == MV_RIGHT && right_is_free))
6098 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6100 InitMovingField(x, y, belt_dir);
6101 started_moving = TRUE;
6103 Pushed[x][y] = TRUE;
6104 Pushed[nextx][y] = TRUE;
6106 GfxAction[x][y] = ACTION_DEFAULT;
6110 MovDir[x][y] = 0; /* if element was moving, stop it */
6115 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6117 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6119 if (CAN_MOVE(element) && !started_moving)
6122 int move_pattern = element_info[element].move_pattern;
6127 if (MovDir[x][y] == MV_NONE)
6129 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6130 x, y, element, element_info[element].token_name);
6131 printf("StartMoving(): This should never happen!\n");
6136 Moving2Blocked(x, y, &newx, &newy);
6138 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6141 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6142 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6144 WasJustMoving[x][y] = 0;
6145 CheckCollision[x][y] = 0;
6147 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6149 if (Feld[x][y] != element) /* element has changed */
6153 if (!MovDelay[x][y]) /* start new movement phase */
6155 /* all objects that can change their move direction after each step
6156 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6158 if (element != EL_YAMYAM &&
6159 element != EL_DARK_YAMYAM &&
6160 element != EL_PACMAN &&
6161 !(move_pattern & MV_ANY_DIRECTION) &&
6162 move_pattern != MV_TURNING_LEFT &&
6163 move_pattern != MV_TURNING_RIGHT &&
6164 move_pattern != MV_TURNING_LEFT_RIGHT &&
6165 move_pattern != MV_TURNING_RIGHT_LEFT &&
6166 move_pattern != MV_TURNING_RANDOM)
6170 if (MovDelay[x][y] && (element == EL_BUG ||
6171 element == EL_SPACESHIP ||
6172 element == EL_SP_SNIKSNAK ||
6173 element == EL_SP_ELECTRON ||
6174 element == EL_MOLE))
6175 DrawLevelField(x, y);
6179 if (MovDelay[x][y]) /* wait some time before next movement */
6183 if (element == EL_ROBOT ||
6184 element == EL_YAMYAM ||
6185 element == EL_DARK_YAMYAM)
6187 DrawLevelElementAnimationIfNeeded(x, y, element);
6188 PlayLevelSoundAction(x, y, ACTION_WAITING);
6190 else if (element == EL_SP_ELECTRON)
6191 DrawLevelElementAnimationIfNeeded(x, y, element);
6192 else if (element == EL_DRAGON)
6195 int dir = MovDir[x][y];
6196 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6197 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6198 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6199 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6200 dir == MV_UP ? IMG_FLAMES_1_UP :
6201 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6202 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6204 GfxAction[x][y] = ACTION_ATTACKING;
6206 if (IS_PLAYER(x, y))
6207 DrawPlayerField(x, y);
6209 DrawLevelField(x, y);
6211 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6213 for (i = 1; i <= 3; i++)
6215 int xx = x + i * dx;
6216 int yy = y + i * dy;
6217 int sx = SCREENX(xx);
6218 int sy = SCREENY(yy);
6219 int flame_graphic = graphic + (i - 1);
6221 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6226 int flamed = MovingOrBlocked2Element(xx, yy);
6230 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6232 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6233 RemoveMovingField(xx, yy);
6235 RemoveField(xx, yy);
6237 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6240 RemoveMovingField(xx, yy);
6243 ChangeDelay[xx][yy] = 0;
6245 Feld[xx][yy] = EL_FLAMES;
6247 if (IN_SCR_FIELD(sx, sy))
6249 DrawLevelFieldCrumbledSand(xx, yy);
6250 DrawGraphic(sx, sy, flame_graphic, frame);
6255 if (Feld[xx][yy] == EL_FLAMES)
6256 Feld[xx][yy] = EL_EMPTY;
6257 DrawLevelField(xx, yy);
6262 if (MovDelay[x][y]) /* element still has to wait some time */
6264 PlayLevelSoundAction(x, y, ACTION_WAITING);
6270 /* now make next step */
6272 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6274 if (DONT_COLLIDE_WITH(element) &&
6275 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6276 !PLAYER_ENEMY_PROTECTED(newx, newy))
6278 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6283 else if (CAN_MOVE_INTO_ACID(element) &&
6284 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6285 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6286 (MovDir[x][y] == MV_DOWN ||
6287 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6289 SplashAcid(newx, newy);
6290 Store[x][y] = EL_ACID;
6292 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6294 if (Feld[newx][newy] == EL_EXIT_OPEN)
6297 DrawLevelField(x, y);
6299 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6300 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6301 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6303 local_player->friends_still_needed--;
6304 if (!local_player->friends_still_needed &&
6305 !local_player->GameOver && AllPlayersGone)
6306 local_player->LevelSolved = local_player->GameOver = TRUE;
6310 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6312 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6313 DrawLevelField(newx, newy);
6315 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6317 else if (!IS_FREE(newx, newy))
6319 GfxAction[x][y] = ACTION_WAITING;
6321 if (IS_PLAYER(x, y))
6322 DrawPlayerField(x, y);
6324 DrawLevelField(x, y);
6329 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6331 if (IS_FOOD_PIG(Feld[newx][newy]))
6333 if (IS_MOVING(newx, newy))
6334 RemoveMovingField(newx, newy);
6337 Feld[newx][newy] = EL_EMPTY;
6338 DrawLevelField(newx, newy);
6341 PlayLevelSound(x, y, SND_PIG_DIGGING);
6343 else if (!IS_FREE(newx, newy))
6345 if (IS_PLAYER(x, y))
6346 DrawPlayerField(x, y);
6348 DrawLevelField(x, y);
6353 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6355 if (Store[x][y] != EL_EMPTY)
6357 boolean can_clone = FALSE;
6360 /* check if element to clone is still there */
6361 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6363 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6371 /* cannot clone or target field not free anymore -- do not clone */
6372 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6373 Store[x][y] = EL_EMPTY;
6376 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6378 if (IS_MV_DIAGONAL(MovDir[x][y]))
6380 int diagonal_move_dir = MovDir[x][y];
6381 int stored = Store[x][y];
6382 int change_delay = 8;
6385 /* android is moving diagonally */
6387 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6389 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6390 GfxElement[x][y] = EL_EMC_ANDROID;
6391 GfxAction[x][y] = ACTION_SHRINKING;
6392 GfxDir[x][y] = diagonal_move_dir;
6393 ChangeDelay[x][y] = change_delay;
6395 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6398 DrawLevelGraphicAnimation(x, y, graphic);
6399 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6401 if (Feld[newx][newy] == EL_ACID)
6403 SplashAcid(newx, newy);
6408 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6410 Store[newx][newy] = EL_EMC_ANDROID;
6411 GfxElement[newx][newy] = EL_EMC_ANDROID;
6412 GfxAction[newx][newy] = ACTION_GROWING;
6413 GfxDir[newx][newy] = diagonal_move_dir;
6414 ChangeDelay[newx][newy] = change_delay;
6416 graphic = el_act_dir2img(GfxElement[newx][newy],
6417 GfxAction[newx][newy], GfxDir[newx][newy]);
6419 DrawLevelGraphicAnimation(newx, newy, graphic);
6420 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6426 Feld[newx][newy] = EL_EMPTY;
6427 DrawLevelField(newx, newy);
6429 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6432 else if (!IS_FREE(newx, newy))
6435 if (IS_PLAYER(x, y))
6436 DrawPlayerField(x, y);
6438 DrawLevelField(x, y);
6444 else if (IS_CUSTOM_ELEMENT(element) &&
6445 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6447 int new_element = Feld[newx][newy];
6449 if (!IS_FREE(newx, newy))
6451 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6452 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6455 /* no element can dig solid indestructible elements */
6456 if (IS_INDESTRUCTIBLE(new_element) &&
6457 !IS_DIGGABLE(new_element) &&
6458 !IS_COLLECTIBLE(new_element))
6461 if (AmoebaNr[newx][newy] &&
6462 (new_element == EL_AMOEBA_FULL ||
6463 new_element == EL_BD_AMOEBA ||
6464 new_element == EL_AMOEBA_GROWING))
6466 AmoebaCnt[AmoebaNr[newx][newy]]--;
6467 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6470 if (IS_MOVING(newx, newy))
6471 RemoveMovingField(newx, newy);
6474 RemoveField(newx, newy);
6475 DrawLevelField(newx, newy);
6478 /* if digged element was about to explode, prevent the explosion */
6479 ExplodeField[newx][newy] = EX_TYPE_NONE;
6481 PlayLevelSoundAction(x, y, action);
6484 Store[newx][newy] = EL_EMPTY;
6486 /* this makes it possible to leave the removed element again */
6487 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6488 Store[newx][newy] = new_element;
6490 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6492 int move_leave_element = element_info[element].move_leave_element;
6494 /* this makes it possible to leave the removed element again */
6495 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6496 new_element : move_leave_element);
6500 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6502 RunnerVisit[x][y] = FrameCounter;
6503 PlayerVisit[x][y] /= 8; /* expire player visit path */
6506 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6508 if (!IS_FREE(newx, newy))
6510 if (IS_PLAYER(x, y))
6511 DrawPlayerField(x, y);
6513 DrawLevelField(x, y);
6519 boolean wanna_flame = !RND(10);
6520 int dx = newx - x, dy = newy - y;
6521 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6522 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6523 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6524 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6525 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6526 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6529 IS_CLASSIC_ENEMY(element1) ||
6530 IS_CLASSIC_ENEMY(element2)) &&
6531 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6532 element1 != EL_FLAMES && element2 != EL_FLAMES)
6534 ResetGfxAnimation(x, y);
6535 GfxAction[x][y] = ACTION_ATTACKING;
6537 if (IS_PLAYER(x, y))
6538 DrawPlayerField(x, y);
6540 DrawLevelField(x, y);
6542 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6544 MovDelay[x][y] = 50;
6548 RemoveField(newx, newy);
6550 Feld[newx][newy] = EL_FLAMES;
6551 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6554 RemoveField(newx1, newy1);
6556 Feld[newx1][newy1] = EL_FLAMES;
6558 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6561 RemoveField(newx2, newy2);
6563 Feld[newx2][newy2] = EL_FLAMES;
6570 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6571 Feld[newx][newy] == EL_DIAMOND)
6573 if (IS_MOVING(newx, newy))
6574 RemoveMovingField(newx, newy);
6577 Feld[newx][newy] = EL_EMPTY;
6578 DrawLevelField(newx, newy);
6581 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6583 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6584 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6586 if (AmoebaNr[newx][newy])
6588 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6589 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6590 Feld[newx][newy] == EL_BD_AMOEBA)
6591 AmoebaCnt[AmoebaNr[newx][newy]]--;
6596 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6598 RemoveMovingField(newx, newy);
6601 if (IS_MOVING(newx, newy))
6603 RemoveMovingField(newx, newy);
6608 Feld[newx][newy] = EL_EMPTY;
6609 DrawLevelField(newx, newy);
6612 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6614 else if ((element == EL_PACMAN || element == EL_MOLE)
6615 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6617 if (AmoebaNr[newx][newy])
6619 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6620 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6621 Feld[newx][newy] == EL_BD_AMOEBA)
6622 AmoebaCnt[AmoebaNr[newx][newy]]--;
6625 if (element == EL_MOLE)
6627 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6628 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6630 ResetGfxAnimation(x, y);
6631 GfxAction[x][y] = ACTION_DIGGING;
6632 DrawLevelField(x, y);
6634 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6636 return; /* wait for shrinking amoeba */
6638 else /* element == EL_PACMAN */
6640 Feld[newx][newy] = EL_EMPTY;
6641 DrawLevelField(newx, newy);
6642 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6645 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6646 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6647 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6649 /* wait for shrinking amoeba to completely disappear */
6652 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6654 /* object was running against a wall */
6659 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6660 if (move_pattern & MV_ANY_DIRECTION &&
6661 move_pattern == MovDir[x][y])
6663 int blocking_element =
6664 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6666 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6669 element = Feld[x][y]; /* element might have changed */
6673 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6674 DrawLevelElementAnimation(x, y, element);
6676 if (DONT_TOUCH(element))
6677 TestIfBadThingTouchesPlayer(x, y);
6682 InitMovingField(x, y, MovDir[x][y]);
6684 PlayLevelSoundAction(x, y, ACTION_MOVING);
6688 ContinueMoving(x, y);
6691 void ContinueMoving(int x, int y)
6693 int element = Feld[x][y];
6694 struct ElementInfo *ei = &element_info[element];
6695 int direction = MovDir[x][y];
6696 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6697 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6698 int newx = x + dx, newy = y + dy;
6699 int stored = Store[x][y];
6700 int stored_new = Store[newx][newy];
6701 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6702 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6703 boolean last_line = (newy == lev_fieldy - 1);
6705 MovPos[x][y] += getElementMoveStepsize(x, y);
6707 if (pushed_by_player) /* special case: moving object pushed by player */
6708 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6710 if (ABS(MovPos[x][y]) < TILEX)
6712 DrawLevelField(x, y);
6714 return; /* element is still moving */
6717 /* element reached destination field */
6719 Feld[x][y] = EL_EMPTY;
6720 Feld[newx][newy] = element;
6721 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6723 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6725 element = Feld[newx][newy] = EL_ACID;
6727 else if (element == EL_MOLE)
6729 Feld[x][y] = EL_SAND;
6731 DrawLevelFieldCrumbledSandNeighbours(x, y);
6733 else if (element == EL_QUICKSAND_FILLING)
6735 element = Feld[newx][newy] = get_next_element(element);
6736 Store[newx][newy] = Store[x][y];
6738 else if (element == EL_QUICKSAND_EMPTYING)
6740 Feld[x][y] = get_next_element(element);
6741 element = Feld[newx][newy] = Store[x][y];
6743 else if (element == EL_MAGIC_WALL_FILLING)
6745 element = Feld[newx][newy] = get_next_element(element);
6746 if (!game.magic_wall_active)
6747 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6748 Store[newx][newy] = Store[x][y];
6750 else if (element == EL_MAGIC_WALL_EMPTYING)
6752 Feld[x][y] = get_next_element(element);
6753 if (!game.magic_wall_active)
6754 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6755 element = Feld[newx][newy] = Store[x][y];
6757 #if USE_NEW_CUSTOM_VALUE
6758 InitField(newx, newy, FALSE);
6761 else if (element == EL_BD_MAGIC_WALL_FILLING)
6763 element = Feld[newx][newy] = get_next_element(element);
6764 if (!game.magic_wall_active)
6765 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6766 Store[newx][newy] = Store[x][y];
6768 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6770 Feld[x][y] = get_next_element(element);
6771 if (!game.magic_wall_active)
6772 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6773 element = Feld[newx][newy] = Store[x][y];
6775 #if USE_NEW_CUSTOM_VALUE
6776 InitField(newx, newy, FALSE);
6779 else if (element == EL_AMOEBA_DROPPING)
6781 Feld[x][y] = get_next_element(element);
6782 element = Feld[newx][newy] = Store[x][y];
6784 else if (element == EL_SOKOBAN_OBJECT)
6787 Feld[x][y] = Back[x][y];
6789 if (Back[newx][newy])
6790 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6792 Back[x][y] = Back[newx][newy] = 0;
6795 Store[x][y] = EL_EMPTY;
6800 MovDelay[newx][newy] = 0;
6803 if (CAN_CHANGE_OR_HAS_ACTION(element))
6805 if (CAN_CHANGE(element))
6808 /* copy element change control values to new field */
6809 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6810 ChangePage[newx][newy] = ChangePage[x][y];
6811 ChangeCount[newx][newy] = ChangeCount[x][y];
6812 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6815 #if USE_NEW_CUSTOM_VALUE
6816 CustomValue[newx][newy] = CustomValue[x][y];
6822 #if USE_NEW_CUSTOM_VALUE
6823 CustomValue[newx][newy] = CustomValue[x][y];
6827 ChangeDelay[x][y] = 0;
6828 ChangePage[x][y] = -1;
6829 ChangeCount[x][y] = 0;
6830 ChangeEvent[x][y] = -1;
6832 #if USE_NEW_CUSTOM_VALUE
6833 CustomValue[x][y] = 0;
6836 /* copy animation control values to new field */
6837 GfxFrame[newx][newy] = GfxFrame[x][y];
6838 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6839 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6840 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6842 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6844 /* some elements can leave other elements behind after moving */
6846 if (ei->move_leave_element != EL_EMPTY &&
6847 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6848 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6850 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6851 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6852 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6855 int move_leave_element = ei->move_leave_element;
6859 /* this makes it possible to leave the removed element again */
6860 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6861 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6863 /* this makes it possible to leave the removed element again */
6864 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6865 move_leave_element = stored;
6868 /* this makes it possible to leave the removed element again */
6869 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6870 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6871 move_leave_element = stored;
6874 Feld[x][y] = move_leave_element;
6876 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6877 MovDir[x][y] = direction;
6879 InitField(x, y, FALSE);
6881 if (GFX_CRUMBLED(Feld[x][y]))
6882 DrawLevelFieldCrumbledSandNeighbours(x, y);
6884 if (ELEM_IS_PLAYER(move_leave_element))
6885 RelocatePlayer(x, y, move_leave_element);
6888 /* do this after checking for left-behind element */
6889 ResetGfxAnimation(x, y); /* reset animation values for old field */
6891 if (!CAN_MOVE(element) ||
6892 (CAN_FALL(element) && direction == MV_DOWN &&
6893 (element == EL_SPRING ||
6894 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6895 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6896 GfxDir[x][y] = MovDir[newx][newy] = 0;
6898 DrawLevelField(x, y);
6899 DrawLevelField(newx, newy);
6901 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6903 /* prevent pushed element from moving on in pushed direction */
6904 if (pushed_by_player && CAN_MOVE(element) &&
6905 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6906 !(element_info[element].move_pattern & direction))
6907 TurnRound(newx, newy);
6909 /* prevent elements on conveyor belt from moving on in last direction */
6910 if (pushed_by_conveyor && CAN_FALL(element) &&
6911 direction & MV_HORIZONTAL)
6912 MovDir[newx][newy] = 0;
6914 if (!pushed_by_player)
6916 int nextx = newx + dx, nexty = newy + dy;
6917 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6919 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6921 if (CAN_FALL(element) && direction == MV_DOWN)
6922 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6924 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6925 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6928 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6930 TestIfBadThingTouchesPlayer(newx, newy);
6931 TestIfBadThingTouchesFriend(newx, newy);
6933 if (!IS_CUSTOM_ELEMENT(element))
6934 TestIfBadThingTouchesOtherBadThing(newx, newy);
6936 else if (element == EL_PENGUIN)
6937 TestIfFriendTouchesBadThing(newx, newy);
6939 /* give the player one last chance (one more frame) to move away */
6940 if (CAN_FALL(element) && direction == MV_DOWN &&
6941 (last_line || (!IS_FREE(x, newy + 1) &&
6942 (!IS_PLAYER(x, newy + 1) ||
6943 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6946 if (pushed_by_player && !game.use_change_when_pushing_bug)
6948 int push_side = MV_DIR_OPPOSITE(direction);
6949 struct PlayerInfo *player = PLAYERINFO(x, y);
6951 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6952 player->index_bit, push_side);
6953 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6954 player->index_bit, push_side);
6957 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6958 MovDelay[newx][newy] = 1;
6960 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6962 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6965 if (ChangePage[newx][newy] != -1) /* delayed change */
6967 int page = ChangePage[newx][newy];
6968 struct ElementChangeInfo *change = &ei->change_page[page];
6970 ChangePage[newx][newy] = -1;
6972 if (change->can_change)
6974 if (ChangeElement(newx, newy, element, page))
6976 if (change->post_change_function)
6977 change->post_change_function(newx, newy);
6981 if (change->has_action)
6982 ExecuteCustomElementAction(newx, newy, element, page);
6986 TestIfElementHitsCustomElement(newx, newy, direction);
6987 TestIfPlayerTouchesCustomElement(newx, newy);
6988 TestIfElementTouchesCustomElement(newx, newy);
6991 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6992 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6993 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6994 MV_DIR_OPPOSITE(direction));
6998 int AmoebeNachbarNr(int ax, int ay)
7001 int element = Feld[ax][ay];
7003 static int xy[4][2] =
7011 for (i = 0; i < NUM_DIRECTIONS; i++)
7013 int x = ax + xy[i][0];
7014 int y = ay + xy[i][1];
7016 if (!IN_LEV_FIELD(x, y))
7019 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7020 group_nr = AmoebaNr[x][y];
7026 void AmoebenVereinigen(int ax, int ay)
7028 int i, x, y, xx, yy;
7029 int new_group_nr = AmoebaNr[ax][ay];
7030 static int xy[4][2] =
7038 if (new_group_nr == 0)
7041 for (i = 0; i < NUM_DIRECTIONS; i++)
7046 if (!IN_LEV_FIELD(x, y))
7049 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7050 Feld[x][y] == EL_BD_AMOEBA ||
7051 Feld[x][y] == EL_AMOEBA_DEAD) &&
7052 AmoebaNr[x][y] != new_group_nr)
7054 int old_group_nr = AmoebaNr[x][y];
7056 if (old_group_nr == 0)
7059 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7060 AmoebaCnt[old_group_nr] = 0;
7061 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7062 AmoebaCnt2[old_group_nr] = 0;
7065 SCAN_PLAYFIELD(xx, yy)
7067 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7070 if (AmoebaNr[xx][yy] == old_group_nr)
7071 AmoebaNr[xx][yy] = new_group_nr;
7077 void AmoebeUmwandeln(int ax, int ay)
7081 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7083 int group_nr = AmoebaNr[ax][ay];
7088 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7089 printf("AmoebeUmwandeln(): This should never happen!\n");
7095 SCAN_PLAYFIELD(x, y)
7097 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7100 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7103 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7107 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7108 SND_AMOEBA_TURNING_TO_GEM :
7109 SND_AMOEBA_TURNING_TO_ROCK));
7114 static int xy[4][2] =
7122 for (i = 0; i < NUM_DIRECTIONS; i++)
7127 if (!IN_LEV_FIELD(x, y))
7130 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7132 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7133 SND_AMOEBA_TURNING_TO_GEM :
7134 SND_AMOEBA_TURNING_TO_ROCK));
7141 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7144 int group_nr = AmoebaNr[ax][ay];
7145 boolean done = FALSE;
7150 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7151 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7157 SCAN_PLAYFIELD(x, y)
7159 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7162 if (AmoebaNr[x][y] == group_nr &&
7163 (Feld[x][y] == EL_AMOEBA_DEAD ||
7164 Feld[x][y] == EL_BD_AMOEBA ||
7165 Feld[x][y] == EL_AMOEBA_GROWING))
7168 Feld[x][y] = new_element;
7169 InitField(x, y, FALSE);
7170 DrawLevelField(x, y);
7176 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7177 SND_BD_AMOEBA_TURNING_TO_ROCK :
7178 SND_BD_AMOEBA_TURNING_TO_GEM));
7181 void AmoebeWaechst(int x, int y)
7183 static unsigned long sound_delay = 0;
7184 static unsigned long sound_delay_value = 0;
7186 if (!MovDelay[x][y]) /* start new growing cycle */
7190 if (DelayReached(&sound_delay, sound_delay_value))
7192 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7193 sound_delay_value = 30;
7197 if (MovDelay[x][y]) /* wait some time before growing bigger */
7200 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7202 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7203 6 - MovDelay[x][y]);
7205 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7208 if (!MovDelay[x][y])
7210 Feld[x][y] = Store[x][y];
7212 DrawLevelField(x, y);
7217 void AmoebaDisappearing(int x, int y)
7219 static unsigned long sound_delay = 0;
7220 static unsigned long sound_delay_value = 0;
7222 if (!MovDelay[x][y]) /* start new shrinking cycle */
7226 if (DelayReached(&sound_delay, sound_delay_value))
7227 sound_delay_value = 30;
7230 if (MovDelay[x][y]) /* wait some time before shrinking */
7233 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7235 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7236 6 - MovDelay[x][y]);
7238 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7241 if (!MovDelay[x][y])
7243 Feld[x][y] = EL_EMPTY;
7244 DrawLevelField(x, y);
7246 /* don't let mole enter this field in this cycle;
7247 (give priority to objects falling to this field from above) */
7253 void AmoebeAbleger(int ax, int ay)
7256 int element = Feld[ax][ay];
7257 int graphic = el2img(element);
7258 int newax = ax, neway = ay;
7259 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7260 static int xy[4][2] =
7268 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7270 Feld[ax][ay] = EL_AMOEBA_DEAD;
7271 DrawLevelField(ax, ay);
7275 if (IS_ANIMATED(graphic))
7276 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7278 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7279 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7281 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7284 if (MovDelay[ax][ay])
7288 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7291 int x = ax + xy[start][0];
7292 int y = ay + xy[start][1];
7294 if (!IN_LEV_FIELD(x, y))
7297 if (IS_FREE(x, y) ||
7298 CAN_GROW_INTO(Feld[x][y]) ||
7299 Feld[x][y] == EL_QUICKSAND_EMPTY)
7305 if (newax == ax && neway == ay)
7308 else /* normal or "filled" (BD style) amoeba */
7311 boolean waiting_for_player = FALSE;
7313 for (i = 0; i < NUM_DIRECTIONS; i++)
7315 int j = (start + i) % 4;
7316 int x = ax + xy[j][0];
7317 int y = ay + xy[j][1];
7319 if (!IN_LEV_FIELD(x, y))
7322 if (IS_FREE(x, y) ||
7323 CAN_GROW_INTO(Feld[x][y]) ||
7324 Feld[x][y] == EL_QUICKSAND_EMPTY)
7330 else if (IS_PLAYER(x, y))
7331 waiting_for_player = TRUE;
7334 if (newax == ax && neway == ay) /* amoeba cannot grow */
7336 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7338 Feld[ax][ay] = EL_AMOEBA_DEAD;
7339 DrawLevelField(ax, ay);
7340 AmoebaCnt[AmoebaNr[ax][ay]]--;
7342 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7344 if (element == EL_AMOEBA_FULL)
7345 AmoebeUmwandeln(ax, ay);
7346 else if (element == EL_BD_AMOEBA)
7347 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7352 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7354 /* amoeba gets larger by growing in some direction */
7356 int new_group_nr = AmoebaNr[ax][ay];
7359 if (new_group_nr == 0)
7361 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7362 printf("AmoebeAbleger(): This should never happen!\n");
7367 AmoebaNr[newax][neway] = new_group_nr;
7368 AmoebaCnt[new_group_nr]++;
7369 AmoebaCnt2[new_group_nr]++;
7371 /* if amoeba touches other amoeba(s) after growing, unify them */
7372 AmoebenVereinigen(newax, neway);
7374 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7376 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7382 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7383 (neway == lev_fieldy - 1 && newax != ax))
7385 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7386 Store[newax][neway] = element;
7388 else if (neway == ay || element == EL_EMC_DRIPPER)
7390 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7392 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7396 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7397 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7398 Store[ax][ay] = EL_AMOEBA_DROP;
7399 ContinueMoving(ax, ay);
7403 DrawLevelField(newax, neway);
7406 void Life(int ax, int ay)
7410 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7413 int element = Feld[ax][ay];
7414 int graphic = el2img(element);
7415 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7417 boolean changed = FALSE;
7419 if (IS_ANIMATED(graphic))
7420 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7425 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7426 MovDelay[ax][ay] = life_time;
7428 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7431 if (MovDelay[ax][ay])
7435 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7437 int xx = ax+x1, yy = ay+y1;
7440 if (!IN_LEV_FIELD(xx, yy))
7443 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7445 int x = xx+x2, y = yy+y2;
7447 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7450 if (((Feld[x][y] == element ||
7451 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7453 (IS_FREE(x, y) && Stop[x][y]))
7457 if (xx == ax && yy == ay) /* field in the middle */
7459 if (nachbarn < life_parameter[0] ||
7460 nachbarn > life_parameter[1])
7462 Feld[xx][yy] = EL_EMPTY;
7464 DrawLevelField(xx, yy);
7465 Stop[xx][yy] = TRUE;
7469 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7470 { /* free border field */
7471 if (nachbarn >= life_parameter[2] &&
7472 nachbarn <= life_parameter[3])
7474 Feld[xx][yy] = element;
7475 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7477 DrawLevelField(xx, yy);
7478 Stop[xx][yy] = TRUE;
7485 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7486 SND_GAME_OF_LIFE_GROWING);
7489 static void InitRobotWheel(int x, int y)
7491 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7494 static void RunRobotWheel(int x, int y)
7496 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7499 static void StopRobotWheel(int x, int y)
7501 if (ZX == x && ZY == y)
7505 static void InitTimegateWheel(int x, int y)
7507 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7510 static void RunTimegateWheel(int x, int y)
7512 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7515 static void InitMagicBallDelay(int x, int y)
7518 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7520 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7524 static void ActivateMagicBall(int bx, int by)
7528 if (level.ball_random)
7530 int pos_border = RND(8); /* select one of the eight border elements */
7531 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7532 int xx = pos_content % 3;
7533 int yy = pos_content / 3;
7538 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7539 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7543 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7545 int xx = x - bx + 1;
7546 int yy = y - by + 1;
7548 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7549 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7553 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7556 static void InitDiagonalMovingElement(int x, int y)
7559 MovDelay[x][y] = level.android_move_time;
7563 void CheckExit(int x, int y)
7565 if (local_player->gems_still_needed > 0 ||
7566 local_player->sokobanfields_still_needed > 0 ||
7567 local_player->lights_still_needed > 0)
7569 int element = Feld[x][y];
7570 int graphic = el2img(element);
7572 if (IS_ANIMATED(graphic))
7573 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7578 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7581 Feld[x][y] = EL_EXIT_OPENING;
7583 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7586 void CheckExitSP(int x, int y)
7588 if (local_player->gems_still_needed > 0)
7590 int element = Feld[x][y];
7591 int graphic = el2img(element);
7593 if (IS_ANIMATED(graphic))
7594 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7599 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7602 Feld[x][y] = EL_SP_EXIT_OPENING;
7604 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7607 static void CloseAllOpenTimegates()
7612 SCAN_PLAYFIELD(x, y)
7614 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7617 int element = Feld[x][y];
7619 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7621 Feld[x][y] = EL_TIMEGATE_CLOSING;
7623 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7628 void EdelsteinFunkeln(int x, int y)
7630 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7633 if (Feld[x][y] == EL_BD_DIAMOND)
7636 if (MovDelay[x][y] == 0) /* next animation frame */
7637 MovDelay[x][y] = 11 * !SimpleRND(500);
7639 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7643 if (setup.direct_draw && MovDelay[x][y])
7644 SetDrawtoField(DRAW_BUFFERED);
7646 DrawLevelElementAnimation(x, y, Feld[x][y]);
7648 if (MovDelay[x][y] != 0)
7650 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7651 10 - MovDelay[x][y]);
7653 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7655 if (setup.direct_draw)
7659 dest_x = FX + SCREENX(x) * TILEX;
7660 dest_y = FY + SCREENY(y) * TILEY;
7662 BlitBitmap(drawto_field, window,
7663 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7664 SetDrawtoField(DRAW_DIRECT);
7670 void MauerWaechst(int x, int y)
7674 if (!MovDelay[x][y]) /* next animation frame */
7675 MovDelay[x][y] = 3 * delay;
7677 if (MovDelay[x][y]) /* wait some time before next frame */
7681 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7683 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7684 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7686 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7689 if (!MovDelay[x][y])
7691 if (MovDir[x][y] == MV_LEFT)
7693 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7694 DrawLevelField(x - 1, y);
7696 else if (MovDir[x][y] == MV_RIGHT)
7698 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7699 DrawLevelField(x + 1, y);
7701 else if (MovDir[x][y] == MV_UP)
7703 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7704 DrawLevelField(x, y - 1);
7708 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7709 DrawLevelField(x, y + 1);
7712 Feld[x][y] = Store[x][y];
7714 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7715 DrawLevelField(x, y);
7720 void MauerAbleger(int ax, int ay)
7722 int element = Feld[ax][ay];
7723 int graphic = el2img(element);
7724 boolean oben_frei = FALSE, unten_frei = FALSE;
7725 boolean links_frei = FALSE, rechts_frei = FALSE;
7726 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7727 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7728 boolean new_wall = FALSE;
7730 if (IS_ANIMATED(graphic))
7731 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7733 if (!MovDelay[ax][ay]) /* start building new wall */
7734 MovDelay[ax][ay] = 6;
7736 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7739 if (MovDelay[ax][ay])
7743 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7745 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7747 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7749 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7752 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7753 element == EL_EXPANDABLE_WALL_ANY)
7757 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7758 Store[ax][ay-1] = element;
7759 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7760 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7761 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7762 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7767 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7768 Store[ax][ay+1] = element;
7769 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7770 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7771 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7772 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7777 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7778 element == EL_EXPANDABLE_WALL_ANY ||
7779 element == EL_EXPANDABLE_WALL)
7783 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7784 Store[ax-1][ay] = element;
7785 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7786 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7787 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7788 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
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_RIGHT;
7797 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7798 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7799 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7804 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7805 DrawLevelField(ax, ay);
7807 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7809 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7810 unten_massiv = TRUE;
7811 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7812 links_massiv = TRUE;
7813 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7814 rechts_massiv = TRUE;
7816 if (((oben_massiv && unten_massiv) ||
7817 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7818 element == EL_EXPANDABLE_WALL) &&
7819 ((links_massiv && rechts_massiv) ||
7820 element == EL_EXPANDABLE_WALL_VERTICAL))
7821 Feld[ax][ay] = EL_WALL;
7824 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7827 void CheckForDragon(int x, int y)
7830 boolean dragon_found = FALSE;
7831 static int xy[4][2] =
7839 for (i = 0; i < NUM_DIRECTIONS; i++)
7841 for (j = 0; j < 4; j++)
7843 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7845 if (IN_LEV_FIELD(xx, yy) &&
7846 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7848 if (Feld[xx][yy] == EL_DRAGON)
7849 dragon_found = TRUE;
7858 for (i = 0; i < NUM_DIRECTIONS; i++)
7860 for (j = 0; j < 3; j++)
7862 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7864 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7866 Feld[xx][yy] = EL_EMPTY;
7867 DrawLevelField(xx, yy);
7876 static void InitBuggyBase(int x, int y)
7878 int element = Feld[x][y];
7879 int activating_delay = FRAMES_PER_SECOND / 4;
7882 (element == EL_SP_BUGGY_BASE ?
7883 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7884 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7886 element == EL_SP_BUGGY_BASE_ACTIVE ?
7887 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7890 static void WarnBuggyBase(int x, int y)
7893 static int xy[4][2] =
7901 for (i = 0; i < NUM_DIRECTIONS; i++)
7903 int xx = x + xy[i][0];
7904 int yy = y + xy[i][1];
7906 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7908 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7915 static void InitTrap(int x, int y)
7917 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7920 static void ActivateTrap(int x, int y)
7922 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7925 static void ChangeActiveTrap(int x, int y)
7927 int graphic = IMG_TRAP_ACTIVE;
7929 /* if new animation frame was drawn, correct crumbled sand border */
7930 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7931 DrawLevelFieldCrumbledSand(x, y);
7934 static int getSpecialActionElement(int element, int number, int base_element)
7936 return (element != EL_EMPTY ? element :
7937 number != -1 ? base_element + number - 1 :
7941 static int getModifiedActionNumber(int value_old, int operator, int operand,
7942 int value_min, int value_max)
7944 int value_new = (operator == CA_MODE_SET ? operand :
7945 operator == CA_MODE_ADD ? value_old + operand :
7946 operator == CA_MODE_SUBTRACT ? value_old - operand :
7947 operator == CA_MODE_MULTIPLY ? value_old * operand :
7948 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7949 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7952 return (value_new < value_min ? value_min :
7953 value_new > value_max ? value_max :
7957 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7959 struct ElementInfo *ei = &element_info[element];
7960 struct ElementChangeInfo *change = &ei->change_page[page];
7961 int target_element = change->target_element;
7962 int action_type = change->action_type;
7963 int action_mode = change->action_mode;
7964 int action_arg = change->action_arg;
7967 if (!change->has_action)
7970 /* ---------- determine action paramater values -------------------------- */
7972 int level_time_value =
7973 (level.time > 0 ? TimeLeft :
7976 int action_arg_element =
7977 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7978 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7979 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7982 int action_arg_direction =
7983 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7984 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7985 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7986 change->actual_trigger_side :
7987 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7988 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7991 int action_arg_number_min =
7992 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7995 int action_arg_number_max =
7996 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7997 action_type == CA_SET_LEVEL_GEMS ? 999 :
7998 action_type == CA_SET_LEVEL_TIME ? 9999 :
7999 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8000 action_type == CA_SET_CE_VALUE ? 9999 :
8001 action_type == CA_SET_CE_SCORE ? 9999 :
8004 int action_arg_number_reset =
8005 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
8006 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8007 action_type == CA_SET_LEVEL_TIME ? level.time :
8008 action_type == CA_SET_LEVEL_SCORE ? 0 :
8010 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8012 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8014 action_type == CA_SET_CE_SCORE ? 0 :
8017 int action_arg_number =
8018 (action_arg <= CA_ARG_MAX ? action_arg :
8019 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8020 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8021 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8022 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8023 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8024 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8025 #if USE_NEW_CUSTOM_VALUE
8026 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8028 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8030 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8031 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8032 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8033 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8034 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8035 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8036 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8037 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8038 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8039 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8040 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8043 int action_arg_number_old =
8044 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8045 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8046 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8047 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8048 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8051 int action_arg_number_new =
8052 getModifiedActionNumber(action_arg_number_old,
8053 action_mode, action_arg_number,
8054 action_arg_number_min, action_arg_number_max);
8056 int trigger_player_bits =
8057 (change->actual_trigger_player >= EL_PLAYER_1 &&
8058 change->actual_trigger_player <= EL_PLAYER_4 ?
8059 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8062 int action_arg_player_bits =
8063 (action_arg >= CA_ARG_PLAYER_1 &&
8064 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8065 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8068 /* ---------- execute action -------------------------------------------- */
8077 /* ---------- level actions ------------------------------------------- */
8079 case CA_RESTART_LEVEL:
8081 game.restart_level = TRUE;
8086 case CA_SHOW_ENVELOPE:
8088 int element = getSpecialActionElement(action_arg_element,
8089 action_arg_number, EL_ENVELOPE_1);
8091 if (IS_ENVELOPE(element))
8092 local_player->show_envelope = element;
8097 case CA_SET_LEVEL_TIME:
8099 if (level.time > 0) /* only modify limited time value */
8101 TimeLeft = action_arg_number_new;
8103 DrawGameValue_Time(TimeLeft);
8105 if (!TimeLeft && setup.time_limit)
8106 for (i = 0; i < MAX_PLAYERS; i++)
8107 KillPlayer(&stored_player[i]);
8113 case CA_SET_LEVEL_SCORE:
8115 local_player->score = action_arg_number_new;
8117 DrawGameValue_Score(local_player->score);
8122 case CA_SET_LEVEL_GEMS:
8124 local_player->gems_still_needed = action_arg_number_new;
8126 DrawGameValue_Emeralds(local_player->gems_still_needed);
8131 #if !USE_PLAYER_GRAVITY
8132 case CA_SET_LEVEL_GRAVITY:
8134 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8135 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8136 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8142 case CA_SET_LEVEL_WIND:
8144 game.wind_direction = action_arg_direction;
8149 /* ---------- player actions ------------------------------------------ */
8151 case CA_MOVE_PLAYER:
8153 /* automatically move to the next field in specified direction */
8154 for (i = 0; i < MAX_PLAYERS; i++)
8155 if (trigger_player_bits & (1 << i))
8156 stored_player[i].programmed_action = action_arg_direction;
8161 case CA_EXIT_PLAYER:
8163 for (i = 0; i < MAX_PLAYERS; i++)
8164 if (action_arg_player_bits & (1 << i))
8165 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8170 case CA_KILL_PLAYER:
8172 for (i = 0; i < MAX_PLAYERS; i++)
8173 if (action_arg_player_bits & (1 << i))
8174 KillPlayer(&stored_player[i]);
8179 case CA_SET_PLAYER_KEYS:
8181 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8182 int element = getSpecialActionElement(action_arg_element,
8183 action_arg_number, EL_KEY_1);
8185 if (IS_KEY(element))
8187 for (i = 0; i < MAX_PLAYERS; i++)
8189 if (trigger_player_bits & (1 << i))
8191 stored_player[i].key[KEY_NR(element)] = key_state;
8194 DrawGameDoorValues();
8196 DrawGameValue_Keys(stored_player[i].key);
8199 redraw_mask |= REDRAW_DOOR_1;
8207 case CA_SET_PLAYER_SPEED:
8209 for (i = 0; i < MAX_PLAYERS; i++)
8211 if (trigger_player_bits & (1 << i))
8213 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8215 if (action_arg == CA_ARG_SPEED_FASTER &&
8216 stored_player[i].cannot_move)
8218 action_arg_number = STEPSIZE_VERY_SLOW;
8220 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8221 action_arg == CA_ARG_SPEED_FASTER)
8223 action_arg_number = 2;
8224 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8229 getModifiedActionNumber(move_stepsize,
8232 action_arg_number_min,
8233 action_arg_number_max);
8236 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8238 /* make sure that value is power of 2 */
8239 move_stepsize = (1 << log_2(move_stepsize));
8241 /* do no immediately change -- the player might just be moving */
8242 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8244 stored_player[i].cannot_move =
8245 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8253 case CA_SET_PLAYER_SHIELD:
8255 for (i = 0; i < MAX_PLAYERS; i++)
8257 if (trigger_player_bits & (1 << i))
8259 if (action_arg == CA_ARG_SHIELD_OFF)
8261 stored_player[i].shield_normal_time_left = 0;
8262 stored_player[i].shield_deadly_time_left = 0;
8264 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8266 stored_player[i].shield_normal_time_left = 999999;
8268 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8270 stored_player[i].shield_normal_time_left = 999999;
8271 stored_player[i].shield_deadly_time_left = 999999;
8279 #if USE_PLAYER_GRAVITY
8280 case CA_SET_PLAYER_GRAVITY:
8282 for (i = 0; i < MAX_PLAYERS; i++)
8284 if (trigger_player_bits & (1 << i))
8286 stored_player[i].gravity =
8287 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8288 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8289 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8290 stored_player[i].gravity);
8298 case CA_SET_PLAYER_ARTWORK:
8300 for (i = 0; i < MAX_PLAYERS; i++)
8302 if (trigger_player_bits & (1 << i))
8304 int artwork_element = action_arg_element;
8306 if (action_arg == CA_ARG_ELEMENT_RESET)
8308 (level.use_artwork_element[i] ? level.artwork_element[i] :
8309 stored_player[i].element_nr);
8311 stored_player[i].artwork_element = artwork_element;
8313 SetPlayerWaiting(&stored_player[i], FALSE);
8315 /* set number of special actions for bored and sleeping animation */
8316 stored_player[i].num_special_action_bored =
8317 get_num_special_action(artwork_element,
8318 ACTION_BORING_1, ACTION_BORING_LAST);
8319 stored_player[i].num_special_action_sleeping =
8320 get_num_special_action(artwork_element,
8321 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8328 /* ---------- CE actions ---------------------------------------------- */
8330 case CA_SET_CE_VALUE:
8332 #if USE_NEW_CUSTOM_VALUE
8333 int last_ce_value = CustomValue[x][y];
8335 CustomValue[x][y] = action_arg_number_new;
8338 printf("::: CE value == %d\n", CustomValue[x][y]);
8341 if (CustomValue[x][y] != last_ce_value)
8343 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8344 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8346 if (CustomValue[x][y] == 0)
8349 printf("::: CE_VALUE_GETS_ZERO\n");
8352 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8353 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8356 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8366 case CA_SET_CE_SCORE:
8368 #if USE_NEW_CUSTOM_VALUE
8369 int last_ce_score = ei->collect_score;
8371 ei->collect_score = action_arg_number_new;
8374 printf("::: CE score == %d\n", ei->collect_score);
8377 if (ei->collect_score != last_ce_score)
8379 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8380 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8382 if (ei->collect_score == 0)
8385 printf("::: CE_SCORE_GETS_ZERO\n");
8388 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8389 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8392 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8402 /* ---------- engine actions ------------------------------------------ */
8404 case CA_SET_ENGINE_SCAN_MODE:
8406 InitPlayfieldScanMode(action_arg);
8416 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8418 int old_element = Feld[x][y];
8419 int new_element = get_element_from_group_element(element);
8420 int previous_move_direction = MovDir[x][y];
8421 #if USE_NEW_CUSTOM_VALUE
8422 int last_ce_value = CustomValue[x][y];
8424 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8425 IS_WALKABLE(old_element));
8428 /* check if element under the player changes from accessible to unaccessible
8429 (needed for special case of dropping element which then changes) */
8430 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8431 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8441 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8442 RemoveMovingField(x, y);
8446 Feld[x][y] = new_element;
8448 #if !USE_GFX_RESET_GFX_ANIMATION
8449 ResetGfxAnimation(x, y);
8450 ResetRandomAnimationValue(x, y);
8453 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8454 MovDir[x][y] = previous_move_direction;
8456 #if USE_NEW_CUSTOM_VALUE
8457 if (element_info[new_element].use_last_ce_value)
8458 CustomValue[x][y] = last_ce_value;
8461 InitField_WithBug1(x, y, FALSE);
8463 new_element = Feld[x][y]; /* element may have changed */
8465 #if USE_GFX_RESET_GFX_ANIMATION
8466 ResetGfxAnimation(x, y);
8467 ResetRandomAnimationValue(x, y);
8470 DrawLevelField(x, y);
8472 if (GFX_CRUMBLED(new_element))
8473 DrawLevelFieldCrumbledSandNeighbours(x, y);
8477 /* check if element under the player changes from accessible to unaccessible
8478 (needed for special case of dropping element which then changes) */
8479 /* (must be checked after creating new element for walkable group elements) */
8480 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8481 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8489 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8490 if (ELEM_IS_PLAYER(new_element))
8491 RelocatePlayer(x, y, new_element);
8494 ChangeCount[x][y]++; /* count number of changes in the same frame */
8496 TestIfBadThingTouchesPlayer(x, y);
8497 TestIfPlayerTouchesCustomElement(x, y);
8498 TestIfElementTouchesCustomElement(x, y);
8501 static void CreateField(int x, int y, int element)
8503 CreateFieldExt(x, y, element, FALSE);
8506 static void CreateElementFromChange(int x, int y, int element)
8508 element = GET_VALID_RUNTIME_ELEMENT(element);
8510 #if USE_STOP_CHANGED_ELEMENTS
8511 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8513 int old_element = Feld[x][y];
8515 /* prevent changed element from moving in same engine frame
8516 unless both old and new element can either fall or move */
8517 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8518 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8523 CreateFieldExt(x, y, element, TRUE);
8526 static boolean ChangeElement(int x, int y, int element, int page)
8528 struct ElementInfo *ei = &element_info[element];
8529 struct ElementChangeInfo *change = &ei->change_page[page];
8530 int ce_value = CustomValue[x][y];
8531 int ce_score = ei->collect_score;
8533 int old_element = Feld[x][y];
8535 /* always use default change event to prevent running into a loop */
8536 if (ChangeEvent[x][y] == -1)
8537 ChangeEvent[x][y] = CE_DELAY;
8539 if (ChangeEvent[x][y] == CE_DELAY)
8541 /* reset actual trigger element, trigger player and action element */
8542 change->actual_trigger_element = EL_EMPTY;
8543 change->actual_trigger_player = EL_PLAYER_1;
8544 change->actual_trigger_side = CH_SIDE_NONE;
8545 change->actual_trigger_ce_value = 0;
8546 change->actual_trigger_ce_score = 0;
8549 /* do not change elements more than a specified maximum number of changes */
8550 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8553 ChangeCount[x][y]++; /* count number of changes in the same frame */
8555 if (change->explode)
8562 if (change->use_target_content)
8564 boolean complete_replace = TRUE;
8565 boolean can_replace[3][3];
8568 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8571 boolean is_walkable;
8572 boolean is_diggable;
8573 boolean is_collectible;
8574 boolean is_removable;
8575 boolean is_destructible;
8576 int ex = x + xx - 1;
8577 int ey = y + yy - 1;
8578 int content_element = change->target_content.e[xx][yy];
8581 can_replace[xx][yy] = TRUE;
8583 if (ex == x && ey == y) /* do not check changing element itself */
8586 if (content_element == EL_EMPTY_SPACE)
8588 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8593 if (!IN_LEV_FIELD(ex, ey))
8595 can_replace[xx][yy] = FALSE;
8596 complete_replace = FALSE;
8603 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8604 e = MovingOrBlocked2Element(ex, ey);
8606 is_empty = (IS_FREE(ex, ey) ||
8607 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8609 is_walkable = (is_empty || IS_WALKABLE(e));
8610 is_diggable = (is_empty || IS_DIGGABLE(e));
8611 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8612 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8613 is_removable = (is_diggable || is_collectible);
8615 can_replace[xx][yy] =
8616 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8617 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8618 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8619 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8620 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8621 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8622 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8624 if (!can_replace[xx][yy])
8625 complete_replace = FALSE;
8628 if (!change->only_if_complete || complete_replace)
8630 boolean something_has_changed = FALSE;
8632 if (change->only_if_complete && change->use_random_replace &&
8633 RND(100) < change->random_percentage)
8636 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8638 int ex = x + xx - 1;
8639 int ey = y + yy - 1;
8640 int content_element;
8642 if (can_replace[xx][yy] && (!change->use_random_replace ||
8643 RND(100) < change->random_percentage))
8645 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8646 RemoveMovingField(ex, ey);
8648 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8650 content_element = change->target_content.e[xx][yy];
8651 target_element = GET_TARGET_ELEMENT(content_element, change,
8652 ce_value, ce_score);
8654 CreateElementFromChange(ex, ey, target_element);
8656 something_has_changed = TRUE;
8658 /* for symmetry reasons, freeze newly created border elements */
8659 if (ex != x || ey != y)
8660 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8664 if (something_has_changed)
8666 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8667 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8673 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8674 ce_value, ce_score);
8676 if (element == EL_DIAGONAL_GROWING ||
8677 element == EL_DIAGONAL_SHRINKING)
8679 target_element = Store[x][y];
8681 Store[x][y] = EL_EMPTY;
8684 CreateElementFromChange(x, y, target_element);
8686 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8687 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8690 /* this uses direct change before indirect change */
8691 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8696 #if USE_NEW_DELAYED_ACTION
8698 static void HandleElementChange(int x, int y, int page)
8700 int element = MovingOrBlocked2Element(x, y);
8701 struct ElementInfo *ei = &element_info[element];
8702 struct ElementChangeInfo *change = &ei->change_page[page];
8705 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8706 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8709 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8710 x, y, element, element_info[element].token_name);
8711 printf("HandleElementChange(): This should never happen!\n");
8716 /* this can happen with classic bombs on walkable, changing elements */
8717 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8720 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8721 ChangeDelay[x][y] = 0;
8727 if (ChangeDelay[x][y] == 0) /* initialize element change */
8729 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8731 if (change->can_change)
8733 ResetGfxAnimation(x, y);
8734 ResetRandomAnimationValue(x, y);
8736 if (change->pre_change_function)
8737 change->pre_change_function(x, y);
8741 ChangeDelay[x][y]--;
8743 if (ChangeDelay[x][y] != 0) /* continue element change */
8745 if (change->can_change)
8747 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8749 if (IS_ANIMATED(graphic))
8750 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8752 if (change->change_function)
8753 change->change_function(x, y);
8756 else /* finish element change */
8758 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8760 page = ChangePage[x][y];
8761 ChangePage[x][y] = -1;
8763 change = &ei->change_page[page];
8766 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8768 ChangeDelay[x][y] = 1; /* try change after next move step */
8769 ChangePage[x][y] = page; /* remember page to use for change */
8774 if (change->can_change)
8776 if (ChangeElement(x, y, element, page))
8778 if (change->post_change_function)
8779 change->post_change_function(x, y);
8783 if (change->has_action)
8784 ExecuteCustomElementAction(x, y, element, page);
8790 static void HandleElementChange(int x, int y, int page)
8792 int element = MovingOrBlocked2Element(x, y);
8793 struct ElementInfo *ei = &element_info[element];
8794 struct ElementChangeInfo *change = &ei->change_page[page];
8797 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8800 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8801 x, y, element, element_info[element].token_name);
8802 printf("HandleElementChange(): This should never happen!\n");
8807 /* this can happen with classic bombs on walkable, changing elements */
8808 if (!CAN_CHANGE(element))
8811 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8812 ChangeDelay[x][y] = 0;
8818 if (ChangeDelay[x][y] == 0) /* initialize element change */
8820 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8822 ResetGfxAnimation(x, y);
8823 ResetRandomAnimationValue(x, y);
8825 if (change->pre_change_function)
8826 change->pre_change_function(x, y);
8829 ChangeDelay[x][y]--;
8831 if (ChangeDelay[x][y] != 0) /* continue element change */
8833 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8835 if (IS_ANIMATED(graphic))
8836 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8838 if (change->change_function)
8839 change->change_function(x, y);
8841 else /* finish element change */
8843 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8845 page = ChangePage[x][y];
8846 ChangePage[x][y] = -1;
8848 change = &ei->change_page[page];
8851 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8853 ChangeDelay[x][y] = 1; /* try change after next move step */
8854 ChangePage[x][y] = page; /* remember page to use for change */
8859 if (ChangeElement(x, y, element, page))
8861 if (change->post_change_function)
8862 change->post_change_function(x, y);
8869 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8870 int trigger_element,
8876 boolean change_done_any = FALSE;
8877 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8880 if (!(trigger_events[trigger_element][trigger_event]))
8883 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8885 int element = EL_CUSTOM_START + i;
8886 boolean change_done = FALSE;
8889 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8890 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8893 for (p = 0; p < element_info[element].num_change_pages; p++)
8895 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8897 if (change->can_change_or_has_action &&
8898 change->has_event[trigger_event] &&
8899 change->trigger_side & trigger_side &&
8900 change->trigger_player & trigger_player &&
8901 change->trigger_page & trigger_page_bits &&
8902 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8904 change->actual_trigger_element = trigger_element;
8905 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8906 change->actual_trigger_side = trigger_side;
8907 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8908 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8910 if ((change->can_change && !change_done) || change->has_action)
8915 SCAN_PLAYFIELD(x, y)
8917 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8920 if (Feld[x][y] == element)
8922 if (change->can_change && !change_done)
8924 ChangeDelay[x][y] = 1;
8925 ChangeEvent[x][y] = trigger_event;
8927 HandleElementChange(x, y, p);
8929 #if USE_NEW_DELAYED_ACTION
8930 else if (change->has_action)
8932 ExecuteCustomElementAction(x, y, element, p);
8933 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8936 if (change->has_action)
8938 ExecuteCustomElementAction(x, y, element, p);
8939 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8945 if (change->can_change)
8948 change_done_any = TRUE;
8955 return change_done_any;
8958 static boolean CheckElementChangeExt(int x, int y,
8960 int trigger_element,
8965 boolean change_done = FALSE;
8968 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8969 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8972 if (Feld[x][y] == EL_BLOCKED)
8974 Blocked2Moving(x, y, &x, &y);
8975 element = Feld[x][y];
8979 /* check if element has already changed */
8980 if (Feld[x][y] != element)
8983 /* check if element has already changed or is about to change after moving */
8984 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8985 Feld[x][y] != element) ||
8987 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8988 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8989 ChangePage[x][y] != -1)))
8993 for (p = 0; p < element_info[element].num_change_pages; p++)
8995 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8997 boolean check_trigger_element =
8998 (trigger_event == CE_TOUCHING_X ||
8999 trigger_event == CE_HITTING_X ||
9000 trigger_event == CE_HIT_BY_X);
9002 if (change->can_change_or_has_action &&
9003 change->has_event[trigger_event] &&
9004 change->trigger_side & trigger_side &&
9005 change->trigger_player & trigger_player &&
9006 (!check_trigger_element ||
9007 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9009 change->actual_trigger_element = trigger_element;
9010 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9011 change->actual_trigger_side = trigger_side;
9012 change->actual_trigger_ce_value = CustomValue[x][y];
9013 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9015 /* special case: trigger element not at (x,y) position for some events */
9016 if (check_trigger_element)
9028 { 0, 0 }, { 0, 0 }, { 0, 0 },
9032 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9033 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9035 change->actual_trigger_ce_value = CustomValue[xx][yy];
9036 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9039 if (change->can_change && !change_done)
9041 ChangeDelay[x][y] = 1;
9042 ChangeEvent[x][y] = trigger_event;
9044 HandleElementChange(x, y, p);
9048 #if USE_NEW_DELAYED_ACTION
9049 else if (change->has_action)
9051 ExecuteCustomElementAction(x, y, element, p);
9052 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9055 if (change->has_action)
9057 ExecuteCustomElementAction(x, y, element, p);
9058 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9067 static void PlayPlayerSound(struct PlayerInfo *player)
9069 int jx = player->jx, jy = player->jy;
9070 int sound_element = player->artwork_element;
9071 int last_action = player->last_action_waiting;
9072 int action = player->action_waiting;
9074 if (player->is_waiting)
9076 if (action != last_action)
9077 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9079 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9083 if (action != last_action)
9084 StopSound(element_info[sound_element].sound[last_action]);
9086 if (last_action == ACTION_SLEEPING)
9087 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9091 static void PlayAllPlayersSound()
9095 for (i = 0; i < MAX_PLAYERS; i++)
9096 if (stored_player[i].active)
9097 PlayPlayerSound(&stored_player[i]);
9100 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9102 boolean last_waiting = player->is_waiting;
9103 int move_dir = player->MovDir;
9105 player->dir_waiting = move_dir;
9106 player->last_action_waiting = player->action_waiting;
9110 if (!last_waiting) /* not waiting -> waiting */
9112 player->is_waiting = TRUE;
9114 player->frame_counter_bored =
9116 game.player_boring_delay_fixed +
9117 SimpleRND(game.player_boring_delay_random);
9118 player->frame_counter_sleeping =
9120 game.player_sleeping_delay_fixed +
9121 SimpleRND(game.player_sleeping_delay_random);
9124 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9126 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9130 if (game.player_sleeping_delay_fixed +
9131 game.player_sleeping_delay_random > 0 &&
9132 player->anim_delay_counter == 0 &&
9133 player->post_delay_counter == 0 &&
9134 FrameCounter >= player->frame_counter_sleeping)
9135 player->is_sleeping = TRUE;
9136 else if (game.player_boring_delay_fixed +
9137 game.player_boring_delay_random > 0 &&
9138 FrameCounter >= player->frame_counter_bored)
9139 player->is_bored = TRUE;
9141 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9142 player->is_bored ? ACTION_BORING :
9146 if (player->is_sleeping && player->use_murphy)
9148 /* special case for sleeping Murphy when leaning against non-free tile */
9150 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9151 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9152 !IS_MOVING(player->jx - 1, player->jy)))
9154 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9155 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9156 !IS_MOVING(player->jx + 1, player->jy)))
9157 move_dir = MV_RIGHT;
9159 player->is_sleeping = FALSE;
9161 player->dir_waiting = move_dir;
9165 if (player->is_sleeping)
9167 if (player->num_special_action_sleeping > 0)
9169 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9171 int last_special_action = player->special_action_sleeping;
9172 int num_special_action = player->num_special_action_sleeping;
9173 int special_action =
9174 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9175 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9176 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9177 last_special_action + 1 : ACTION_SLEEPING);
9178 int special_graphic =
9179 el_act_dir2img(player->artwork_element, special_action, move_dir);
9181 player->anim_delay_counter =
9182 graphic_info[special_graphic].anim_delay_fixed +
9183 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9184 player->post_delay_counter =
9185 graphic_info[special_graphic].post_delay_fixed +
9186 SimpleRND(graphic_info[special_graphic].post_delay_random);
9188 player->special_action_sleeping = special_action;
9191 if (player->anim_delay_counter > 0)
9193 player->action_waiting = player->special_action_sleeping;
9194 player->anim_delay_counter--;
9196 else if (player->post_delay_counter > 0)
9198 player->post_delay_counter--;
9202 else if (player->is_bored)
9204 if (player->num_special_action_bored > 0)
9206 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9208 int special_action =
9209 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9210 int special_graphic =
9211 el_act_dir2img(player->artwork_element, special_action, move_dir);
9213 player->anim_delay_counter =
9214 graphic_info[special_graphic].anim_delay_fixed +
9215 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9216 player->post_delay_counter =
9217 graphic_info[special_graphic].post_delay_fixed +
9218 SimpleRND(graphic_info[special_graphic].post_delay_random);
9220 player->special_action_bored = special_action;
9223 if (player->anim_delay_counter > 0)
9225 player->action_waiting = player->special_action_bored;
9226 player->anim_delay_counter--;
9228 else if (player->post_delay_counter > 0)
9230 player->post_delay_counter--;
9235 else if (last_waiting) /* waiting -> not waiting */
9237 player->is_waiting = FALSE;
9238 player->is_bored = FALSE;
9239 player->is_sleeping = FALSE;
9241 player->frame_counter_bored = -1;
9242 player->frame_counter_sleeping = -1;
9244 player->anim_delay_counter = 0;
9245 player->post_delay_counter = 0;
9247 player->dir_waiting = player->MovDir;
9248 player->action_waiting = ACTION_DEFAULT;
9250 player->special_action_bored = ACTION_DEFAULT;
9251 player->special_action_sleeping = ACTION_DEFAULT;
9255 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9257 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9258 int left = player_action & JOY_LEFT;
9259 int right = player_action & JOY_RIGHT;
9260 int up = player_action & JOY_UP;
9261 int down = player_action & JOY_DOWN;
9262 int button1 = player_action & JOY_BUTTON_1;
9263 int button2 = player_action & JOY_BUTTON_2;
9264 int dx = (left ? -1 : right ? 1 : 0);
9265 int dy = (up ? -1 : down ? 1 : 0);
9267 if (!player->active || tape.pausing)
9273 snapped = SnapField(player, dx, dy);
9277 dropped = DropElement(player);
9279 moved = MovePlayer(player, dx, dy);
9282 if (tape.single_step && tape.recording && !tape.pausing)
9284 if (button1 || (dropped && !moved))
9286 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9287 SnapField(player, 0, 0); /* stop snapping */
9291 SetPlayerWaiting(player, FALSE);
9293 return player_action;
9297 /* no actions for this player (no input at player's configured device) */
9299 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9300 SnapField(player, 0, 0);
9301 CheckGravityMovementWhenNotMoving(player);
9303 if (player->MovPos == 0)
9304 SetPlayerWaiting(player, TRUE);
9306 if (player->MovPos == 0) /* needed for tape.playing */
9307 player->is_moving = FALSE;
9309 player->is_dropping = FALSE;
9310 player->is_dropping_pressed = FALSE;
9311 player->drop_pressed_delay = 0;
9317 static void CheckLevelTime()
9321 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9323 if (level.native_em_level->lev->home == 0) /* all players at home */
9325 local_player->LevelSolved = TRUE;
9326 AllPlayersGone = TRUE;
9328 level.native_em_level->lev->home = -1;
9331 if (level.native_em_level->ply[0]->alive == 0 &&
9332 level.native_em_level->ply[1]->alive == 0 &&
9333 level.native_em_level->ply[2]->alive == 0 &&
9334 level.native_em_level->ply[3]->alive == 0) /* all dead */
9335 AllPlayersGone = TRUE;
9338 if (TimeFrames >= FRAMES_PER_SECOND)
9343 for (i = 0; i < MAX_PLAYERS; i++)
9345 struct PlayerInfo *player = &stored_player[i];
9347 if (SHIELD_ON(player))
9349 player->shield_normal_time_left--;
9351 if (player->shield_deadly_time_left > 0)
9352 player->shield_deadly_time_left--;
9356 if (!level.use_step_counter)
9364 if (TimeLeft <= 10 && setup.time_limit)
9365 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9367 DrawGameValue_Time(TimeLeft);
9369 if (!TimeLeft && setup.time_limit)
9371 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9372 level.native_em_level->lev->killed_out_of_time = TRUE;
9374 for (i = 0; i < MAX_PLAYERS; i++)
9375 KillPlayer(&stored_player[i]);
9378 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9379 DrawGameValue_Time(TimePlayed);
9381 level.native_em_level->lev->time =
9382 (level.time == 0 ? TimePlayed : TimeLeft);
9385 if (tape.recording || tape.playing)
9386 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9390 void AdvanceFrameAndPlayerCounters(int player_nr)
9395 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9396 FrameCounter, FrameCounter + 1);
9399 /* advance frame counters (global frame counter and time frame counter) */
9403 /* advance player counters (counters for move delay, move animation etc.) */
9404 for (i = 0; i < MAX_PLAYERS; i++)
9406 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9407 int move_delay_value = stored_player[i].move_delay_value;
9408 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9410 if (!advance_player_counters) /* not all players may be affected */
9413 #if USE_NEW_PLAYER_ANIM
9414 if (move_frames == 0) /* less than one move per game frame */
9416 int stepsize = TILEX / move_delay_value;
9417 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9418 int count = (stored_player[i].is_moving ?
9419 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9421 if (count % delay == 0)
9426 stored_player[i].Frame += move_frames;
9428 if (stored_player[i].MovPos != 0)
9429 stored_player[i].StepFrame += move_frames;
9431 if (stored_player[i].move_delay > 0)
9432 stored_player[i].move_delay--;
9434 /* due to bugs in previous versions, counter must count up, not down */
9435 if (stored_player[i].push_delay != -1)
9436 stored_player[i].push_delay++;
9438 if (stored_player[i].drop_delay > 0)
9439 stored_player[i].drop_delay--;
9441 if (stored_player[i].is_dropping_pressed)
9442 stored_player[i].drop_pressed_delay++;
9446 void StartGameActions(boolean init_network_game, boolean record_tape,
9449 unsigned long new_random_seed = InitRND(random_seed);
9452 TapeStartRecording(new_random_seed);
9454 #if defined(NETWORK_AVALIABLE)
9455 if (init_network_game)
9457 SendToServer_StartPlaying();
9465 game_status = GAME_MODE_PLAYING;
9472 static unsigned long game_frame_delay = 0;
9473 unsigned long game_frame_delay_value;
9474 byte *recorded_player_action;
9475 byte summarized_player_action = 0;
9476 byte tape_action[MAX_PLAYERS];
9479 if (game.restart_level)
9480 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9482 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9484 if (level.native_em_level->lev->home == 0) /* all players at home */
9486 local_player->LevelSolved = TRUE;
9487 AllPlayersGone = TRUE;
9489 level.native_em_level->lev->home = -1;
9492 if (level.native_em_level->ply[0]->alive == 0 &&
9493 level.native_em_level->ply[1]->alive == 0 &&
9494 level.native_em_level->ply[2]->alive == 0 &&
9495 level.native_em_level->ply[3]->alive == 0) /* all dead */
9496 AllPlayersGone = TRUE;
9499 if (local_player->LevelSolved)
9502 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9505 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9508 game_frame_delay_value =
9509 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9511 if (tape.playing && tape.warp_forward && !tape.pausing)
9512 game_frame_delay_value = 0;
9514 /* ---------- main game synchronization point ---------- */
9516 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9518 if (network_playing && !network_player_action_received)
9520 /* try to get network player actions in time */
9522 #if defined(NETWORK_AVALIABLE)
9523 /* last chance to get network player actions without main loop delay */
9527 /* game was quit by network peer */
9528 if (game_status != GAME_MODE_PLAYING)
9531 if (!network_player_action_received)
9532 return; /* failed to get network player actions in time */
9534 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9540 /* at this point we know that we really continue executing the game */
9543 network_player_action_received = FALSE;
9546 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9548 if (tape.set_centered_player)
9550 game.centered_player_nr_next = tape.centered_player_nr_next;
9551 game.set_centered_player = TRUE;
9554 for (i = 0; i < MAX_PLAYERS; i++)
9556 summarized_player_action |= stored_player[i].action;
9558 if (!network_playing)
9559 stored_player[i].effective_action = stored_player[i].action;
9562 #if defined(NETWORK_AVALIABLE)
9563 if (network_playing)
9564 SendToServer_MovePlayer(summarized_player_action);
9567 if (!options.network && !setup.team_mode)
9568 local_player->effective_action = summarized_player_action;
9570 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9572 for (i = 0; i < MAX_PLAYERS; i++)
9573 stored_player[i].effective_action =
9574 (i == game.centered_player_nr ? summarized_player_action : 0);
9577 if (recorded_player_action != NULL)
9578 for (i = 0; i < MAX_PLAYERS; i++)
9579 stored_player[i].effective_action = recorded_player_action[i];
9581 for (i = 0; i < MAX_PLAYERS; i++)
9583 tape_action[i] = stored_player[i].effective_action;
9585 /* (this can only happen in the R'n'D game engine) */
9586 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9587 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9590 /* only record actions from input devices, but not programmed actions */
9592 TapeRecordAction(tape_action);
9594 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9596 GameActions_EM_Main();
9604 void GameActions_EM_Main()
9606 byte effective_action[MAX_PLAYERS];
9607 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9610 for (i = 0; i < MAX_PLAYERS; i++)
9611 effective_action[i] = stored_player[i].effective_action;
9613 GameActions_EM(effective_action, warp_mode);
9617 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9620 void GameActions_RND()
9622 int magic_wall_x = 0, magic_wall_y = 0;
9623 int i, x, y, element, graphic;
9625 InitPlayfieldScanModeVars();
9627 #if USE_ONE_MORE_CHANGE_PER_FRAME
9628 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9630 SCAN_PLAYFIELD(x, y)
9632 ChangeCount[x][y] = 0;
9633 ChangeEvent[x][y] = -1;
9639 if (game.set_centered_player)
9641 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9643 /* switching to "all players" only possible if all players fit to screen */
9644 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9646 game.centered_player_nr_next = game.centered_player_nr;
9647 game.set_centered_player = FALSE;
9650 /* do not switch focus to non-existing (or non-active) player */
9651 if (game.centered_player_nr_next >= 0 &&
9652 !stored_player[game.centered_player_nr_next].active)
9654 game.centered_player_nr_next = game.centered_player_nr;
9655 game.set_centered_player = FALSE;
9659 if (game.set_centered_player &&
9660 ScreenMovPos == 0) /* screen currently aligned at tile position */
9664 if (game.centered_player_nr_next == -1)
9666 setScreenCenteredToAllPlayers(&sx, &sy);
9670 sx = stored_player[game.centered_player_nr_next].jx;
9671 sy = stored_player[game.centered_player_nr_next].jy;
9674 game.centered_player_nr = game.centered_player_nr_next;
9675 game.set_centered_player = FALSE;
9677 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9678 DrawGameDoorValues();
9682 for (i = 0; i < MAX_PLAYERS; i++)
9684 int actual_player_action = stored_player[i].effective_action;
9687 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9688 - rnd_equinox_tetrachloride 048
9689 - rnd_equinox_tetrachloride_ii 096
9690 - rnd_emanuel_schmieg 002
9691 - doctor_sloan_ww 001, 020
9693 if (stored_player[i].MovPos == 0)
9694 CheckGravityMovement(&stored_player[i]);
9697 /* overwrite programmed action with tape action */
9698 if (stored_player[i].programmed_action)
9699 actual_player_action = stored_player[i].programmed_action;
9702 PlayerActions(&stored_player[i], actual_player_action);
9704 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9706 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9707 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9710 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9714 network_player_action_received = FALSE;
9717 ScrollScreen(NULL, SCROLL_GO_ON);
9719 /* for backwards compatibility, the following code emulates a fixed bug that
9720 occured when pushing elements (causing elements that just made their last
9721 pushing step to already (if possible) make their first falling step in the
9722 same game frame, which is bad); this code is also needed to use the famous
9723 "spring push bug" which is used in older levels and might be wanted to be
9724 used also in newer levels, but in this case the buggy pushing code is only
9725 affecting the "spring" element and no other elements */
9727 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9729 for (i = 0; i < MAX_PLAYERS; i++)
9731 struct PlayerInfo *player = &stored_player[i];
9735 if (player->active && player->is_pushing && player->is_moving &&
9737 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9738 Feld[x][y] == EL_SPRING))
9740 ContinueMoving(x, y);
9742 /* continue moving after pushing (this is actually a bug) */
9743 if (!IS_MOVING(x, y))
9752 SCAN_PLAYFIELD(x, y)
9754 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9757 ChangeCount[x][y] = 0;
9758 ChangeEvent[x][y] = -1;
9760 /* this must be handled before main playfield loop */
9761 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9764 if (MovDelay[x][y] <= 0)
9768 #if USE_NEW_SNAP_DELAY
9769 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9772 if (MovDelay[x][y] <= 0)
9775 DrawLevelField(x, y);
9777 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9783 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9785 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9786 printf("GameActions(): This should never happen!\n");
9788 ChangePage[x][y] = -1;
9793 if (WasJustMoving[x][y] > 0)
9794 WasJustMoving[x][y]--;
9795 if (WasJustFalling[x][y] > 0)
9796 WasJustFalling[x][y]--;
9797 if (CheckCollision[x][y] > 0)
9798 CheckCollision[x][y]--;
9802 /* reset finished pushing action (not done in ContinueMoving() to allow
9803 continuous pushing animation for elements with zero push delay) */
9804 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9806 ResetGfxAnimation(x, y);
9807 DrawLevelField(x, y);
9811 if (IS_BLOCKED(x, y))
9815 Blocked2Moving(x, y, &oldx, &oldy);
9816 if (!IS_MOVING(oldx, oldy))
9818 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9819 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9820 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9821 printf("GameActions(): This should never happen!\n");
9828 SCAN_PLAYFIELD(x, y)
9830 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9833 element = Feld[x][y];
9834 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9837 printf("::: %d,%d\n", x, y);
9839 if (element == EL_ROCK)
9840 printf("::: Yo man! Rocks can fall!\n");
9844 ResetGfxFrame(x, y, TRUE);
9846 if (graphic_info[graphic].anim_global_sync)
9847 GfxFrame[x][y] = FrameCounter;
9848 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9850 int old_gfx_frame = GfxFrame[x][y];
9852 GfxFrame[x][y] = CustomValue[x][y];
9855 if (GfxFrame[x][y] != old_gfx_frame)
9857 DrawLevelGraphicAnimation(x, y, graphic);
9859 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9861 int old_gfx_frame = GfxFrame[x][y];
9863 GfxFrame[x][y] = element_info[element].collect_score;
9866 if (GfxFrame[x][y] != old_gfx_frame)
9868 DrawLevelGraphicAnimation(x, y, graphic);
9870 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9872 int old_gfx_frame = GfxFrame[x][y];
9874 GfxFrame[x][y] = ChangeDelay[x][y];
9877 if (GfxFrame[x][y] != old_gfx_frame)
9879 DrawLevelGraphicAnimation(x, y, graphic);
9883 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9884 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9885 ResetRandomAnimationValue(x, y);
9887 SetRandomAnimationValue(x, y);
9889 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9891 if (IS_INACTIVE(element))
9893 if (IS_ANIMATED(graphic))
9894 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9899 /* this may take place after moving, so 'element' may have changed */
9900 if (IS_CHANGING(x, y) &&
9901 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9903 int page = element_info[element].event_page_nr[CE_DELAY];
9905 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9909 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9913 if (element == EL_CUSTOM_255)
9914 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9918 HandleElementChange(x, y, page);
9920 if (CAN_CHANGE(element))
9921 HandleElementChange(x, y, page);
9923 if (HAS_ACTION(element))
9924 ExecuteCustomElementAction(x, y, element, page);
9929 element = Feld[x][y];
9930 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9933 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9937 element = Feld[x][y];
9938 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9940 if (IS_ANIMATED(graphic) &&
9943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9945 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9946 EdelsteinFunkeln(x, y);
9948 else if ((element == EL_ACID ||
9949 element == EL_EXIT_OPEN ||
9950 element == EL_SP_EXIT_OPEN ||
9951 element == EL_SP_TERMINAL ||
9952 element == EL_SP_TERMINAL_ACTIVE ||
9953 element == EL_EXTRA_TIME ||
9954 element == EL_SHIELD_NORMAL ||
9955 element == EL_SHIELD_DEADLY) &&
9956 IS_ANIMATED(graphic))
9957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9958 else if (IS_MOVING(x, y))
9959 ContinueMoving(x, y);
9960 else if (IS_ACTIVE_BOMB(element))
9961 CheckDynamite(x, y);
9962 else if (element == EL_AMOEBA_GROWING)
9963 AmoebeWaechst(x, y);
9964 else if (element == EL_AMOEBA_SHRINKING)
9965 AmoebaDisappearing(x, y);
9967 #if !USE_NEW_AMOEBA_CODE
9968 else if (IS_AMOEBALIVE(element))
9969 AmoebeAbleger(x, y);
9972 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9974 else if (element == EL_EXIT_CLOSED)
9976 else if (element == EL_SP_EXIT_CLOSED)
9978 else if (element == EL_EXPANDABLE_WALL_GROWING)
9980 else if (element == EL_EXPANDABLE_WALL ||
9981 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9982 element == EL_EXPANDABLE_WALL_VERTICAL ||
9983 element == EL_EXPANDABLE_WALL_ANY)
9985 else if (element == EL_FLAMES)
9986 CheckForDragon(x, y);
9987 else if (element == EL_EXPLOSION)
9988 ; /* drawing of correct explosion animation is handled separately */
9989 else if (element == EL_ELEMENT_SNAPPING ||
9990 element == EL_DIAGONAL_SHRINKING ||
9991 element == EL_DIAGONAL_GROWING)
9994 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9996 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9999 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10000 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10003 if (element == EL_CUSTOM_255 ||
10004 element == EL_CUSTOM_256)
10005 DrawLevelGraphicAnimation(x, y, graphic);
10008 if (IS_BELT_ACTIVE(element))
10009 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10011 if (game.magic_wall_active)
10013 int jx = local_player->jx, jy = local_player->jy;
10015 /* play the element sound at the position nearest to the player */
10016 if ((element == EL_MAGIC_WALL_FULL ||
10017 element == EL_MAGIC_WALL_ACTIVE ||
10018 element == EL_MAGIC_WALL_EMPTYING ||
10019 element == EL_BD_MAGIC_WALL_FULL ||
10020 element == EL_BD_MAGIC_WALL_ACTIVE ||
10021 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10022 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10030 #if USE_NEW_AMOEBA_CODE
10031 /* new experimental amoeba growth stuff */
10032 if (!(FrameCounter % 8))
10034 static unsigned long random = 1684108901;
10036 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10038 x = RND(lev_fieldx);
10039 y = RND(lev_fieldy);
10040 element = Feld[x][y];
10042 if (!IS_PLAYER(x,y) &&
10043 (element == EL_EMPTY ||
10044 CAN_GROW_INTO(element) ||
10045 element == EL_QUICKSAND_EMPTY ||
10046 element == EL_ACID_SPLASH_LEFT ||
10047 element == EL_ACID_SPLASH_RIGHT))
10049 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10050 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10051 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10052 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10053 Feld[x][y] = EL_AMOEBA_DROP;
10056 random = random * 129 + 1;
10062 if (game.explosions_delayed)
10065 game.explosions_delayed = FALSE;
10068 SCAN_PLAYFIELD(x, y)
10070 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10073 element = Feld[x][y];
10075 if (ExplodeField[x][y])
10076 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10077 else if (element == EL_EXPLOSION)
10078 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10080 ExplodeField[x][y] = EX_TYPE_NONE;
10083 game.explosions_delayed = TRUE;
10086 if (game.magic_wall_active)
10088 if (!(game.magic_wall_time_left % 4))
10090 int element = Feld[magic_wall_x][magic_wall_y];
10092 if (element == EL_BD_MAGIC_WALL_FULL ||
10093 element == EL_BD_MAGIC_WALL_ACTIVE ||
10094 element == EL_BD_MAGIC_WALL_EMPTYING)
10095 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10097 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10100 if (game.magic_wall_time_left > 0)
10102 game.magic_wall_time_left--;
10103 if (!game.magic_wall_time_left)
10106 SCAN_PLAYFIELD(x, y)
10108 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10111 element = Feld[x][y];
10113 if (element == EL_MAGIC_WALL_ACTIVE ||
10114 element == EL_MAGIC_WALL_FULL)
10116 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10117 DrawLevelField(x, y);
10119 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10120 element == EL_BD_MAGIC_WALL_FULL)
10122 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10123 DrawLevelField(x, y);
10127 game.magic_wall_active = FALSE;
10132 if (game.light_time_left > 0)
10134 game.light_time_left--;
10136 if (game.light_time_left == 0)
10137 RedrawAllLightSwitchesAndInvisibleElements();
10140 if (game.timegate_time_left > 0)
10142 game.timegate_time_left--;
10144 if (game.timegate_time_left == 0)
10145 CloseAllOpenTimegates();
10148 if (game.lenses_time_left > 0)
10150 game.lenses_time_left--;
10152 if (game.lenses_time_left == 0)
10153 RedrawAllInvisibleElementsForLenses();
10156 if (game.magnify_time_left > 0)
10158 game.magnify_time_left--;
10160 if (game.magnify_time_left == 0)
10161 RedrawAllInvisibleElementsForMagnifier();
10164 for (i = 0; i < MAX_PLAYERS; i++)
10166 struct PlayerInfo *player = &stored_player[i];
10168 if (SHIELD_ON(player))
10170 if (player->shield_deadly_time_left)
10171 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10172 else if (player->shield_normal_time_left)
10173 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10180 PlayAllPlayersSound();
10182 if (options.debug) /* calculate frames per second */
10184 static unsigned long fps_counter = 0;
10185 static int fps_frames = 0;
10186 unsigned long fps_delay_ms = Counter() - fps_counter;
10190 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10192 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10195 fps_counter = Counter();
10198 redraw_mask |= REDRAW_FPS;
10201 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10203 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10205 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10207 local_player->show_envelope = 0;
10210 /* use random number generator in every frame to make it less predictable */
10211 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10215 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10217 int min_x = x, min_y = y, max_x = x, max_y = y;
10220 for (i = 0; i < MAX_PLAYERS; i++)
10222 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10224 if (!stored_player[i].active || &stored_player[i] == player)
10227 min_x = MIN(min_x, jx);
10228 min_y = MIN(min_y, jy);
10229 max_x = MAX(max_x, jx);
10230 max_y = MAX(max_y, jy);
10233 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10236 static boolean AllPlayersInVisibleScreen()
10240 for (i = 0; i < MAX_PLAYERS; i++)
10242 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10244 if (!stored_player[i].active)
10247 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10254 void ScrollLevel(int dx, int dy)
10256 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10259 BlitBitmap(drawto_field, drawto_field,
10260 FX + TILEX * (dx == -1) - softscroll_offset,
10261 FY + TILEY * (dy == -1) - softscroll_offset,
10262 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10263 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10264 FX + TILEX * (dx == 1) - softscroll_offset,
10265 FY + TILEY * (dy == 1) - softscroll_offset);
10269 x = (dx == 1 ? BX1 : BX2);
10270 for (y = BY1; y <= BY2; y++)
10271 DrawScreenField(x, y);
10276 y = (dy == 1 ? BY1 : BY2);
10277 for (x = BX1; x <= BX2; x++)
10278 DrawScreenField(x, y);
10281 redraw_mask |= REDRAW_FIELD;
10284 static boolean canFallDown(struct PlayerInfo *player)
10286 int jx = player->jx, jy = player->jy;
10288 return (IN_LEV_FIELD(jx, jy + 1) &&
10289 (IS_FREE(jx, jy + 1) ||
10290 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10291 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10292 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10295 static boolean canPassField(int x, int y, int move_dir)
10297 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10298 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10299 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10300 int nextx = x + dx;
10301 int nexty = y + dy;
10302 int element = Feld[x][y];
10304 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10305 !CAN_MOVE(element) &&
10306 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10307 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10308 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10311 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10313 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10314 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10315 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10319 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10320 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10321 (IS_DIGGABLE(Feld[newx][newy]) ||
10322 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10323 canPassField(newx, newy, move_dir)));
10326 static void CheckGravityMovement(struct PlayerInfo *player)
10328 #if USE_PLAYER_GRAVITY
10329 if (player->gravity && !player->programmed_action)
10331 if (game.gravity && !player->programmed_action)
10334 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10335 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10336 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10337 int jx = player->jx, jy = player->jy;
10338 boolean player_is_moving_to_valid_field =
10339 (!player_is_snapping &&
10340 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10341 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10342 boolean player_can_fall_down = canFallDown(player);
10344 if (player_can_fall_down &&
10345 !player_is_moving_to_valid_field)
10346 player->programmed_action = MV_DOWN;
10350 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10352 return CheckGravityMovement(player);
10354 #if USE_PLAYER_GRAVITY
10355 if (player->gravity && !player->programmed_action)
10357 if (game.gravity && !player->programmed_action)
10360 int jx = player->jx, jy = player->jy;
10361 boolean field_under_player_is_free =
10362 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10363 boolean player_is_standing_on_valid_field =
10364 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10365 (IS_WALKABLE(Feld[jx][jy]) &&
10366 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10368 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10369 player->programmed_action = MV_DOWN;
10374 MovePlayerOneStep()
10375 -----------------------------------------------------------------------------
10376 dx, dy: direction (non-diagonal) to try to move the player to
10377 real_dx, real_dy: direction as read from input device (can be diagonal)
10380 boolean MovePlayerOneStep(struct PlayerInfo *player,
10381 int dx, int dy, int real_dx, int real_dy)
10383 int jx = player->jx, jy = player->jy;
10384 int new_jx = jx + dx, new_jy = jy + dy;
10385 #if !USE_FIXED_DONT_RUN_INTO
10389 boolean player_can_move = !player->cannot_move;
10391 if (!player->active || (!dx && !dy))
10392 return MP_NO_ACTION;
10394 player->MovDir = (dx < 0 ? MV_LEFT :
10395 dx > 0 ? MV_RIGHT :
10397 dy > 0 ? MV_DOWN : MV_NONE);
10399 if (!IN_LEV_FIELD(new_jx, new_jy))
10400 return MP_NO_ACTION;
10402 if (!player_can_move)
10405 if (player->MovPos == 0)
10407 player->is_moving = FALSE;
10408 player->is_digging = FALSE;
10409 player->is_collecting = FALSE;
10410 player->is_snapping = FALSE;
10411 player->is_pushing = FALSE;
10414 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10415 SnapField(player, 0, 0);
10419 return MP_NO_ACTION;
10424 if (!options.network && game.centered_player_nr == -1 &&
10425 !AllPlayersInSight(player, new_jx, new_jy))
10426 return MP_NO_ACTION;
10428 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10429 return MP_NO_ACTION;
10432 #if !USE_FIXED_DONT_RUN_INTO
10433 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10435 /* (moved to DigField()) */
10436 if (player_can_move && DONT_RUN_INTO(element))
10438 if (element == EL_ACID && dx == 0 && dy == 1)
10440 SplashAcid(new_jx, new_jy);
10441 Feld[jx][jy] = EL_PLAYER_1;
10442 InitMovingField(jx, jy, MV_DOWN);
10443 Store[jx][jy] = EL_ACID;
10444 ContinueMoving(jx, jy);
10445 BuryPlayer(player);
10448 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10454 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10456 #if USE_FIXED_DONT_RUN_INTO
10457 if (can_move == MP_DONT_RUN_INTO)
10461 if (can_move != MP_MOVING)
10464 #if USE_FIXED_DONT_RUN_INTO
10467 /* check if DigField() has caused relocation of the player */
10468 if (player->jx != jx || player->jy != jy)
10469 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10471 StorePlayer[jx][jy] = 0;
10472 player->last_jx = jx;
10473 player->last_jy = jy;
10474 player->jx = new_jx;
10475 player->jy = new_jy;
10476 StorePlayer[new_jx][new_jy] = player->element_nr;
10478 if (player->move_delay_value_next != -1)
10480 player->move_delay_value = player->move_delay_value_next;
10481 player->move_delay_value_next = -1;
10485 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10487 player->step_counter++;
10489 PlayerVisit[jx][jy] = FrameCounter;
10491 ScrollPlayer(player, SCROLL_INIT);
10496 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10498 int jx = player->jx, jy = player->jy;
10499 int old_jx = jx, old_jy = jy;
10500 int moved = MP_NO_ACTION;
10502 if (!player->active)
10507 if (player->MovPos == 0)
10509 player->is_moving = FALSE;
10510 player->is_digging = FALSE;
10511 player->is_collecting = FALSE;
10512 player->is_snapping = FALSE;
10513 player->is_pushing = FALSE;
10519 if (player->move_delay > 0)
10522 player->move_delay = -1; /* set to "uninitialized" value */
10524 /* store if player is automatically moved to next field */
10525 player->is_auto_moving = (player->programmed_action != MV_NONE);
10527 /* remove the last programmed player action */
10528 player->programmed_action = 0;
10530 if (player->MovPos)
10532 /* should only happen if pre-1.2 tape recordings are played */
10533 /* this is only for backward compatibility */
10535 int original_move_delay_value = player->move_delay_value;
10538 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10542 /* scroll remaining steps with finest movement resolution */
10543 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10545 while (player->MovPos)
10547 ScrollPlayer(player, SCROLL_GO_ON);
10548 ScrollScreen(NULL, SCROLL_GO_ON);
10550 AdvanceFrameAndPlayerCounters(player->index_nr);
10556 player->move_delay_value = original_move_delay_value;
10559 if (player->last_move_dir & MV_HORIZONTAL)
10561 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10562 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10566 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10567 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10574 if (moved & MP_MOVING && !ScreenMovPos &&
10575 (player->index_nr == game.centered_player_nr ||
10576 game.centered_player_nr == -1))
10578 if (moved & MP_MOVING && !ScreenMovPos &&
10579 (player == local_player || !options.network))
10582 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10583 int offset = (setup.scroll_delay ? 3 : 0);
10585 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10587 /* actual player has left the screen -- scroll in that direction */
10588 if (jx != old_jx) /* player has moved horizontally */
10589 scroll_x += (jx - old_jx);
10590 else /* player has moved vertically */
10591 scroll_y += (jy - old_jy);
10595 if (jx != old_jx) /* player has moved horizontally */
10597 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10598 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10599 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10601 /* don't scroll over playfield boundaries */
10602 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10603 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10605 /* don't scroll more than one field at a time */
10606 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10608 /* don't scroll against the player's moving direction */
10609 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10610 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10611 scroll_x = old_scroll_x;
10613 else /* player has moved vertically */
10615 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10616 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10617 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10619 /* don't scroll over playfield boundaries */
10620 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10621 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10623 /* don't scroll more than one field at a time */
10624 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10626 /* don't scroll against the player's moving direction */
10627 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10628 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10629 scroll_y = old_scroll_y;
10633 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10636 if (!options.network && game.centered_player_nr == -1 &&
10637 !AllPlayersInVisibleScreen())
10639 scroll_x = old_scroll_x;
10640 scroll_y = old_scroll_y;
10644 if (!options.network && !AllPlayersInVisibleScreen())
10646 scroll_x = old_scroll_x;
10647 scroll_y = old_scroll_y;
10652 ScrollScreen(player, SCROLL_INIT);
10653 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10658 player->StepFrame = 0;
10660 if (moved & MP_MOVING)
10662 if (old_jx != jx && old_jy == jy)
10663 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10664 else if (old_jx == jx && old_jy != jy)
10665 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10667 DrawLevelField(jx, jy); /* for "crumbled sand" */
10669 player->last_move_dir = player->MovDir;
10670 player->is_moving = TRUE;
10671 player->is_snapping = FALSE;
10672 player->is_switching = FALSE;
10673 player->is_dropping = FALSE;
10674 player->is_dropping_pressed = FALSE;
10675 player->drop_pressed_delay = 0;
10679 CheckGravityMovementWhenNotMoving(player);
10681 player->is_moving = FALSE;
10683 /* at this point, the player is allowed to move, but cannot move right now
10684 (e.g. because of something blocking the way) -- ensure that the player
10685 is also allowed to move in the next frame (in old versions before 3.1.1,
10686 the player was forced to wait again for eight frames before next try) */
10688 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10689 player->move_delay = 0; /* allow direct movement in the next frame */
10692 if (player->move_delay == -1) /* not yet initialized by DigField() */
10693 player->move_delay = player->move_delay_value;
10695 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10697 TestIfPlayerTouchesBadThing(jx, jy);
10698 TestIfPlayerTouchesCustomElement(jx, jy);
10701 if (!player->active)
10702 RemovePlayer(player);
10707 void ScrollPlayer(struct PlayerInfo *player, int mode)
10709 int jx = player->jx, jy = player->jy;
10710 int last_jx = player->last_jx, last_jy = player->last_jy;
10711 int move_stepsize = TILEX / player->move_delay_value;
10713 #if USE_NEW_PLAYER_SPEED
10714 if (!player->active)
10717 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10720 if (!player->active || player->MovPos == 0)
10724 if (mode == SCROLL_INIT)
10726 player->actual_frame_counter = FrameCounter;
10727 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10729 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10730 Feld[last_jx][last_jy] == EL_EMPTY)
10732 int last_field_block_delay = 0; /* start with no blocking at all */
10733 int block_delay_adjustment = player->block_delay_adjustment;
10735 /* if player blocks last field, add delay for exactly one move */
10736 if (player->block_last_field)
10738 last_field_block_delay += player->move_delay_value;
10740 /* when blocking enabled, prevent moving up despite gravity */
10741 #if USE_PLAYER_GRAVITY
10742 if (player->gravity && player->MovDir == MV_UP)
10743 block_delay_adjustment = -1;
10745 if (game.gravity && player->MovDir == MV_UP)
10746 block_delay_adjustment = -1;
10750 /* add block delay adjustment (also possible when not blocking) */
10751 last_field_block_delay += block_delay_adjustment;
10753 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10754 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10757 #if USE_NEW_PLAYER_SPEED
10758 if (player->MovPos != 0) /* player has not yet reached destination */
10764 else if (!FrameReached(&player->actual_frame_counter, 1))
10768 printf("::: player->MovPos: %d -> %d\n",
10770 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10773 #if USE_NEW_PLAYER_SPEED
10774 if (player->MovPos != 0)
10776 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10777 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10779 /* before DrawPlayer() to draw correct player graphic for this case */
10780 if (player->MovPos == 0)
10781 CheckGravityMovement(player);
10784 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10785 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10787 /* before DrawPlayer() to draw correct player graphic for this case */
10788 if (player->MovPos == 0)
10789 CheckGravityMovement(player);
10792 if (player->MovPos == 0) /* player reached destination field */
10795 printf("::: player reached destination field\n");
10798 if (player->move_delay_reset_counter > 0)
10800 player->move_delay_reset_counter--;
10802 if (player->move_delay_reset_counter == 0)
10804 /* continue with normal speed after quickly moving through gate */
10805 HALVE_PLAYER_SPEED(player);
10807 /* be able to make the next move without delay */
10808 player->move_delay = 0;
10812 player->last_jx = jx;
10813 player->last_jy = jy;
10815 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10816 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10817 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10819 DrawPlayer(player); /* needed here only to cleanup last field */
10820 RemovePlayer(player);
10822 if (local_player->friends_still_needed == 0 ||
10823 IS_SP_ELEMENT(Feld[jx][jy]))
10824 player->LevelSolved = player->GameOver = TRUE;
10827 /* this breaks one level: "machine", level 000 */
10829 int move_direction = player->MovDir;
10830 int enter_side = MV_DIR_OPPOSITE(move_direction);
10831 int leave_side = move_direction;
10832 int old_jx = last_jx;
10833 int old_jy = last_jy;
10834 int old_element = Feld[old_jx][old_jy];
10835 int new_element = Feld[jx][jy];
10837 if (IS_CUSTOM_ELEMENT(old_element))
10838 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10840 player->index_bit, leave_side);
10842 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10843 CE_PLAYER_LEAVES_X,
10844 player->index_bit, leave_side);
10846 if (IS_CUSTOM_ELEMENT(new_element))
10847 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10848 player->index_bit, enter_side);
10850 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10851 CE_PLAYER_ENTERS_X,
10852 player->index_bit, enter_side);
10854 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10855 CE_MOVE_OF_X, move_direction);
10858 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10860 TestIfPlayerTouchesBadThing(jx, jy);
10861 TestIfPlayerTouchesCustomElement(jx, jy);
10863 /* needed because pushed element has not yet reached its destination,
10864 so it would trigger a change event at its previous field location */
10865 if (!player->is_pushing)
10866 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10868 if (!player->active)
10869 RemovePlayer(player);
10872 if (level.use_step_counter)
10882 if (TimeLeft <= 10 && setup.time_limit)
10883 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10885 DrawGameValue_Time(TimeLeft);
10887 if (!TimeLeft && setup.time_limit)
10888 for (i = 0; i < MAX_PLAYERS; i++)
10889 KillPlayer(&stored_player[i]);
10891 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10892 DrawGameValue_Time(TimePlayed);
10895 if (tape.single_step && tape.recording && !tape.pausing &&
10896 !player->programmed_action)
10897 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10901 void ScrollScreen(struct PlayerInfo *player, int mode)
10903 static unsigned long screen_frame_counter = 0;
10905 if (mode == SCROLL_INIT)
10907 /* set scrolling step size according to actual player's moving speed */
10908 ScrollStepSize = TILEX / player->move_delay_value;
10910 screen_frame_counter = FrameCounter;
10911 ScreenMovDir = player->MovDir;
10912 ScreenMovPos = player->MovPos;
10913 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10916 else if (!FrameReached(&screen_frame_counter, 1))
10921 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10922 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10923 redraw_mask |= REDRAW_FIELD;
10926 ScreenMovDir = MV_NONE;
10929 void TestIfPlayerTouchesCustomElement(int x, int y)
10931 static int xy[4][2] =
10938 static int trigger_sides[4][2] =
10940 /* center side border side */
10941 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10942 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10943 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10944 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10946 static int touch_dir[4] =
10948 MV_LEFT | MV_RIGHT,
10953 int center_element = Feld[x][y]; /* should always be non-moving! */
10956 for (i = 0; i < NUM_DIRECTIONS; i++)
10958 int xx = x + xy[i][0];
10959 int yy = y + xy[i][1];
10960 int center_side = trigger_sides[i][0];
10961 int border_side = trigger_sides[i][1];
10962 int border_element;
10964 if (!IN_LEV_FIELD(xx, yy))
10967 if (IS_PLAYER(x, y))
10969 struct PlayerInfo *player = PLAYERINFO(x, y);
10971 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10972 border_element = Feld[xx][yy]; /* may be moving! */
10973 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10974 border_element = Feld[xx][yy];
10975 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10976 border_element = MovingOrBlocked2Element(xx, yy);
10978 continue; /* center and border element do not touch */
10980 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10981 player->index_bit, border_side);
10982 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10983 CE_PLAYER_TOUCHES_X,
10984 player->index_bit, border_side);
10986 else if (IS_PLAYER(xx, yy))
10988 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10990 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10992 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10993 continue; /* center and border element do not touch */
10996 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10997 player->index_bit, center_side);
10998 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10999 CE_PLAYER_TOUCHES_X,
11000 player->index_bit, center_side);
11006 #if USE_ELEMENT_TOUCHING_BUGFIX
11008 void TestIfElementTouchesCustomElement(int x, int y)
11010 static int xy[4][2] =
11017 static int trigger_sides[4][2] =
11019 /* center side border side */
11020 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11021 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11022 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11023 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11025 static int touch_dir[4] =
11027 MV_LEFT | MV_RIGHT,
11032 boolean change_center_element = FALSE;
11033 int center_element = Feld[x][y]; /* should always be non-moving! */
11034 int border_element_old[NUM_DIRECTIONS];
11037 for (i = 0; i < NUM_DIRECTIONS; i++)
11039 int xx = x + xy[i][0];
11040 int yy = y + xy[i][1];
11041 int border_element;
11043 border_element_old[i] = -1;
11045 if (!IN_LEV_FIELD(xx, yy))
11048 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11049 border_element = Feld[xx][yy]; /* may be moving! */
11050 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11051 border_element = Feld[xx][yy];
11052 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11053 border_element = MovingOrBlocked2Element(xx, yy);
11055 continue; /* center and border element do not touch */
11057 border_element_old[i] = border_element;
11060 for (i = 0; i < NUM_DIRECTIONS; i++)
11062 int xx = x + xy[i][0];
11063 int yy = y + xy[i][1];
11064 int center_side = trigger_sides[i][0];
11065 int border_element = border_element_old[i];
11067 if (border_element == -1)
11070 /* check for change of border element */
11071 CheckElementChangeBySide(xx, yy, border_element, center_element,
11072 CE_TOUCHING_X, center_side);
11075 for (i = 0; i < NUM_DIRECTIONS; i++)
11077 int border_side = trigger_sides[i][1];
11078 int border_element = border_element_old[i];
11080 if (border_element == -1)
11083 /* check for change of center element (but change it only once) */
11084 if (!change_center_element)
11085 change_center_element =
11086 CheckElementChangeBySide(x, y, center_element, border_element,
11087 CE_TOUCHING_X, border_side);
11093 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11095 static int xy[4][2] =
11102 static int trigger_sides[4][2] =
11104 /* center side border side */
11105 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11106 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11107 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11108 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11110 static int touch_dir[4] =
11112 MV_LEFT | MV_RIGHT,
11117 boolean change_center_element = FALSE;
11118 int center_element = Feld[x][y]; /* should always be non-moving! */
11121 for (i = 0; i < NUM_DIRECTIONS; i++)
11123 int xx = x + xy[i][0];
11124 int yy = y + xy[i][1];
11125 int center_side = trigger_sides[i][0];
11126 int border_side = trigger_sides[i][1];
11127 int border_element;
11129 if (!IN_LEV_FIELD(xx, yy))
11132 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11133 border_element = Feld[xx][yy]; /* may be moving! */
11134 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11135 border_element = Feld[xx][yy];
11136 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11137 border_element = MovingOrBlocked2Element(xx, yy);
11139 continue; /* center and border element do not touch */
11141 /* check for change of center element (but change it only once) */
11142 if (!change_center_element)
11143 change_center_element =
11144 CheckElementChangeBySide(x, y, center_element, border_element,
11145 CE_TOUCHING_X, border_side);
11147 /* check for change of border element */
11148 CheckElementChangeBySide(xx, yy, border_element, center_element,
11149 CE_TOUCHING_X, center_side);
11155 void TestIfElementHitsCustomElement(int x, int y, int direction)
11157 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11158 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11159 int hitx = x + dx, hity = y + dy;
11160 int hitting_element = Feld[x][y];
11161 int touched_element;
11163 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11166 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11167 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11169 if (IN_LEV_FIELD(hitx, hity))
11171 int opposite_direction = MV_DIR_OPPOSITE(direction);
11172 int hitting_side = direction;
11173 int touched_side = opposite_direction;
11174 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11175 MovDir[hitx][hity] != direction ||
11176 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11182 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11183 CE_HITTING_X, touched_side);
11185 CheckElementChangeBySide(hitx, hity, touched_element,
11186 hitting_element, CE_HIT_BY_X, hitting_side);
11188 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11189 CE_HIT_BY_SOMETHING, opposite_direction);
11193 /* "hitting something" is also true when hitting the playfield border */
11194 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11195 CE_HITTING_SOMETHING, direction);
11199 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11201 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11202 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11203 int hitx = x + dx, hity = y + dy;
11204 int hitting_element = Feld[x][y];
11205 int touched_element;
11207 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11208 !IS_FREE(hitx, hity) &&
11209 (!IS_MOVING(hitx, hity) ||
11210 MovDir[hitx][hity] != direction ||
11211 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11214 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11218 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11222 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11223 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11225 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11226 EP_CAN_SMASH_EVERYTHING, direction);
11228 if (IN_LEV_FIELD(hitx, hity))
11230 int opposite_direction = MV_DIR_OPPOSITE(direction);
11231 int hitting_side = direction;
11232 int touched_side = opposite_direction;
11234 int touched_element = MovingOrBlocked2Element(hitx, hity);
11237 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11238 MovDir[hitx][hity] != direction ||
11239 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11248 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11249 CE_SMASHED_BY_SOMETHING, opposite_direction);
11251 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11252 CE_OTHER_IS_SMASHING, touched_side);
11254 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11255 CE_OTHER_GETS_SMASHED, hitting_side);
11261 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11263 int i, kill_x = -1, kill_y = -1;
11265 int bad_element = -1;
11266 static int test_xy[4][2] =
11273 static int test_dir[4] =
11281 for (i = 0; i < NUM_DIRECTIONS; i++)
11283 int test_x, test_y, test_move_dir, test_element;
11285 test_x = good_x + test_xy[i][0];
11286 test_y = good_y + test_xy[i][1];
11288 if (!IN_LEV_FIELD(test_x, test_y))
11292 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11294 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11296 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11297 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11299 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11300 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11304 bad_element = test_element;
11310 if (kill_x != -1 || kill_y != -1)
11312 if (IS_PLAYER(good_x, good_y))
11314 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11316 if (player->shield_deadly_time_left > 0 &&
11317 !IS_INDESTRUCTIBLE(bad_element))
11318 Bang(kill_x, kill_y);
11319 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11320 KillPlayer(player);
11323 Bang(good_x, good_y);
11327 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11329 int i, kill_x = -1, kill_y = -1;
11330 int bad_element = Feld[bad_x][bad_y];
11331 static int test_xy[4][2] =
11338 static int touch_dir[4] =
11340 MV_LEFT | MV_RIGHT,
11345 static int test_dir[4] =
11353 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11356 for (i = 0; i < NUM_DIRECTIONS; i++)
11358 int test_x, test_y, test_move_dir, test_element;
11360 test_x = bad_x + test_xy[i][0];
11361 test_y = bad_y + test_xy[i][1];
11362 if (!IN_LEV_FIELD(test_x, test_y))
11366 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11368 test_element = Feld[test_x][test_y];
11370 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11371 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11373 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11374 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11376 /* good thing is player or penguin that does not move away */
11377 if (IS_PLAYER(test_x, test_y))
11379 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11381 if (bad_element == EL_ROBOT && player->is_moving)
11382 continue; /* robot does not kill player if he is moving */
11384 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11386 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11387 continue; /* center and border element do not touch */
11394 else if (test_element == EL_PENGUIN)
11403 if (kill_x != -1 || kill_y != -1)
11405 if (IS_PLAYER(kill_x, kill_y))
11407 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11409 if (player->shield_deadly_time_left > 0 &&
11410 !IS_INDESTRUCTIBLE(bad_element))
11411 Bang(bad_x, bad_y);
11412 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11413 KillPlayer(player);
11416 Bang(kill_x, kill_y);
11420 void TestIfPlayerTouchesBadThing(int x, int y)
11422 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11425 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11427 TestIfGoodThingHitsBadThing(x, y, move_dir);
11430 void TestIfBadThingTouchesPlayer(int x, int y)
11432 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11435 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11437 TestIfBadThingHitsGoodThing(x, y, move_dir);
11440 void TestIfFriendTouchesBadThing(int x, int y)
11442 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11445 void TestIfBadThingTouchesFriend(int x, int y)
11447 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11450 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11452 int i, kill_x = bad_x, kill_y = bad_y;
11453 static int xy[4][2] =
11461 for (i = 0; i < NUM_DIRECTIONS; i++)
11465 x = bad_x + xy[i][0];
11466 y = bad_y + xy[i][1];
11467 if (!IN_LEV_FIELD(x, y))
11470 element = Feld[x][y];
11471 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11472 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11480 if (kill_x != bad_x || kill_y != bad_y)
11481 Bang(bad_x, bad_y);
11484 void KillPlayer(struct PlayerInfo *player)
11486 int jx = player->jx, jy = player->jy;
11488 if (!player->active)
11491 /* remove accessible field at the player's position */
11492 Feld[jx][jy] = EL_EMPTY;
11494 /* deactivate shield (else Bang()/Explode() would not work right) */
11495 player->shield_normal_time_left = 0;
11496 player->shield_deadly_time_left = 0;
11499 BuryPlayer(player);
11502 static void KillPlayerUnlessEnemyProtected(int x, int y)
11504 if (!PLAYER_ENEMY_PROTECTED(x, y))
11505 KillPlayer(PLAYERINFO(x, y));
11508 static void KillPlayerUnlessExplosionProtected(int x, int y)
11510 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11511 KillPlayer(PLAYERINFO(x, y));
11514 void BuryPlayer(struct PlayerInfo *player)
11516 int jx = player->jx, jy = player->jy;
11518 if (!player->active)
11521 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11522 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11524 player->GameOver = TRUE;
11525 RemovePlayer(player);
11528 void RemovePlayer(struct PlayerInfo *player)
11530 int jx = player->jx, jy = player->jy;
11531 int i, found = FALSE;
11533 player->present = FALSE;
11534 player->active = FALSE;
11536 if (!ExplodeField[jx][jy])
11537 StorePlayer[jx][jy] = 0;
11539 if (player->is_moving)
11540 DrawLevelField(player->last_jx, player->last_jy);
11542 for (i = 0; i < MAX_PLAYERS; i++)
11543 if (stored_player[i].active)
11547 AllPlayersGone = TRUE;
11553 #if USE_NEW_SNAP_DELAY
11554 static void setFieldForSnapping(int x, int y, int element, int direction)
11556 struct ElementInfo *ei = &element_info[element];
11557 int direction_bit = MV_DIR_TO_BIT(direction);
11558 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11559 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11560 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11562 Feld[x][y] = EL_ELEMENT_SNAPPING;
11563 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11565 ResetGfxAnimation(x, y);
11567 GfxElement[x][y] = element;
11568 GfxAction[x][y] = action;
11569 GfxDir[x][y] = direction;
11570 GfxFrame[x][y] = -1;
11575 =============================================================================
11576 checkDiagonalPushing()
11577 -----------------------------------------------------------------------------
11578 check if diagonal input device direction results in pushing of object
11579 (by checking if the alternative direction is walkable, diggable, ...)
11580 =============================================================================
11583 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11584 int x, int y, int real_dx, int real_dy)
11586 int jx, jy, dx, dy, xx, yy;
11588 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11591 /* diagonal direction: check alternative direction */
11596 xx = jx + (dx == 0 ? real_dx : 0);
11597 yy = jy + (dy == 0 ? real_dy : 0);
11599 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11603 =============================================================================
11605 -----------------------------------------------------------------------------
11606 x, y: field next to player (non-diagonal) to try to dig to
11607 real_dx, real_dy: direction as read from input device (can be diagonal)
11608 =============================================================================
11611 int DigField(struct PlayerInfo *player,
11612 int oldx, int oldy, int x, int y,
11613 int real_dx, int real_dy, int mode)
11615 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11616 boolean player_was_pushing = player->is_pushing;
11617 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11618 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11619 int jx = oldx, jy = oldy;
11620 int dx = x - jx, dy = y - jy;
11621 int nextx = x + dx, nexty = y + dy;
11622 int move_direction = (dx == -1 ? MV_LEFT :
11623 dx == +1 ? MV_RIGHT :
11625 dy == +1 ? MV_DOWN : MV_NONE);
11626 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11627 int dig_side = MV_DIR_OPPOSITE(move_direction);
11628 int old_element = Feld[jx][jy];
11629 #if USE_FIXED_DONT_RUN_INTO
11630 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11636 if (is_player) /* function can also be called by EL_PENGUIN */
11638 if (player->MovPos == 0)
11640 player->is_digging = FALSE;
11641 player->is_collecting = FALSE;
11644 if (player->MovPos == 0) /* last pushing move finished */
11645 player->is_pushing = FALSE;
11647 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11649 player->is_switching = FALSE;
11650 player->push_delay = -1;
11652 return MP_NO_ACTION;
11656 #if !USE_FIXED_DONT_RUN_INTO
11657 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11658 return MP_NO_ACTION;
11661 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11662 old_element = Back[jx][jy];
11664 /* in case of element dropped at player position, check background */
11665 else if (Back[jx][jy] != EL_EMPTY &&
11666 game.engine_version >= VERSION_IDENT(2,2,0,0))
11667 old_element = Back[jx][jy];
11670 #if USE_FIXED_DONT_RUN_INTO
11671 if (player_can_move && DONT_RUN_INTO(element))
11673 if (element == EL_ACID && dx == 0 && dy == 1)
11676 Feld[jx][jy] = EL_PLAYER_1;
11677 InitMovingField(jx, jy, MV_DOWN);
11678 Store[jx][jy] = EL_ACID;
11679 ContinueMoving(jx, jy);
11680 BuryPlayer(player);
11683 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11685 return MP_DONT_RUN_INTO;
11691 #if USE_FIXED_DONT_RUN_INTO
11692 if (player_can_move && DONT_RUN_INTO(element))
11694 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11696 return MP_DONT_RUN_INTO;
11701 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11702 return MP_NO_ACTION; /* field has no opening in this direction */
11704 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11705 return MP_NO_ACTION; /* field has no opening in this direction */
11708 #if USE_FIXED_DONT_RUN_INTO
11709 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11712 Feld[jx][jy] = EL_PLAYER_1;
11713 InitMovingField(jx, jy, MV_DOWN);
11714 Store[jx][jy] = EL_ACID;
11715 ContinueMoving(jx, jy);
11716 BuryPlayer(player);
11718 return MP_DONT_RUN_INTO;
11724 #if USE_FIXED_DONT_RUN_INTO
11725 if (player_can_move && DONT_RUN_INTO(element))
11727 if (element == EL_ACID && dx == 0 && dy == 1)
11730 Feld[jx][jy] = EL_PLAYER_1;
11731 InitMovingField(jx, jy, MV_DOWN);
11732 Store[jx][jy] = EL_ACID;
11733 ContinueMoving(jx, jy);
11734 BuryPlayer(player);
11737 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11739 return MP_DONT_RUN_INTO;
11744 #if USE_FIXED_DONT_RUN_INTO
11745 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11746 return MP_NO_ACTION;
11749 #if !USE_FIXED_DONT_RUN_INTO
11750 element = Feld[x][y];
11753 collect_count = element_info[element].collect_count_initial;
11755 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11756 return MP_NO_ACTION;
11758 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11759 player_can_move = player_can_move_or_snap;
11761 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11762 game.engine_version >= VERSION_IDENT(2,2,0,0))
11764 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11765 player->index_bit, dig_side);
11766 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11767 player->index_bit, dig_side);
11769 if (Feld[x][y] != element) /* field changed by snapping */
11772 return MP_NO_ACTION;
11775 #if USE_PLAYER_GRAVITY
11776 if (player->gravity && is_player && !player->is_auto_moving &&
11777 canFallDown(player) && move_direction != MV_DOWN &&
11778 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11779 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11781 if (game.gravity && is_player && !player->is_auto_moving &&
11782 canFallDown(player) && move_direction != MV_DOWN &&
11783 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11784 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11787 if (player_can_move &&
11788 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11790 int sound_element = SND_ELEMENT(element);
11791 int sound_action = ACTION_WALKING;
11793 if (IS_RND_GATE(element))
11795 if (!player->key[RND_GATE_NR(element)])
11796 return MP_NO_ACTION;
11798 else if (IS_RND_GATE_GRAY(element))
11800 if (!player->key[RND_GATE_GRAY_NR(element)])
11801 return MP_NO_ACTION;
11803 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11805 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11806 return MP_NO_ACTION;
11808 else if (element == EL_EXIT_OPEN ||
11809 element == EL_SP_EXIT_OPEN ||
11810 element == EL_SP_EXIT_OPENING)
11812 sound_action = ACTION_PASSING; /* player is passing exit */
11814 else if (element == EL_EMPTY)
11816 sound_action = ACTION_MOVING; /* nothing to walk on */
11819 /* play sound from background or player, whatever is available */
11820 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11821 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11823 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11825 else if (player_can_move &&
11826 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11828 if (!ACCESS_FROM(element, opposite_direction))
11829 return MP_NO_ACTION; /* field not accessible from this direction */
11831 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11832 return MP_NO_ACTION;
11834 if (IS_EM_GATE(element))
11836 if (!player->key[EM_GATE_NR(element)])
11837 return MP_NO_ACTION;
11839 else if (IS_EM_GATE_GRAY(element))
11841 if (!player->key[EM_GATE_GRAY_NR(element)])
11842 return MP_NO_ACTION;
11844 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11846 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11847 return MP_NO_ACTION;
11849 else if (IS_SP_PORT(element))
11851 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11852 element == EL_SP_GRAVITY_PORT_RIGHT ||
11853 element == EL_SP_GRAVITY_PORT_UP ||
11854 element == EL_SP_GRAVITY_PORT_DOWN)
11855 #if USE_PLAYER_GRAVITY
11856 player->gravity = !player->gravity;
11858 game.gravity = !game.gravity;
11860 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11861 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11862 element == EL_SP_GRAVITY_ON_PORT_UP ||
11863 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11864 #if USE_PLAYER_GRAVITY
11865 player->gravity = TRUE;
11867 game.gravity = TRUE;
11869 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11870 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11871 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11872 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11873 #if USE_PLAYER_GRAVITY
11874 player->gravity = FALSE;
11876 game.gravity = FALSE;
11880 /* automatically move to the next field with double speed */
11881 player->programmed_action = move_direction;
11883 if (player->move_delay_reset_counter == 0)
11885 player->move_delay_reset_counter = 2; /* two double speed steps */
11887 DOUBLE_PLAYER_SPEED(player);
11890 PlayLevelSoundAction(x, y, ACTION_PASSING);
11892 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11896 if (mode != DF_SNAP)
11898 GfxElement[x][y] = GFX_ELEMENT(element);
11899 player->is_digging = TRUE;
11902 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11904 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11905 player->index_bit, dig_side);
11907 if (mode == DF_SNAP)
11909 #if USE_NEW_SNAP_DELAY
11910 if (level.block_snap_field)
11911 setFieldForSnapping(x, y, element, move_direction);
11913 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11915 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11918 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11919 player->index_bit, dig_side);
11922 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11926 if (is_player && mode != DF_SNAP)
11928 GfxElement[x][y] = element;
11929 player->is_collecting = TRUE;
11932 if (element == EL_SPEED_PILL)
11934 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11936 else if (element == EL_EXTRA_TIME && level.time > 0)
11938 TimeLeft += level.extra_time;
11939 DrawGameValue_Time(TimeLeft);
11941 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11943 player->shield_normal_time_left += level.shield_normal_time;
11944 if (element == EL_SHIELD_DEADLY)
11945 player->shield_deadly_time_left += level.shield_deadly_time;
11947 else if (element == EL_DYNAMITE ||
11948 element == EL_EM_DYNAMITE ||
11949 element == EL_SP_DISK_RED)
11951 if (player->inventory_size < MAX_INVENTORY_SIZE)
11952 player->inventory_element[player->inventory_size++] = element;
11955 DrawGameDoorValues();
11957 DrawGameValue_Dynamite(local_player->inventory_size);
11960 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11962 player->dynabomb_count++;
11963 player->dynabombs_left++;
11965 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11967 player->dynabomb_size++;
11969 else if (element == EL_DYNABOMB_INCREASE_POWER)
11971 player->dynabomb_xl = TRUE;
11973 else if (IS_KEY(element))
11975 player->key[KEY_NR(element)] = TRUE;
11978 DrawGameDoorValues();
11980 DrawGameValue_Keys(player->key);
11983 redraw_mask |= REDRAW_DOOR_1;
11985 else if (IS_ENVELOPE(element))
11987 player->show_envelope = element;
11989 else if (element == EL_EMC_LENSES)
11991 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11993 RedrawAllInvisibleElementsForLenses();
11995 else if (element == EL_EMC_MAGNIFIER)
11997 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11999 RedrawAllInvisibleElementsForMagnifier();
12001 else if (IS_DROPPABLE(element) ||
12002 IS_THROWABLE(element)) /* can be collected and dropped */
12006 if (collect_count == 0)
12007 player->inventory_infinite_element = element;
12009 for (i = 0; i < collect_count; i++)
12010 if (player->inventory_size < MAX_INVENTORY_SIZE)
12011 player->inventory_element[player->inventory_size++] = element;
12014 DrawGameDoorValues();
12016 DrawGameValue_Dynamite(local_player->inventory_size);
12019 else if (collect_count > 0)
12021 local_player->gems_still_needed -= collect_count;
12022 if (local_player->gems_still_needed < 0)
12023 local_player->gems_still_needed = 0;
12025 DrawGameValue_Emeralds(local_player->gems_still_needed);
12028 RaiseScoreElement(element);
12029 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12032 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12033 player->index_bit, dig_side);
12035 if (mode == DF_SNAP)
12037 #if USE_NEW_SNAP_DELAY
12038 if (level.block_snap_field)
12039 setFieldForSnapping(x, y, element, move_direction);
12041 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12043 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12046 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12047 player->index_bit, dig_side);
12050 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12052 if (mode == DF_SNAP && element != EL_BD_ROCK)
12053 return MP_NO_ACTION;
12055 if (CAN_FALL(element) && dy)
12056 return MP_NO_ACTION;
12058 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12059 !(element == EL_SPRING && level.use_spring_bug))
12060 return MP_NO_ACTION;
12062 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12063 ((move_direction & MV_VERTICAL &&
12064 ((element_info[element].move_pattern & MV_LEFT &&
12065 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12066 (element_info[element].move_pattern & MV_RIGHT &&
12067 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12068 (move_direction & MV_HORIZONTAL &&
12069 ((element_info[element].move_pattern & MV_UP &&
12070 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12071 (element_info[element].move_pattern & MV_DOWN &&
12072 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12073 return MP_NO_ACTION;
12075 /* do not push elements already moving away faster than player */
12076 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12077 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12078 return MP_NO_ACTION;
12080 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12082 if (player->push_delay_value == -1 || !player_was_pushing)
12083 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12085 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12087 if (player->push_delay_value == -1)
12088 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12090 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12092 if (!player->is_pushing)
12093 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12096 player->is_pushing = TRUE;
12098 if (!(IN_LEV_FIELD(nextx, nexty) &&
12099 (IS_FREE(nextx, nexty) ||
12100 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12101 IS_SB_ELEMENT(element)))))
12102 return MP_NO_ACTION;
12104 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12105 return MP_NO_ACTION;
12107 if (player->push_delay == -1) /* new pushing; restart delay */
12108 player->push_delay = 0;
12110 if (player->push_delay < player->push_delay_value &&
12111 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12112 element != EL_SPRING && element != EL_BALLOON)
12114 /* make sure that there is no move delay before next try to push */
12115 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12116 player->move_delay = 0;
12118 return MP_NO_ACTION;
12121 if (IS_SB_ELEMENT(element))
12123 if (element == EL_SOKOBAN_FIELD_FULL)
12125 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12126 local_player->sokobanfields_still_needed++;
12129 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12131 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12132 local_player->sokobanfields_still_needed--;
12135 Feld[x][y] = EL_SOKOBAN_OBJECT;
12137 if (Back[x][y] == Back[nextx][nexty])
12138 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12139 else if (Back[x][y] != 0)
12140 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12143 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12146 if (local_player->sokobanfields_still_needed == 0 &&
12147 game.emulation == EMU_SOKOBAN)
12149 player->LevelSolved = player->GameOver = TRUE;
12150 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12154 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12156 InitMovingField(x, y, move_direction);
12157 GfxAction[x][y] = ACTION_PUSHING;
12159 if (mode == DF_SNAP)
12160 ContinueMoving(x, y);
12162 MovPos[x][y] = (dx != 0 ? dx : dy);
12164 Pushed[x][y] = TRUE;
12165 Pushed[nextx][nexty] = TRUE;
12167 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12168 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12170 player->push_delay_value = -1; /* get new value later */
12172 /* check for element change _after_ element has been pushed */
12173 if (game.use_change_when_pushing_bug)
12175 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12176 player->index_bit, dig_side);
12177 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12178 player->index_bit, dig_side);
12181 else if (IS_SWITCHABLE(element))
12183 if (PLAYER_SWITCHING(player, x, y))
12185 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12186 player->index_bit, dig_side);
12191 player->is_switching = TRUE;
12192 player->switch_x = x;
12193 player->switch_y = y;
12195 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12197 if (element == EL_ROBOT_WHEEL)
12199 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12203 DrawLevelField(x, y);
12205 else if (element == EL_SP_TERMINAL)
12210 SCAN_PLAYFIELD(xx, yy)
12212 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12215 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12217 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12218 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12221 else if (IS_BELT_SWITCH(element))
12223 ToggleBeltSwitch(x, y);
12225 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12226 element == EL_SWITCHGATE_SWITCH_DOWN)
12228 ToggleSwitchgateSwitch(x, y);
12230 else if (element == EL_LIGHT_SWITCH ||
12231 element == EL_LIGHT_SWITCH_ACTIVE)
12233 ToggleLightSwitch(x, y);
12235 else if (element == EL_TIMEGATE_SWITCH)
12237 ActivateTimegateSwitch(x, y);
12239 else if (element == EL_BALLOON_SWITCH_LEFT ||
12240 element == EL_BALLOON_SWITCH_RIGHT ||
12241 element == EL_BALLOON_SWITCH_UP ||
12242 element == EL_BALLOON_SWITCH_DOWN ||
12243 element == EL_BALLOON_SWITCH_NONE ||
12244 element == EL_BALLOON_SWITCH_ANY)
12246 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12247 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12248 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12249 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12250 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12253 else if (element == EL_LAMP)
12255 Feld[x][y] = EL_LAMP_ACTIVE;
12256 local_player->lights_still_needed--;
12258 ResetGfxAnimation(x, y);
12259 DrawLevelField(x, y);
12261 else if (element == EL_TIME_ORB_FULL)
12263 Feld[x][y] = EL_TIME_ORB_EMPTY;
12265 if (level.time > 0 || level.use_time_orb_bug)
12267 TimeLeft += level.time_orb_time;
12268 DrawGameValue_Time(TimeLeft);
12271 ResetGfxAnimation(x, y);
12272 DrawLevelField(x, y);
12274 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12275 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12279 game.ball_state = !game.ball_state;
12282 SCAN_PLAYFIELD(xx, yy)
12284 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12287 int e = Feld[xx][yy];
12289 if (game.ball_state)
12291 if (e == EL_EMC_MAGIC_BALL)
12292 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12293 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12294 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12298 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12299 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12300 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12301 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12306 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12307 player->index_bit, dig_side);
12309 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12310 player->index_bit, dig_side);
12312 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12313 player->index_bit, dig_side);
12319 if (!PLAYER_SWITCHING(player, x, y))
12321 player->is_switching = TRUE;
12322 player->switch_x = x;
12323 player->switch_y = y;
12325 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12326 player->index_bit, dig_side);
12327 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12328 player->index_bit, dig_side);
12330 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12331 player->index_bit, dig_side);
12332 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12333 player->index_bit, dig_side);
12336 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12337 player->index_bit, dig_side);
12338 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12339 player->index_bit, dig_side);
12341 return MP_NO_ACTION;
12344 player->push_delay = -1;
12346 if (is_player) /* function can also be called by EL_PENGUIN */
12348 if (Feld[x][y] != element) /* really digged/collected something */
12349 player->is_collecting = !player->is_digging;
12355 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12357 int jx = player->jx, jy = player->jy;
12358 int x = jx + dx, y = jy + dy;
12359 int snap_direction = (dx == -1 ? MV_LEFT :
12360 dx == +1 ? MV_RIGHT :
12362 dy == +1 ? MV_DOWN : MV_NONE);
12363 boolean can_continue_snapping = (level.continuous_snapping &&
12364 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12366 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12369 if (!player->active || !IN_LEV_FIELD(x, y))
12377 if (player->MovPos == 0)
12378 player->is_pushing = FALSE;
12380 player->is_snapping = FALSE;
12382 if (player->MovPos == 0)
12384 player->is_moving = FALSE;
12385 player->is_digging = FALSE;
12386 player->is_collecting = FALSE;
12392 #if USE_NEW_CONTINUOUS_SNAPPING
12393 /* prevent snapping with already pressed snap key when not allowed */
12394 if (player->is_snapping && !can_continue_snapping)
12397 if (player->is_snapping)
12401 player->MovDir = snap_direction;
12403 if (player->MovPos == 0)
12405 player->is_moving = FALSE;
12406 player->is_digging = FALSE;
12407 player->is_collecting = FALSE;
12410 player->is_dropping = FALSE;
12411 player->is_dropping_pressed = FALSE;
12412 player->drop_pressed_delay = 0;
12414 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12417 player->is_snapping = TRUE;
12419 if (player->MovPos == 0)
12421 player->is_moving = FALSE;
12422 player->is_digging = FALSE;
12423 player->is_collecting = FALSE;
12426 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12427 DrawLevelField(player->last_jx, player->last_jy);
12429 DrawLevelField(x, y);
12434 boolean DropElement(struct PlayerInfo *player)
12436 int old_element, new_element;
12437 int dropx = player->jx, dropy = player->jy;
12438 int drop_direction = player->MovDir;
12439 int drop_side = drop_direction;
12440 int drop_element = (player->inventory_size > 0 ?
12441 player->inventory_element[player->inventory_size - 1] :
12442 player->inventory_infinite_element != EL_UNDEFINED ?
12443 player->inventory_infinite_element :
12444 player->dynabombs_left > 0 ?
12445 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12448 player->is_dropping_pressed = TRUE;
12450 /* do not drop an element on top of another element; when holding drop key
12451 pressed without moving, dropped element must move away before the next
12452 element can be dropped (this is especially important if the next element
12453 is dynamite, which can be placed on background for historical reasons) */
12454 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12457 if (IS_THROWABLE(drop_element))
12459 dropx += GET_DX_FROM_DIR(drop_direction);
12460 dropy += GET_DY_FROM_DIR(drop_direction);
12462 if (!IN_LEV_FIELD(dropx, dropy))
12466 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12467 new_element = drop_element; /* default: no change when dropping */
12469 /* check if player is active, not moving and ready to drop */
12470 if (!player->active || player->MovPos || player->drop_delay > 0)
12473 /* check if player has anything that can be dropped */
12474 if (new_element == EL_UNDEFINED)
12477 /* check if drop key was pressed long enough for EM style dynamite */
12478 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12481 /* check if anything can be dropped at the current position */
12482 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12485 /* collected custom elements can only be dropped on empty fields */
12486 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12489 if (old_element != EL_EMPTY)
12490 Back[dropx][dropy] = old_element; /* store old element on this field */
12492 ResetGfxAnimation(dropx, dropy);
12493 ResetRandomAnimationValue(dropx, dropy);
12495 if (player->inventory_size > 0 ||
12496 player->inventory_infinite_element != EL_UNDEFINED)
12498 if (player->inventory_size > 0)
12500 player->inventory_size--;
12503 DrawGameDoorValues();
12505 DrawGameValue_Dynamite(local_player->inventory_size);
12508 if (new_element == EL_DYNAMITE)
12509 new_element = EL_DYNAMITE_ACTIVE;
12510 else if (new_element == EL_EM_DYNAMITE)
12511 new_element = EL_EM_DYNAMITE_ACTIVE;
12512 else if (new_element == EL_SP_DISK_RED)
12513 new_element = EL_SP_DISK_RED_ACTIVE;
12516 Feld[dropx][dropy] = new_element;
12518 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12519 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12520 el2img(Feld[dropx][dropy]), 0);
12522 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12524 /* needed if previous element just changed to "empty" in the last frame */
12525 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12527 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12528 player->index_bit, drop_side);
12529 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12531 player->index_bit, drop_side);
12533 TestIfElementTouchesCustomElement(dropx, dropy);
12535 else /* player is dropping a dyna bomb */
12537 player->dynabombs_left--;
12539 Feld[dropx][dropy] = new_element;
12541 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12542 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12543 el2img(Feld[dropx][dropy]), 0);
12545 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12548 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12549 InitField_WithBug1(dropx, dropy, FALSE);
12551 new_element = Feld[dropx][dropy]; /* element might have changed */
12553 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12554 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12556 int move_direction, nextx, nexty;
12558 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12559 MovDir[dropx][dropy] = drop_direction;
12561 move_direction = MovDir[dropx][dropy];
12562 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12563 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12565 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12566 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12569 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12570 player->is_dropping = TRUE;
12572 player->drop_pressed_delay = 0;
12573 player->is_dropping_pressed = FALSE;
12575 player->drop_x = dropx;
12576 player->drop_y = dropy;
12581 /* ------------------------------------------------------------------------- */
12582 /* game sound playing functions */
12583 /* ------------------------------------------------------------------------- */
12585 static int *loop_sound_frame = NULL;
12586 static int *loop_sound_volume = NULL;
12588 void InitPlayLevelSound()
12590 int num_sounds = getSoundListSize();
12592 checked_free(loop_sound_frame);
12593 checked_free(loop_sound_volume);
12595 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12596 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12599 static void PlayLevelSound(int x, int y, int nr)
12601 int sx = SCREENX(x), sy = SCREENY(y);
12602 int volume, stereo_position;
12603 int max_distance = 8;
12604 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12606 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12607 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12610 if (!IN_LEV_FIELD(x, y) ||
12611 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12612 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12615 volume = SOUND_MAX_VOLUME;
12617 if (!IN_SCR_FIELD(sx, sy))
12619 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12620 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12622 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12625 stereo_position = (SOUND_MAX_LEFT +
12626 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12627 (SCR_FIELDX + 2 * max_distance));
12629 if (IS_LOOP_SOUND(nr))
12631 /* This assures that quieter loop sounds do not overwrite louder ones,
12632 while restarting sound volume comparison with each new game frame. */
12634 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12637 loop_sound_volume[nr] = volume;
12638 loop_sound_frame[nr] = FrameCounter;
12641 PlaySoundExt(nr, volume, stereo_position, type);
12644 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12646 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12647 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12648 y < LEVELY(BY1) ? LEVELY(BY1) :
12649 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12653 static void PlayLevelSoundAction(int x, int y, int action)
12655 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12658 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12660 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12662 if (sound_effect != SND_UNDEFINED)
12663 PlayLevelSound(x, y, sound_effect);
12666 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12669 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12671 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12672 PlayLevelSound(x, y, sound_effect);
12675 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12677 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12679 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12680 PlayLevelSound(x, y, sound_effect);
12683 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12685 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12687 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12688 StopSound(sound_effect);
12691 static void PlayLevelMusic()
12693 if (levelset.music[level_nr] != MUS_UNDEFINED)
12694 PlayMusic(levelset.music[level_nr]); /* from config file */
12696 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12699 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12701 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12706 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12710 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12714 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12718 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12722 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12726 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12730 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12733 case SAMPLE_android_clone:
12734 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12737 case SAMPLE_android_move:
12738 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12741 case SAMPLE_spring:
12742 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12746 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12750 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12753 case SAMPLE_eater_eat:
12754 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12758 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12761 case SAMPLE_collect:
12762 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12765 case SAMPLE_diamond:
12766 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12769 case SAMPLE_squash:
12770 /* !!! CHECK THIS !!! */
12772 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12774 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12778 case SAMPLE_wonderfall:
12779 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12783 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12787 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12791 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12795 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12799 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12803 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12806 case SAMPLE_wonder:
12807 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12811 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12814 case SAMPLE_exit_open:
12815 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12818 case SAMPLE_exit_leave:
12819 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12822 case SAMPLE_dynamite:
12823 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12827 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12831 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12835 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12839 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12843 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12847 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12851 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12856 void RaiseScore(int value)
12858 local_player->score += value;
12860 DrawGameValue_Score(local_player->score);
12863 void RaiseScoreElement(int element)
12868 case EL_BD_DIAMOND:
12869 case EL_EMERALD_YELLOW:
12870 case EL_EMERALD_RED:
12871 case EL_EMERALD_PURPLE:
12872 case EL_SP_INFOTRON:
12873 RaiseScore(level.score[SC_EMERALD]);
12876 RaiseScore(level.score[SC_DIAMOND]);
12879 RaiseScore(level.score[SC_CRYSTAL]);
12882 RaiseScore(level.score[SC_PEARL]);
12885 case EL_BD_BUTTERFLY:
12886 case EL_SP_ELECTRON:
12887 RaiseScore(level.score[SC_BUG]);
12890 case EL_BD_FIREFLY:
12891 case EL_SP_SNIKSNAK:
12892 RaiseScore(level.score[SC_SPACESHIP]);
12895 case EL_DARK_YAMYAM:
12896 RaiseScore(level.score[SC_YAMYAM]);
12899 RaiseScore(level.score[SC_ROBOT]);
12902 RaiseScore(level.score[SC_PACMAN]);
12905 RaiseScore(level.score[SC_NUT]);
12908 case EL_EM_DYNAMITE:
12909 case EL_SP_DISK_RED:
12910 case EL_DYNABOMB_INCREASE_NUMBER:
12911 case EL_DYNABOMB_INCREASE_SIZE:
12912 case EL_DYNABOMB_INCREASE_POWER:
12913 RaiseScore(level.score[SC_DYNAMITE]);
12915 case EL_SHIELD_NORMAL:
12916 case EL_SHIELD_DEADLY:
12917 RaiseScore(level.score[SC_SHIELD]);
12919 case EL_EXTRA_TIME:
12920 RaiseScore(level.extra_time_score);
12934 RaiseScore(level.score[SC_KEY]);
12937 RaiseScore(element_info[element].collect_score);
12942 void RequestQuitGame(boolean ask_if_really_quit)
12944 if (AllPlayersGone ||
12945 !ask_if_really_quit ||
12946 level_editor_test_game ||
12947 Request("Do you really want to quit the game ?",
12948 REQ_ASK | REQ_STAY_CLOSED))
12950 #if defined(NETWORK_AVALIABLE)
12951 if (options.network)
12952 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12956 game_status = GAME_MODE_MAIN;
12962 if (tape.playing && tape.deactivate_display)
12963 TapeDeactivateDisplayOff(TRUE);
12965 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12967 if (tape.playing && tape.deactivate_display)
12968 TapeDeactivateDisplayOn();
12973 /* ---------- new game button stuff ---------------------------------------- */
12975 /* graphic position values for game buttons */
12976 #define GAME_BUTTON_XSIZE 30
12977 #define GAME_BUTTON_YSIZE 30
12978 #define GAME_BUTTON_XPOS 5
12979 #define GAME_BUTTON_YPOS 215
12980 #define SOUND_BUTTON_XPOS 5
12981 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12983 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12984 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12985 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12986 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12987 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12988 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12995 } gamebutton_info[NUM_GAME_BUTTONS] =
12998 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13003 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13004 GAME_CTRL_ID_PAUSE,
13008 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13013 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13014 SOUND_CTRL_ID_MUSIC,
13015 "background music on/off"
13018 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13019 SOUND_CTRL_ID_LOOPS,
13020 "sound loops on/off"
13023 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13024 SOUND_CTRL_ID_SIMPLE,
13025 "normal sounds on/off"
13029 void CreateGameButtons()
13033 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13035 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13036 struct GadgetInfo *gi;
13039 unsigned long event_mask;
13040 int gd_xoffset, gd_yoffset;
13041 int gd_x1, gd_x2, gd_y1, gd_y2;
13044 gd_xoffset = gamebutton_info[i].x;
13045 gd_yoffset = gamebutton_info[i].y;
13046 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13047 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13049 if (id == GAME_CTRL_ID_STOP ||
13050 id == GAME_CTRL_ID_PAUSE ||
13051 id == GAME_CTRL_ID_PLAY)
13053 button_type = GD_TYPE_NORMAL_BUTTON;
13055 event_mask = GD_EVENT_RELEASED;
13056 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13057 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13061 button_type = GD_TYPE_CHECK_BUTTON;
13063 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13064 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13065 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13066 event_mask = GD_EVENT_PRESSED;
13067 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13068 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13071 gi = CreateGadget(GDI_CUSTOM_ID, id,
13072 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13073 GDI_X, DX + gd_xoffset,
13074 GDI_Y, DY + gd_yoffset,
13075 GDI_WIDTH, GAME_BUTTON_XSIZE,
13076 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13077 GDI_TYPE, button_type,
13078 GDI_STATE, GD_BUTTON_UNPRESSED,
13079 GDI_CHECKED, checked,
13080 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13081 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13082 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13083 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13084 GDI_EVENT_MASK, event_mask,
13085 GDI_CALLBACK_ACTION, HandleGameButtons,
13089 Error(ERR_EXIT, "cannot create gadget");
13091 game_gadget[id] = gi;
13095 void FreeGameButtons()
13099 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13100 FreeGadget(game_gadget[i]);
13103 static void MapGameButtons()
13107 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13108 MapGadget(game_gadget[i]);
13111 void UnmapGameButtons()
13115 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13116 UnmapGadget(game_gadget[i]);
13119 static void HandleGameButtons(struct GadgetInfo *gi)
13121 int id = gi->custom_id;
13123 if (game_status != GAME_MODE_PLAYING)
13128 case GAME_CTRL_ID_STOP:
13132 RequestQuitGame(TRUE);
13135 case GAME_CTRL_ID_PAUSE:
13136 if (options.network)
13138 #if defined(NETWORK_AVALIABLE)
13140 SendToServer_ContinuePlaying();
13142 SendToServer_PausePlaying();
13146 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13149 case GAME_CTRL_ID_PLAY:
13152 #if defined(NETWORK_AVALIABLE)
13153 if (options.network)
13154 SendToServer_ContinuePlaying();
13158 tape.pausing = FALSE;
13159 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13164 case SOUND_CTRL_ID_MUSIC:
13165 if (setup.sound_music)
13167 setup.sound_music = FALSE;
13170 else if (audio.music_available)
13172 setup.sound = setup.sound_music = TRUE;
13174 SetAudioMode(setup.sound);
13180 case SOUND_CTRL_ID_LOOPS:
13181 if (setup.sound_loops)
13182 setup.sound_loops = FALSE;
13183 else if (audio.loops_available)
13185 setup.sound = setup.sound_loops = TRUE;
13186 SetAudioMode(setup.sound);
13190 case SOUND_CTRL_ID_SIMPLE:
13191 if (setup.sound_simple)
13192 setup.sound_simple = FALSE;
13193 else if (audio.sound_available)
13195 setup.sound = setup.sound_simple = TRUE;
13196 SetAudioMode(setup.sound);