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)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
60 /* for MovePlayer() */
61 #define MP_NO_ACTION 0
64 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
66 /* for ScrollPlayer() */
68 #define SCROLL_GO_ON 1
70 /* for Bang()/Explode() */
71 #define EX_PHASE_START 0
72 #define EX_TYPE_NONE 0
73 #define EX_TYPE_NORMAL (1 << 0)
74 #define EX_TYPE_CENTER (1 << 1)
75 #define EX_TYPE_BORDER (1 << 2)
76 #define EX_TYPE_CROSS (1 << 3)
77 #define EX_TYPE_DYNA (1 << 4)
78 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
82 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
84 /* special positions in the game control window (relative to control window) */
85 #define XX_LEVEL1 (game.panel.level.x)
86 #define XX_LEVEL2 (game.panel.level.x - 1)
87 #define YY_LEVEL (game.panel.level.y)
88 #define XX_EMERALDS (game.panel.gems.x)
89 #define YY_EMERALDS (game.panel.gems.y)
90 #define XX_DYNAMITE (game.panel.inventory.x)
91 #define YY_DYNAMITE (game.panel.inventory.y)
92 #define XX_KEYS (game.panel.keys.x)
93 #define YY_KEYS (game.panel.keys.y)
94 #define XX_SCORE (game.panel.score.x)
95 #define YY_SCORE (game.panel.score.y)
96 #define XX_TIME1 (game.panel.time.x)
97 #define XX_TIME2 (game.panel.time.x + 1)
98 #define YY_TIME (game.panel.time.y)
102 /* special positions in the game control window (relative to control window) */
105 #define XX_EMERALDS 29
106 #define YY_EMERALDS 54
107 #define XX_DYNAMITE 29
108 #define YY_DYNAMITE 89
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1 (DX + XX_LEVEL1)
121 #define DX_LEVEL2 (DX + XX_LEVEL2)
122 #define DY_LEVEL (DY + YY_LEVEL)
123 #define DX_EMERALDS (DX + XX_EMERALDS)
124 #define DY_EMERALDS (DY + YY_EMERALDS)
125 #define DX_DYNAMITE (DX + XX_DYNAMITE)
126 #define DY_DYNAMITE (DY + YY_DYNAMITE)
127 #define DX_KEYS (DX + XX_KEYS)
128 #define DY_KEYS (DY + YY_KEYS)
129 #define DX_SCORE (DX + XX_SCORE)
130 #define DY_SCORE (DY + YY_SCORE)
131 #define DX_TIME1 (DX + XX_TIME1)
132 #define DX_TIME2 (DX + XX_TIME2)
133 #define DY_TIME (DY + YY_TIME)
135 /* values for delayed check of falling and moving elements and for collision */
136 #define CHECK_DELAY_MOVING 3
137 #define CHECK_DELAY_FALLING 3
138 #define CHECK_DELAY_COLLISION 2
140 /* values for initial player move delay (initial delay counter value) */
141 #define INITIAL_MOVE_DELAY_OFF -1
142 #define INITIAL_MOVE_DELAY_ON 0
144 /* values for player movement speed (which is in fact a delay value) */
145 #define MOVE_DELAY_MIN_SPEED 32
146 #define MOVE_DELAY_NORMAL_SPEED 8
147 #define MOVE_DELAY_HIGH_SPEED 4
148 #define MOVE_DELAY_MAX_SPEED 1
151 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
152 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
154 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
155 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
157 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
158 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
160 /* values for other actions */
161 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
162 #define MOVE_STEPSIZE_MIN (1)
163 #define MOVE_STEPSIZE_MAX (TILEX)
165 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
166 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
168 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
170 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
171 RND(element_info[e].push_delay_random))
172 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
173 RND(element_info[e].drop_delay_random))
174 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
175 RND(element_info[e].move_delay_random))
176 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
177 (element_info[e].move_delay_random))
178 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
179 RND(element_info[e].ce_value_random_initial))
180 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
181 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
182 RND((c)->delay_random * (c)->delay_frames))
183 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
184 RND((c)->delay_random))
188 #define GET_VALID_RUNTIME_ELEMENT(e) \
189 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
191 #define GET_VALID_FILE_ELEMENT(e) \
192 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
195 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
196 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
197 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
198 (be) + (e) - EL_SELF)
200 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
201 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
202 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
203 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
204 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
205 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
206 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
207 (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \
208 RESOLVED_REFERENCE_ELEMENT(be, e) : \
211 #define CAN_GROW_INTO(e) \
212 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
214 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
215 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
218 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
224 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
230 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
231 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
233 (CAN_MOVE_INTO_ACID(e) && \
234 Feld[x][y] == EL_ACID) || \
235 (DONT_COLLIDE_WITH(e) && \
237 !PLAYER_ENEMY_PROTECTED(x, y))))
239 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
240 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
242 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
243 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
245 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
248 #define ANDROID_CAN_CLONE_FIELD(x, y) \
249 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
250 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
252 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
255 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
258 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
261 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
262 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
264 #define PIG_CAN_ENTER_FIELD(e, x, y) \
265 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
267 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
268 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
269 IS_FOOD_PENGUIN(Feld[x][y])))
270 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
271 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
273 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
274 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
276 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
277 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
279 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
280 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
281 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
284 #define GROUP_NR(e) ((e) - EL_GROUP_START)
285 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
286 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
288 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
289 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
292 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
294 #define CE_ENTER_FIELD_COND(e, x, y) \
295 (!IS_PLAYER(x, y) && \
296 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
298 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
299 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
301 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
302 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
304 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
305 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
306 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
307 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
309 /* game button identifiers */
310 #define GAME_CTRL_ID_STOP 0
311 #define GAME_CTRL_ID_PAUSE 1
312 #define GAME_CTRL_ID_PLAY 2
313 #define SOUND_CTRL_ID_MUSIC 3
314 #define SOUND_CTRL_ID_LOOPS 4
315 #define SOUND_CTRL_ID_SIMPLE 5
317 #define NUM_GAME_BUTTONS 6
320 /* forward declaration for internal use */
322 static void CreateField(int, int, int);
324 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
325 static void AdvanceFrameAndPlayerCounters(int);
327 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
328 static boolean MovePlayer(struct PlayerInfo *, int, int);
329 static void ScrollPlayer(struct PlayerInfo *, int);
330 static void ScrollScreen(struct PlayerInfo *, int);
332 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
334 static void InitBeltMovement(void);
335 static void CloseAllOpenTimegates(void);
336 static void CheckGravityMovement(struct PlayerInfo *);
337 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
338 static void KillPlayerUnlessEnemyProtected(int, int);
339 static void KillPlayerUnlessExplosionProtected(int, int);
341 static void TestIfPlayerTouchesCustomElement(int, int);
342 static void TestIfElementTouchesCustomElement(int, int);
343 static void TestIfElementHitsCustomElement(int, int, int);
345 static void TestIfElementSmashesCustomElement(int, int, int);
348 static void HandleElementChange(int, int, int);
349 static void ExecuteCustomElementAction(int, int, int, int);
350 static boolean ChangeElement(int, int, int, int);
352 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
353 #define CheckTriggeredElementChange(x, y, e, ev) \
354 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
355 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
356 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
357 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
358 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
359 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
360 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
362 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
363 #define CheckElementChange(x, y, e, te, ev) \
364 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
365 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
366 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
367 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
368 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
370 static void PlayLevelSound(int, int, int);
371 static void PlayLevelSoundNearest(int, int, int);
372 static void PlayLevelSoundAction(int, int, int);
373 static void PlayLevelSoundElementAction(int, int, int, int);
374 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
375 static void PlayLevelSoundActionIfLoop(int, int, int);
376 static void StopLevelSoundActionIfLoop(int, int, int);
377 static void PlayLevelMusic();
379 static void MapGameButtons();
380 static void HandleGameButtons(struct GadgetInfo *);
382 int AmoebeNachbarNr(int, int);
383 void AmoebeUmwandeln(int, int);
384 void ContinueMoving(int, int);
386 void InitMovDir(int, int);
387 void InitAmoebaNr(int, int);
388 int NewHiScore(void);
390 void TestIfGoodThingHitsBadThing(int, int, int);
391 void TestIfBadThingHitsGoodThing(int, int, int);
392 void TestIfPlayerTouchesBadThing(int, int);
393 void TestIfPlayerRunsIntoBadThing(int, int, int);
394 void TestIfBadThingTouchesPlayer(int, int);
395 void TestIfBadThingRunsIntoPlayer(int, int, int);
396 void TestIfFriendTouchesBadThing(int, int);
397 void TestIfBadThingTouchesFriend(int, int);
398 void TestIfBadThingTouchesOtherBadThing(int, int);
400 void KillPlayer(struct PlayerInfo *);
401 void BuryPlayer(struct PlayerInfo *);
402 void RemovePlayer(struct PlayerInfo *);
404 boolean SnapField(struct PlayerInfo *, int, int);
405 boolean DropElement(struct PlayerInfo *);
407 static int getInvisibleActiveFromInvisibleElement(int);
408 static int getInvisibleFromInvisibleActiveElement(int);
410 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
413 /* ------------------------------------------------------------------------- */
414 /* definition of elements that automatically change to other elements after */
415 /* a specified time, eventually calling a function when changing */
416 /* ------------------------------------------------------------------------- */
418 /* forward declaration for changer functions */
419 static void InitBuggyBase(int, int);
420 static void WarnBuggyBase(int, int);
422 static void InitTrap(int, int);
423 static void ActivateTrap(int, int);
424 static void ChangeActiveTrap(int, int);
426 static void InitRobotWheel(int, int);
427 static void RunRobotWheel(int, int);
428 static void StopRobotWheel(int, int);
430 static void InitTimegateWheel(int, int);
431 static void RunTimegateWheel(int, int);
433 static void InitMagicBallDelay(int, int);
434 static void ActivateMagicBall(int, int);
436 static void InitDiagonalMovingElement(int, int);
438 struct ChangingElementInfo
443 void (*pre_change_function)(int x, int y);
444 void (*change_function)(int x, int y);
445 void (*post_change_function)(int x, int y);
448 static struct ChangingElementInfo change_delay_list[] =
499 EL_SWITCHGATE_OPENING,
507 EL_SWITCHGATE_CLOSING,
508 EL_SWITCHGATE_CLOSED,
540 EL_ACID_SPLASH_RIGHT,
549 EL_SP_BUGGY_BASE_ACTIVATING,
556 EL_SP_BUGGY_BASE_ACTIVATING,
557 EL_SP_BUGGY_BASE_ACTIVE,
564 EL_SP_BUGGY_BASE_ACTIVE,
588 EL_ROBOT_WHEEL_ACTIVE,
596 EL_TIMEGATE_SWITCH_ACTIVE,
604 EL_EMC_MAGIC_BALL_ACTIVE,
605 EL_EMC_MAGIC_BALL_ACTIVE,
612 EL_EMC_SPRING_BUMPER_ACTIVE,
613 EL_EMC_SPRING_BUMPER,
620 EL_DIAGONAL_SHRINKING,
633 InitDiagonalMovingElement
649 int push_delay_fixed, push_delay_random;
654 { EL_BALLOON, 0, 0 },
656 { EL_SOKOBAN_OBJECT, 2, 0 },
657 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
658 { EL_SATELLITE, 2, 0 },
659 { EL_SP_DISK_YELLOW, 2, 0 },
661 { EL_UNDEFINED, 0, 0 },
669 move_stepsize_list[] =
671 { EL_AMOEBA_DROP, 2 },
672 { EL_AMOEBA_DROPPING, 2 },
673 { EL_QUICKSAND_FILLING, 1 },
674 { EL_QUICKSAND_EMPTYING, 1 },
675 { EL_MAGIC_WALL_FILLING, 2 },
676 { EL_BD_MAGIC_WALL_FILLING, 2 },
677 { EL_MAGIC_WALL_EMPTYING, 2 },
678 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
688 collect_count_list[] =
691 { EL_BD_DIAMOND, 1 },
692 { EL_EMERALD_YELLOW, 1 },
693 { EL_EMERALD_RED, 1 },
694 { EL_EMERALD_PURPLE, 1 },
696 { EL_SP_INFOTRON, 1 },
708 access_direction_list[] =
710 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
711 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
712 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
713 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
714 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
715 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
716 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
717 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
718 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
719 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
720 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
722 { EL_SP_PORT_LEFT, MV_RIGHT },
723 { EL_SP_PORT_RIGHT, MV_LEFT },
724 { EL_SP_PORT_UP, MV_DOWN },
725 { EL_SP_PORT_DOWN, MV_UP },
726 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
727 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
728 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
729 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
730 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
731 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
732 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
733 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
734 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
735 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
736 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
737 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
738 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
739 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
740 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
742 { EL_UNDEFINED, MV_NONE }
745 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
747 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
748 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
749 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
750 IS_JUST_CHANGING(x, y))
752 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
754 /* static variables for playfield scan mode (scanning forward or backward) */
755 static int playfield_scan_start_x = 0;
756 static int playfield_scan_start_y = 0;
757 static int playfield_scan_delta_x = 1;
758 static int playfield_scan_delta_y = 1;
760 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
761 (y) >= 0 && (y) <= lev_fieldy - 1; \
762 (y) += playfield_scan_delta_y) \
763 for ((x) = playfield_scan_start_x; \
764 (x) >= 0 && (x) <= lev_fieldx - 1; \
765 (x) += playfield_scan_delta_x) \
768 void DEBUG_SetMaximumDynamite()
772 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
773 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
774 local_player->inventory_element[local_player->inventory_size++] =
779 static void InitPlayfieldScanModeVars()
781 if (game.use_reverse_scan_direction)
783 playfield_scan_start_x = lev_fieldx - 1;
784 playfield_scan_start_y = lev_fieldy - 1;
786 playfield_scan_delta_x = -1;
787 playfield_scan_delta_y = -1;
791 playfield_scan_start_x = 0;
792 playfield_scan_start_y = 0;
794 playfield_scan_delta_x = 1;
795 playfield_scan_delta_y = 1;
799 static void InitPlayfieldScanMode(int mode)
801 game.use_reverse_scan_direction =
802 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
804 InitPlayfieldScanModeVars();
807 static int get_move_delay_from_stepsize(int move_stepsize)
810 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
812 /* make sure that stepsize value is always a power of 2 */
813 move_stepsize = (1 << log_2(move_stepsize));
815 return TILEX / move_stepsize;
818 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
821 int player_nr = player->index_nr;
822 int move_delay = get_move_delay_from_stepsize(move_stepsize);
823 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
825 /* do no immediately change move delay -- the player might just be moving */
826 player->move_delay_value_next = move_delay;
828 /* information if player can move must be set separately */
829 player->cannot_move = cannot_move;
833 player->move_delay = game.initial_move_delay[player_nr];
834 player->move_delay_value = game.initial_move_delay_value[player_nr];
836 player->move_delay_value_next = -1;
838 player->move_delay_reset_counter = 0;
842 void GetPlayerConfig()
844 if (!audio.sound_available)
845 setup.sound_simple = FALSE;
847 if (!audio.loops_available)
848 setup.sound_loops = FALSE;
850 if (!audio.music_available)
851 setup.sound_music = FALSE;
853 if (!video.fullscreen_available)
854 setup.fullscreen = FALSE;
856 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
858 SetAudioMode(setup.sound);
862 static int getBeltNrFromBeltElement(int element)
864 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
865 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
866 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
869 static int getBeltNrFromBeltActiveElement(int element)
871 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
872 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
873 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
876 static int getBeltNrFromBeltSwitchElement(int element)
878 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
879 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
880 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
883 static int getBeltDirNrFromBeltSwitchElement(int element)
885 static int belt_base_element[4] =
887 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
888 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
889 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
890 EL_CONVEYOR_BELT_4_SWITCH_LEFT
893 int belt_nr = getBeltNrFromBeltSwitchElement(element);
894 int belt_dir_nr = element - belt_base_element[belt_nr];
896 return (belt_dir_nr % 3);
899 static int getBeltDirFromBeltSwitchElement(int element)
901 static int belt_move_dir[3] =
908 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
910 return belt_move_dir[belt_dir_nr];
913 static int get_element_from_group_element(int element)
915 if (IS_GROUP_ELEMENT(element))
917 struct ElementGroupInfo *group = element_info[element].group;
918 int last_anim_random_frame = gfx.anim_random_frame;
921 if (group->choice_mode == ANIM_RANDOM)
922 gfx.anim_random_frame = RND(group->num_elements_resolved);
924 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
925 group->choice_mode, 0,
928 if (group->choice_mode == ANIM_RANDOM)
929 gfx.anim_random_frame = last_anim_random_frame;
933 element = group->element_resolved[element_pos];
939 static void InitPlayerField(int x, int y, int element, boolean init_game)
941 if (element == EL_SP_MURPHY)
945 if (stored_player[0].present)
947 Feld[x][y] = EL_SP_MURPHY_CLONE;
953 stored_player[0].use_murphy = TRUE;
955 if (!level.use_artwork_element[0])
956 stored_player[0].artwork_element = EL_SP_MURPHY;
959 Feld[x][y] = EL_PLAYER_1;
965 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
966 int jx = player->jx, jy = player->jy;
968 player->present = TRUE;
970 player->block_last_field = (element == EL_SP_MURPHY ?
971 level.sp_block_last_field :
972 level.block_last_field);
974 /* ---------- initialize player's last field block delay --------------- */
976 /* always start with reliable default value (no adjustment needed) */
977 player->block_delay_adjustment = 0;
979 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
980 if (player->block_last_field && element == EL_SP_MURPHY)
981 player->block_delay_adjustment = 1;
983 /* special case 2: in game engines before 3.1.1, blocking was different */
984 if (game.use_block_last_field_bug)
985 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
987 if (!options.network || player->connected)
989 player->active = TRUE;
991 /* remove potentially duplicate players */
992 if (StorePlayer[jx][jy] == Feld[x][y])
993 StorePlayer[jx][jy] = 0;
995 StorePlayer[x][y] = Feld[x][y];
999 printf("Player %d activated.\n", player->element_nr);
1000 printf("[Local player is %d and currently %s.]\n",
1001 local_player->element_nr,
1002 local_player->active ? "active" : "not active");
1006 Feld[x][y] = EL_EMPTY;
1008 player->jx = player->last_jx = x;
1009 player->jy = player->last_jy = y;
1013 static void InitField(int x, int y, boolean init_game)
1015 int element = Feld[x][y];
1024 InitPlayerField(x, y, element, init_game);
1027 case EL_SOKOBAN_FIELD_PLAYER:
1028 element = Feld[x][y] = EL_PLAYER_1;
1029 InitField(x, y, init_game);
1031 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1032 InitField(x, y, init_game);
1035 case EL_SOKOBAN_FIELD_EMPTY:
1036 local_player->sokobanfields_still_needed++;
1040 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1041 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1042 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1043 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1044 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1045 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1046 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1047 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1048 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1049 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1058 case EL_SPACESHIP_RIGHT:
1059 case EL_SPACESHIP_UP:
1060 case EL_SPACESHIP_LEFT:
1061 case EL_SPACESHIP_DOWN:
1062 case EL_BD_BUTTERFLY:
1063 case EL_BD_BUTTERFLY_RIGHT:
1064 case EL_BD_BUTTERFLY_UP:
1065 case EL_BD_BUTTERFLY_LEFT:
1066 case EL_BD_BUTTERFLY_DOWN:
1068 case EL_BD_FIREFLY_RIGHT:
1069 case EL_BD_FIREFLY_UP:
1070 case EL_BD_FIREFLY_LEFT:
1071 case EL_BD_FIREFLY_DOWN:
1072 case EL_PACMAN_RIGHT:
1074 case EL_PACMAN_LEFT:
1075 case EL_PACMAN_DOWN:
1077 case EL_YAMYAM_LEFT:
1078 case EL_YAMYAM_RIGHT:
1080 case EL_YAMYAM_DOWN:
1081 case EL_DARK_YAMYAM:
1084 case EL_SP_SNIKSNAK:
1085 case EL_SP_ELECTRON:
1094 case EL_AMOEBA_FULL:
1099 case EL_AMOEBA_DROP:
1100 if (y == lev_fieldy - 1)
1102 Feld[x][y] = EL_AMOEBA_GROWING;
1103 Store[x][y] = EL_AMOEBA_WET;
1107 case EL_DYNAMITE_ACTIVE:
1108 case EL_SP_DISK_RED_ACTIVE:
1109 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1110 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1111 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1112 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1113 MovDelay[x][y] = 96;
1116 case EL_EM_DYNAMITE_ACTIVE:
1117 MovDelay[x][y] = 32;
1121 local_player->lights_still_needed++;
1125 local_player->friends_still_needed++;
1130 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1133 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1134 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1135 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1136 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1137 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1138 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1139 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1140 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1141 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1142 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1143 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1144 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1147 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1148 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1149 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1151 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1153 game.belt_dir[belt_nr] = belt_dir;
1154 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1156 else /* more than one switch -- set it like the first switch */
1158 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1163 #if !USE_BOTH_SWITCHGATE_SWITCHES
1164 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1166 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1170 case EL_LIGHT_SWITCH_ACTIVE:
1172 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1175 case EL_INVISIBLE_STEELWALL:
1176 case EL_INVISIBLE_WALL:
1177 case EL_INVISIBLE_SAND:
1178 if (game.light_time_left > 0 ||
1179 game.lenses_time_left > 0)
1180 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1183 case EL_EMC_MAGIC_BALL:
1184 if (game.ball_state)
1185 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1188 case EL_EMC_MAGIC_BALL_SWITCH:
1189 if (game.ball_state)
1190 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1195 if (IS_CUSTOM_ELEMENT(element))
1197 if (CAN_MOVE(element))
1200 #if USE_NEW_CUSTOM_VALUE
1201 if (!element_info[element].use_last_ce_value || init_game)
1202 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1206 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1209 else if (IS_GROUP_ELEMENT(element))
1212 Feld[x][y] = get_element_from_group_element(element);
1214 InitField(x, y, init_game);
1216 struct ElementGroupInfo *group = element_info[element].group;
1217 int last_anim_random_frame = gfx.anim_random_frame;
1220 if (group->choice_mode == ANIM_RANDOM)
1221 gfx.anim_random_frame = RND(group->num_elements_resolved);
1223 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1224 group->choice_mode, 0,
1227 if (group->choice_mode == ANIM_RANDOM)
1228 gfx.anim_random_frame = last_anim_random_frame;
1230 group->choice_pos++;
1232 Feld[x][y] = group->element_resolved[element_pos];
1234 InitField(x, y, init_game);
1243 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1248 #if USE_NEW_CUSTOM_VALUE
1251 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1253 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1261 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1263 InitField(x, y, init_game);
1265 /* not needed to call InitMovDir() -- already done by InitField()! */
1266 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1267 CAN_MOVE(Feld[x][y]))
1271 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1273 int old_element = Feld[x][y];
1275 InitField(x, y, init_game);
1277 /* not needed to call InitMovDir() -- already done by InitField()! */
1278 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1279 CAN_MOVE(old_element) &&
1280 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1283 /* this case is in fact a combination of not less than three bugs:
1284 first, it calls InitMovDir() for elements that can move, although this is
1285 already done by InitField(); then, it checks the element that was at this
1286 field _before_ the call to InitField() (which can change it); lastly, it
1287 was not called for "mole with direction" elements, which were treated as
1288 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1292 inline void DrawGameValue_Emeralds(int value)
1294 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1296 if (PANEL_DEACTIVATED(game.panel.gems))
1299 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1302 inline void DrawGameValue_Dynamite(int value)
1304 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1306 if (PANEL_DEACTIVATED(game.panel.inventory))
1309 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1312 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1314 int base_key_graphic = EL_KEY_1;
1317 if (PANEL_DEACTIVATED(game.panel.keys))
1320 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1321 base_key_graphic = EL_EM_KEY_1;
1323 /* currently only 4 of 8 possible keys are displayed */
1324 for (i = 0; i < STD_NUM_KEYS; i++)
1326 int x = XX_KEYS + i * MINI_TILEX;
1330 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1332 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1333 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1337 inline void DrawGameValue_Score(int value)
1339 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1341 if (PANEL_DEACTIVATED(game.panel.score))
1344 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1347 inline void DrawGameValue_Time(int value)
1349 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1350 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1352 if (PANEL_DEACTIVATED(game.panel.time))
1355 /* clear background if value just changed its size */
1356 if (value == 999 || value == 1000)
1357 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1360 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1362 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1365 inline void DrawGameValue_Level(int value)
1367 if (PANEL_DEACTIVATED(game.panel.level))
1371 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1374 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1377 /* misuse area for displaying emeralds to draw bigger level number */
1378 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1379 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1381 /* now copy it to the area for displaying level number */
1382 BlitBitmap(drawto, drawto,
1383 DX_EMERALDS, DY_EMERALDS + 1,
1384 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1385 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1386 DX_LEVEL - 1, DY_LEVEL + 1);
1388 /* restore the area for displaying emeralds */
1389 DrawGameValue_Emeralds(local_player->gems_still_needed);
1391 /* yes, this is all really ugly :-) */
1396 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1399 int key[MAX_NUM_KEYS];
1402 for (i = 0; i < MAX_NUM_KEYS; i++)
1403 key[i] = key_bits & (1 << i);
1405 DrawGameValue_Level(level_nr);
1407 DrawGameValue_Emeralds(emeralds);
1408 DrawGameValue_Dynamite(dynamite);
1409 DrawGameValue_Score(score);
1410 DrawGameValue_Time(time);
1412 DrawGameValue_Keys(key);
1415 void DrawGameDoorValues()
1417 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1418 int dynamite_state = 0;
1422 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1424 DrawGameDoorValues_EM();
1430 DrawGameValue_Level(level_nr);
1432 DrawGameValue_Emeralds(local_player->gems_still_needed);
1433 DrawGameValue_Dynamite(local_player->inventory_size);
1434 DrawGameValue_Score(local_player->score);
1435 DrawGameValue_Time(TimeLeft);
1439 if (game.centered_player_nr == -1)
1441 for (i = 0; i < MAX_PLAYERS; i++)
1443 for (j = 0; j < MAX_NUM_KEYS; j++)
1444 if (stored_player[i].key[j])
1445 key_bits |= (1 << j);
1447 dynamite_state += stored_player[i].inventory_size;
1451 DrawGameValue_Keys(stored_player[i].key);
1456 int player_nr = game.centered_player_nr;
1458 for (i = 0; i < MAX_NUM_KEYS; i++)
1459 if (stored_player[player_nr].key[i])
1460 key_bits |= (1 << i);
1462 dynamite_state = stored_player[player_nr].inventory_size;
1465 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1466 local_player->score, time_value, key_bits);
1471 static void resolve_group_element(int group_element, int recursion_depth)
1473 static int group_nr;
1474 static struct ElementGroupInfo *group;
1475 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1478 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1480 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1481 group_element - EL_GROUP_START + 1);
1483 /* replace element which caused too deep recursion by question mark */
1484 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1489 if (recursion_depth == 0) /* initialization */
1491 group = element_info[group_element].group;
1492 group_nr = group_element - EL_GROUP_START;
1494 group->num_elements_resolved = 0;
1495 group->choice_pos = 0;
1498 for (i = 0; i < actual_group->num_elements; i++)
1500 int element = actual_group->element[i];
1502 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1505 if (IS_GROUP_ELEMENT(element))
1506 resolve_group_element(element, recursion_depth + 1);
1509 group->element_resolved[group->num_elements_resolved++] = element;
1510 element_info[element].in_group[group_nr] = TRUE;
1517 static void replace_reference_element(int base_element, int *element)
1519 if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
1521 *element = base_element + *element - EL_SELF;
1522 *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
1523 *element > EL_CUSTOM_END ? EL_CUSTOM_END : *element);
1529 =============================================================================
1531 -----------------------------------------------------------------------------
1532 initialize game engine due to level / tape version number
1533 =============================================================================
1536 static void InitGameEngine()
1538 int i, j, k, l, x, y;
1540 /* set game engine from tape file when re-playing, else from level file */
1541 game.engine_version = (tape.playing ? tape.engine_version :
1542 level.game_version);
1544 /* ---------------------------------------------------------------------- */
1545 /* set flags for bugs and changes according to active game engine version */
1546 /* ---------------------------------------------------------------------- */
1549 Summary of bugfix/change:
1550 Fixed handling for custom elements that change when pushed by the player.
1552 Fixed/changed in version:
1556 Before 3.1.0, custom elements that "change when pushing" changed directly
1557 after the player started pushing them (until then handled in "DigField()").
1558 Since 3.1.0, these custom elements are not changed until the "pushing"
1559 move of the element is finished (now handled in "ContinueMoving()").
1561 Affected levels/tapes:
1562 The first condition is generally needed for all levels/tapes before version
1563 3.1.0, which might use the old behaviour before it was changed; known tapes
1564 that are affected are some tapes from the level set "Walpurgis Gardens" by
1566 The second condition is an exception from the above case and is needed for
1567 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1568 above (including some development versions of 3.1.0), but before it was
1569 known that this change would break tapes like the above and was fixed in
1570 3.1.1, so that the changed behaviour was active although the engine version
1571 while recording maybe was before 3.1.0. There is at least one tape that is
1572 affected by this exception, which is the tape for the one-level set "Bug
1573 Machine" by Juergen Bonhagen.
1576 game.use_change_when_pushing_bug =
1577 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1579 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1580 tape.game_version < VERSION_IDENT(3,1,1,0)));
1583 Summary of bugfix/change:
1584 Fixed handling for blocking the field the player leaves when moving.
1586 Fixed/changed in version:
1590 Before 3.1.1, when "block last field when moving" was enabled, the field
1591 the player is leaving when moving was blocked for the time of the move,
1592 and was directly unblocked afterwards. This resulted in the last field
1593 being blocked for exactly one less than the number of frames of one player
1594 move. Additionally, even when blocking was disabled, the last field was
1595 blocked for exactly one frame.
1596 Since 3.1.1, due to changes in player movement handling, the last field
1597 is not blocked at all when blocking is disabled. When blocking is enabled,
1598 the last field is blocked for exactly the number of frames of one player
1599 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1600 last field is blocked for exactly one more than the number of frames of
1603 Affected levels/tapes:
1604 (!!! yet to be determined -- probably many !!!)
1607 game.use_block_last_field_bug =
1608 (game.engine_version < VERSION_IDENT(3,1,1,0));
1611 Summary of bugfix/change:
1612 Changed behaviour of CE changes with multiple changes per single frame.
1614 Fixed/changed in version:
1618 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1619 This resulted in race conditions where CEs seem to behave strange in some
1620 situations (where triggered CE changes were just skipped because there was
1621 already a CE change on that tile in the playfield in that engine frame).
1622 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1623 (The number of changes per frame must be limited in any case, because else
1624 it is easily possible to define CE changes that would result in an infinite
1625 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1626 should be set large enough so that it would only be reached in cases where
1627 the corresponding CE change conditions run into a loop. Therefore, it seems
1628 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1629 maximal number of change pages for custom elements.)
1631 Affected levels/tapes:
1635 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1636 game.max_num_changes_per_frame = 1;
1638 game.max_num_changes_per_frame =
1639 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1642 /* ---------------------------------------------------------------------- */
1644 /* default scan direction: scan playfield from top/left to bottom/right */
1645 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1647 /* dynamically adjust element properties according to game engine version */
1648 InitElementPropertiesEngine(game.engine_version);
1651 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1652 printf(" tape version == %06d [%s] [file: %06d]\n",
1653 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1655 printf(" => game.engine_version == %06d\n", game.engine_version);
1659 /* ---------- recursively resolve group elements ------------------------- */
1661 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1662 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1663 element_info[i].in_group[j] = FALSE;
1665 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1666 resolve_group_element(EL_GROUP_START + i, 0);
1669 /* ---------- initialize player's initial move delay --------------------- */
1672 /* dynamically adjust player properties according to level information */
1673 for (i = 0; i < MAX_PLAYERS; i++)
1674 game.initial_move_delay_value[i] =
1675 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1677 /* dynamically adjust player properties according to level information */
1678 game.initial_move_delay_value =
1679 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1682 /* dynamically adjust player properties according to game engine version */
1683 for (i = 0; i < MAX_PLAYERS; i++)
1684 game.initial_move_delay[i] =
1685 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1686 game.initial_move_delay_value[i] : 0);
1688 /* ---------- initialize player's initial push delay --------------------- */
1690 /* dynamically adjust player properties according to game engine version */
1691 game.initial_push_delay_value =
1692 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1694 /* ---------- initialize changing elements ------------------------------- */
1696 /* initialize changing elements information */
1697 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1699 struct ElementInfo *ei = &element_info[i];
1701 /* this pointer might have been changed in the level editor */
1702 ei->change = &ei->change_page[0];
1704 if (!IS_CUSTOM_ELEMENT(i))
1706 ei->change->target_element = EL_EMPTY_SPACE;
1707 ei->change->delay_fixed = 0;
1708 ei->change->delay_random = 0;
1709 ei->change->delay_frames = 1;
1712 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1714 ei->has_change_event[j] = FALSE;
1716 ei->event_page_nr[j] = 0;
1717 ei->event_page[j] = &ei->change_page[0];
1721 /* add changing elements from pre-defined list */
1722 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1724 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1725 struct ElementInfo *ei = &element_info[ch_delay->element];
1727 ei->change->target_element = ch_delay->target_element;
1728 ei->change->delay_fixed = ch_delay->change_delay;
1730 ei->change->pre_change_function = ch_delay->pre_change_function;
1731 ei->change->change_function = ch_delay->change_function;
1732 ei->change->post_change_function = ch_delay->post_change_function;
1734 ei->change->can_change = TRUE;
1735 ei->change->can_change_or_has_action = TRUE;
1737 ei->has_change_event[CE_DELAY] = TRUE;
1739 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1740 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1743 /* ---------- initialize internal run-time variables ------------- */
1745 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1747 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1749 for (j = 0; j < ei->num_change_pages; j++)
1751 ei->change_page[j].can_change_or_has_action =
1752 (ei->change_page[j].can_change |
1753 ei->change_page[j].has_action);
1757 /* add change events from custom element configuration */
1758 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1760 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1762 for (j = 0; j < ei->num_change_pages; j++)
1764 if (!ei->change_page[j].can_change_or_has_action)
1767 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1769 /* only add event page for the first page found with this event */
1770 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1772 ei->has_change_event[k] = TRUE;
1774 ei->event_page_nr[k] = j;
1775 ei->event_page[k] = &ei->change_page[j];
1781 /* ---------- initialize run-time trigger player and element ------------- */
1783 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1785 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1787 for (j = 0; j < ei->num_change_pages; j++)
1789 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1790 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1791 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1792 ei->change_page[j].actual_trigger_ce_value = 0;
1793 ei->change_page[j].actual_trigger_ce_score = 0;
1797 /* ---------- initialize trigger events ---------------------------------- */
1799 /* initialize trigger events information */
1800 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1801 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1802 trigger_events[i][j] = FALSE;
1804 /* add trigger events from element change event properties */
1805 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1807 struct ElementInfo *ei = &element_info[i];
1809 for (j = 0; j < ei->num_change_pages; j++)
1811 if (!ei->change_page[j].can_change_or_has_action)
1814 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1816 int trigger_element = ei->change_page[j].trigger_element;
1818 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1820 if (ei->change_page[j].has_event[k])
1822 if (IS_GROUP_ELEMENT(trigger_element))
1824 struct ElementGroupInfo *group =
1825 element_info[trigger_element].group;
1827 for (l = 0; l < group->num_elements_resolved; l++)
1828 trigger_events[group->element_resolved[l]][k] = TRUE;
1831 else if (trigger_element == EL_ANY_ELEMENT)
1832 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1833 trigger_events[l][k] = TRUE;
1836 trigger_events[trigger_element][k] = TRUE;
1843 /* ---------- initialize push delay -------------------------------------- */
1845 /* initialize push delay values to default */
1846 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1848 if (!IS_CUSTOM_ELEMENT(i))
1851 /* set default push delay values (corrected since version 3.0.7-1) */
1852 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1854 element_info[i].push_delay_fixed = 2;
1855 element_info[i].push_delay_random = 8;
1859 element_info[i].push_delay_fixed = 8;
1860 element_info[i].push_delay_random = 8;
1863 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1864 element_info[i].push_delay_random = game.default_push_delay_random;
1869 /* set push delay value for certain elements from pre-defined list */
1870 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1872 int e = push_delay_list[i].element;
1874 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1875 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1878 /* set push delay value for Supaplex elements for newer engine versions */
1879 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1881 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1883 if (IS_SP_ELEMENT(i))
1885 /* set SP push delay to just enough to push under a falling zonk */
1886 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1888 element_info[i].push_delay_fixed = delay;
1889 element_info[i].push_delay_random = 0;
1894 /* ---------- initialize move stepsize ----------------------------------- */
1896 /* initialize move stepsize values to default */
1897 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1898 if (!IS_CUSTOM_ELEMENT(i))
1899 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1901 /* set move stepsize value for certain elements from pre-defined list */
1902 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1904 int e = move_stepsize_list[i].element;
1906 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1909 /* ---------- initialize collect score ----------------------------------- */
1911 /* initialize collect score values for custom elements from initial value */
1912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1913 if (IS_CUSTOM_ELEMENT(i))
1914 element_info[i].collect_score = element_info[i].collect_score_initial;
1916 /* ---------- initialize collect count ----------------------------------- */
1918 /* initialize collect count values for non-custom elements */
1919 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1920 if (!IS_CUSTOM_ELEMENT(i))
1921 element_info[i].collect_count_initial = 0;
1923 /* add collect count values for all elements from pre-defined list */
1924 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1925 element_info[collect_count_list[i].element].collect_count_initial =
1926 collect_count_list[i].count;
1928 /* ---------- initialize access direction -------------------------------- */
1930 /* initialize access direction values to default (access from every side) */
1931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1932 if (!IS_CUSTOM_ELEMENT(i))
1933 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1935 /* set access direction value for certain elements from pre-defined list */
1936 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1937 element_info[access_direction_list[i].element].access_direction =
1938 access_direction_list[i].direction;
1940 /* ---------- initialize explosion content ------------------------------- */
1941 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1943 if (IS_CUSTOM_ELEMENT(i))
1946 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1948 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1950 element_info[i].content.e[x][y] =
1951 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1952 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1953 i == EL_PLAYER_3 ? EL_EMERALD :
1954 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1955 i == EL_MOLE ? EL_EMERALD_RED :
1956 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1957 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1958 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1959 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1960 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1961 i == EL_WALL_EMERALD ? EL_EMERALD :
1962 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1963 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1964 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1965 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1966 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1967 i == EL_WALL_PEARL ? EL_PEARL :
1968 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1974 /* ---------- initialize reference elements ------------------------------- */
1975 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1977 int element = EL_CUSTOM_START + i;
1978 struct ElementInfo *ei = &element_info[element];
1980 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1981 replace_reference_element(element, &ei->content.e[x][y]);
1983 for (j = 0; j < ei->num_change_pages; j++)
1985 struct ElementChangeInfo *change = &ei->change_page[j];
1987 replace_reference_element(element, &change->target_element);
1988 replace_reference_element(element, &change->trigger_element);
1990 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1991 replace_reference_element(element, &change->target_content.e[x][y]);
1997 int get_num_special_action(int element, int action_first, int action_last)
1999 int num_special_action = 0;
2002 for (i = action_first; i <= action_last; i++)
2004 boolean found = FALSE;
2006 for (j = 0; j < NUM_DIRECTIONS; j++)
2007 if (el_act_dir2img(element, i, j) !=
2008 el_act_dir2img(element, ACTION_DEFAULT, j))
2012 num_special_action++;
2018 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
2021 return num_special_action;
2025 =============================================================================
2027 -----------------------------------------------------------------------------
2028 initialize and start new game
2029 =============================================================================
2034 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2035 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2036 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2041 /* don't play tapes over network */
2042 network_playing = (options.network && !tape.playing);
2044 for (i = 0; i < MAX_PLAYERS; i++)
2046 struct PlayerInfo *player = &stored_player[i];
2048 player->index_nr = i;
2049 player->index_bit = (1 << i);
2050 player->element_nr = EL_PLAYER_1 + i;
2052 player->present = FALSE;
2053 player->active = FALSE;
2056 player->effective_action = 0;
2057 player->programmed_action = 0;
2060 player->gems_still_needed = level.gems_needed;
2061 player->sokobanfields_still_needed = 0;
2062 player->lights_still_needed = 0;
2063 player->friends_still_needed = 0;
2065 for (j = 0; j < MAX_NUM_KEYS; j++)
2066 player->key[j] = FALSE;
2068 player->dynabomb_count = 0;
2069 player->dynabomb_size = 1;
2070 player->dynabombs_left = 0;
2071 player->dynabomb_xl = FALSE;
2073 player->MovDir = MV_NONE;
2076 player->GfxDir = MV_NONE;
2077 player->GfxAction = ACTION_DEFAULT;
2079 player->StepFrame = 0;
2081 player->use_murphy = FALSE;
2082 player->artwork_element =
2083 (level.use_artwork_element[i] ? level.artwork_element[i] :
2084 player->element_nr);
2086 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2087 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2089 player->gravity = level.initial_player_gravity[i];
2091 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2093 player->actual_frame_counter = 0;
2095 player->step_counter = 0;
2097 player->last_move_dir = MV_NONE;
2099 player->is_active = FALSE;
2101 player->is_waiting = FALSE;
2102 player->is_moving = FALSE;
2103 player->is_auto_moving = FALSE;
2104 player->is_digging = FALSE;
2105 player->is_snapping = FALSE;
2106 player->is_collecting = FALSE;
2107 player->is_pushing = FALSE;
2108 player->is_switching = FALSE;
2109 player->is_dropping = FALSE;
2110 player->is_dropping_pressed = FALSE;
2112 player->is_bored = FALSE;
2113 player->is_sleeping = FALSE;
2115 player->frame_counter_bored = -1;
2116 player->frame_counter_sleeping = -1;
2118 player->anim_delay_counter = 0;
2119 player->post_delay_counter = 0;
2121 player->dir_waiting = MV_NONE;
2122 player->action_waiting = ACTION_DEFAULT;
2123 player->last_action_waiting = ACTION_DEFAULT;
2124 player->special_action_bored = ACTION_DEFAULT;
2125 player->special_action_sleeping = ACTION_DEFAULT;
2128 /* cannot be set here -- could be modified in Init[Player]Field() below */
2130 /* set number of special actions for bored and sleeping animation */
2131 player->num_special_action_bored =
2132 get_num_special_action(player->artwork_element,
2133 ACTION_BORING_1, ACTION_BORING_LAST);
2134 player->num_special_action_sleeping =
2135 get_num_special_action(player->artwork_element,
2136 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2139 player->switch_x = -1;
2140 player->switch_y = -1;
2142 player->drop_x = -1;
2143 player->drop_y = -1;
2145 player->show_envelope = 0;
2148 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2150 player->move_delay = game.initial_move_delay;
2151 player->move_delay_value = game.initial_move_delay_value;
2153 player->move_delay_value_next = -1;
2155 player->move_delay_reset_counter = 0;
2157 player->cannot_move = FALSE;
2160 player->push_delay = -1; /* initialized when pushing starts */
2161 player->push_delay_value = game.initial_push_delay_value;
2163 player->drop_delay = 0;
2164 player->drop_pressed_delay = 0;
2166 player->last_jx = player->last_jy = 0;
2167 player->jx = player->jy = 0;
2169 player->shield_normal_time_left = 0;
2170 player->shield_deadly_time_left = 0;
2172 player->inventory_infinite_element = EL_UNDEFINED;
2173 player->inventory_size = 0;
2175 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2176 SnapField(player, 0, 0);
2178 player->LevelSolved = FALSE;
2179 player->GameOver = FALSE;
2182 network_player_action_received = FALSE;
2184 #if defined(NETWORK_AVALIABLE)
2185 /* initial null action */
2186 if (network_playing)
2187 SendToServer_MovePlayer(MV_NONE);
2196 TimeLeft = level.time;
2199 ScreenMovDir = MV_NONE;
2203 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2205 AllPlayersGone = FALSE;
2207 game.yamyam_content_nr = 0;
2208 game.magic_wall_active = FALSE;
2209 game.magic_wall_time_left = 0;
2210 game.light_time_left = 0;
2211 game.timegate_time_left = 0;
2212 game.switchgate_pos = 0;
2213 game.wind_direction = level.wind_direction_initial;
2215 #if !USE_PLAYER_GRAVITY
2217 game.gravity = FALSE;
2219 game.gravity = level.initial_gravity;
2221 game.explosions_delayed = TRUE;
2224 game.lenses_time_left = 0;
2225 game.magnify_time_left = 0;
2227 game.ball_state = level.ball_state_initial;
2228 game.ball_content_nr = 0;
2230 game.envelope_active = FALSE;
2232 /* set focus to local player for network games, else to all players */
2233 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2234 game.centered_player_nr_next = game.centered_player_nr;
2235 game.set_centered_player = FALSE;
2237 if (network_playing && tape.recording)
2239 /* store client dependent player focus when recording network games */
2240 tape.centered_player_nr_next = game.centered_player_nr_next;
2241 tape.set_centered_player = TRUE;
2245 printf("::: focus set to player %d [%d]\n",
2246 game.centered_player_nr, local_player->index_nr);
2249 for (i = 0; i < NUM_BELTS; i++)
2251 game.belt_dir[i] = MV_NONE;
2252 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2255 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2256 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2259 SCAN_PLAYFIELD(x, y)
2261 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2264 Feld[x][y] = level.field[x][y];
2265 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2266 ChangeDelay[x][y] = 0;
2267 ChangePage[x][y] = -1;
2268 #if USE_NEW_CUSTOM_VALUE
2269 CustomValue[x][y] = 0; /* initialized in InitField() */
2271 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2273 WasJustMoving[x][y] = 0;
2274 WasJustFalling[x][y] = 0;
2275 CheckCollision[x][y] = 0;
2277 Pushed[x][y] = FALSE;
2279 ChangeCount[x][y] = 0;
2280 ChangeEvent[x][y] = -1;
2282 ExplodePhase[x][y] = 0;
2283 ExplodeDelay[x][y] = 0;
2284 ExplodeField[x][y] = EX_TYPE_NONE;
2286 RunnerVisit[x][y] = 0;
2287 PlayerVisit[x][y] = 0;
2290 GfxRandom[x][y] = INIT_GFX_RANDOM();
2291 GfxElement[x][y] = EL_UNDEFINED;
2292 GfxAction[x][y] = ACTION_DEFAULT;
2293 GfxDir[x][y] = MV_NONE;
2297 SCAN_PLAYFIELD(x, y)
2299 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2302 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2304 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2306 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2309 InitField(x, y, TRUE);
2314 for (i = 0; i < MAX_PLAYERS; i++)
2316 struct PlayerInfo *player = &stored_player[i];
2319 /* set number of special actions for bored and sleeping animation */
2320 player->num_special_action_bored =
2321 get_num_special_action(player->artwork_element,
2322 ACTION_BORING_1, ACTION_BORING_LAST);
2323 player->num_special_action_sleeping =
2324 get_num_special_action(player->artwork_element,
2325 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2330 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2331 emulate_sb ? EMU_SOKOBAN :
2332 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2334 #if USE_NEW_ALL_SLIPPERY
2335 /* initialize type of slippery elements */
2336 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2338 if (!IS_CUSTOM_ELEMENT(i))
2340 /* default: elements slip down either to the left or right randomly */
2341 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2343 /* SP style elements prefer to slip down on the left side */
2344 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2345 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2347 /* BD style elements prefer to slip down on the left side */
2348 if (game.emulation == EMU_BOULDERDASH)
2349 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2354 /* initialize explosion and ignition delay */
2355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2357 if (!IS_CUSTOM_ELEMENT(i))
2360 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2361 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2362 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2363 int last_phase = (num_phase + 1) * delay;
2364 int half_phase = (num_phase / 2) * delay;
2366 element_info[i].explosion_delay = last_phase - 1;
2367 element_info[i].ignition_delay = half_phase;
2369 if (i == EL_BLACK_ORB)
2370 element_info[i].ignition_delay = 1;
2374 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2375 element_info[i].explosion_delay = 1;
2377 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2378 element_info[i].ignition_delay = 1;
2382 /* correct non-moving belts to start moving left */
2383 for (i = 0; i < NUM_BELTS; i++)
2384 if (game.belt_dir[i] == MV_NONE)
2385 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2387 /* check if any connected player was not found in playfield */
2388 for (i = 0; i < MAX_PLAYERS; i++)
2390 struct PlayerInfo *player = &stored_player[i];
2392 if (player->connected && !player->present)
2394 for (j = 0; j < MAX_PLAYERS; j++)
2396 struct PlayerInfo *some_player = &stored_player[j];
2397 int jx = some_player->jx, jy = some_player->jy;
2399 /* assign first free player found that is present in the playfield */
2400 if (some_player->present && !some_player->connected)
2402 player->present = TRUE;
2403 player->active = TRUE;
2405 some_player->present = FALSE;
2406 some_player->active = FALSE;
2409 player->element_nr = some_player->element_nr;
2412 player->artwork_element = some_player->artwork_element;
2414 player->block_last_field = some_player->block_last_field;
2415 player->block_delay_adjustment = some_player->block_delay_adjustment;
2417 StorePlayer[jx][jy] = player->element_nr;
2418 player->jx = player->last_jx = jx;
2419 player->jy = player->last_jy = jy;
2429 /* when playing a tape, eliminate all players who do not participate */
2431 for (i = 0; i < MAX_PLAYERS; i++)
2433 if (stored_player[i].active && !tape.player_participates[i])
2435 struct PlayerInfo *player = &stored_player[i];
2436 int jx = player->jx, jy = player->jy;
2438 player->active = FALSE;
2439 StorePlayer[jx][jy] = 0;
2440 Feld[jx][jy] = EL_EMPTY;
2444 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2446 /* when in single player mode, eliminate all but the first active player */
2448 for (i = 0; i < MAX_PLAYERS; i++)
2450 if (stored_player[i].active)
2452 for (j = i + 1; j < MAX_PLAYERS; j++)
2454 if (stored_player[j].active)
2456 struct PlayerInfo *player = &stored_player[j];
2457 int jx = player->jx, jy = player->jy;
2459 player->active = FALSE;
2460 player->present = FALSE;
2462 StorePlayer[jx][jy] = 0;
2463 Feld[jx][jy] = EL_EMPTY;
2470 /* when recording the game, store which players take part in the game */
2473 for (i = 0; i < MAX_PLAYERS; i++)
2474 if (stored_player[i].active)
2475 tape.player_participates[i] = TRUE;
2480 for (i = 0; i < MAX_PLAYERS; i++)
2482 struct PlayerInfo *player = &stored_player[i];
2484 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2489 if (local_player == player)
2490 printf("Player %d is local player.\n", i+1);
2494 if (BorderElement == EL_EMPTY)
2497 SBX_Right = lev_fieldx - SCR_FIELDX;
2499 SBY_Lower = lev_fieldy - SCR_FIELDY;
2504 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2506 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2509 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2510 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2512 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2513 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2515 /* if local player not found, look for custom element that might create
2516 the player (make some assumptions about the right custom element) */
2517 if (!local_player->present)
2519 int start_x = 0, start_y = 0;
2520 int found_rating = 0;
2521 int found_element = EL_UNDEFINED;
2522 int player_nr = local_player->index_nr;
2525 SCAN_PLAYFIELD(x, y)
2527 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2530 int element = Feld[x][y];
2535 if (level.use_start_element[player_nr] &&
2536 level.start_element[player_nr] == element &&
2543 found_element = element;
2546 if (!IS_CUSTOM_ELEMENT(element))
2549 if (CAN_CHANGE(element))
2551 for (i = 0; i < element_info[element].num_change_pages; i++)
2553 /* check for player created from custom element as single target */
2554 content = element_info[element].change_page[i].target_element;
2555 is_player = ELEM_IS_PLAYER(content);
2557 if (is_player && (found_rating < 3 || element < found_element))
2563 found_element = element;
2568 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2570 /* check for player created from custom element as explosion content */
2571 content = element_info[element].content.e[xx][yy];
2572 is_player = ELEM_IS_PLAYER(content);
2574 if (is_player && (found_rating < 2 || element < found_element))
2576 start_x = x + xx - 1;
2577 start_y = y + yy - 1;
2580 found_element = element;
2583 if (!CAN_CHANGE(element))
2586 for (i = 0; i < element_info[element].num_change_pages; i++)
2588 /* check for player created from custom element as extended target */
2590 element_info[element].change_page[i].target_content.e[xx][yy];
2592 is_player = ELEM_IS_PLAYER(content);
2594 if (is_player && (found_rating < 1 || element < found_element))
2596 start_x = x + xx - 1;
2597 start_y = y + yy - 1;
2600 found_element = element;
2606 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2607 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2610 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2611 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2616 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2617 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2618 local_player->jx - MIDPOSX);
2620 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2621 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2622 local_player->jy - MIDPOSY);
2625 if (!game.restart_level)
2626 CloseDoor(DOOR_CLOSE_1);
2628 /* !!! FIX THIS (START) !!! */
2629 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2631 InitGameEngine_EM();
2638 /* after drawing the level, correct some elements */
2639 if (game.timegate_time_left == 0)
2640 CloseAllOpenTimegates();
2642 if (setup.soft_scrolling)
2643 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2645 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2648 /* !!! FIX THIS (END) !!! */
2650 if (!game.restart_level)
2652 /* copy default game door content to main double buffer */
2653 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2654 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2658 SetPanelBackground();
2659 SetDrawBackgroundMask(REDRAW_DOOR_1);
2662 DrawGameDoorValues();
2664 if (!game.restart_level)
2668 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2669 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2670 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2674 /* copy actual game door content to door double buffer for OpenDoor() */
2675 BlitBitmap(drawto, bitmap_db_door,
2676 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2678 OpenDoor(DOOR_OPEN_ALL);
2680 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2682 if (setup.sound_music)
2685 KeyboardAutoRepeatOffUnlessAutoplay();
2689 for (i = 0; i < MAX_PLAYERS; i++)
2690 printf("Player %d %sactive.\n",
2691 i + 1, (stored_player[i].active ? "" : "not "));
2695 game.restart_level = FALSE;
2698 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2700 /* this is used for non-R'n'D game engines to update certain engine values */
2702 /* needed to determine if sounds are played within the visible screen area */
2703 scroll_x = actual_scroll_x;
2704 scroll_y = actual_scroll_y;
2707 void InitMovDir(int x, int y)
2709 int i, element = Feld[x][y];
2710 static int xy[4][2] =
2717 static int direction[3][4] =
2719 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2720 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2721 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2730 Feld[x][y] = EL_BUG;
2731 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2734 case EL_SPACESHIP_RIGHT:
2735 case EL_SPACESHIP_UP:
2736 case EL_SPACESHIP_LEFT:
2737 case EL_SPACESHIP_DOWN:
2738 Feld[x][y] = EL_SPACESHIP;
2739 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2742 case EL_BD_BUTTERFLY_RIGHT:
2743 case EL_BD_BUTTERFLY_UP:
2744 case EL_BD_BUTTERFLY_LEFT:
2745 case EL_BD_BUTTERFLY_DOWN:
2746 Feld[x][y] = EL_BD_BUTTERFLY;
2747 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2750 case EL_BD_FIREFLY_RIGHT:
2751 case EL_BD_FIREFLY_UP:
2752 case EL_BD_FIREFLY_LEFT:
2753 case EL_BD_FIREFLY_DOWN:
2754 Feld[x][y] = EL_BD_FIREFLY;
2755 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2758 case EL_PACMAN_RIGHT:
2760 case EL_PACMAN_LEFT:
2761 case EL_PACMAN_DOWN:
2762 Feld[x][y] = EL_PACMAN;
2763 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2766 case EL_YAMYAM_LEFT:
2767 case EL_YAMYAM_RIGHT:
2769 case EL_YAMYAM_DOWN:
2770 Feld[x][y] = EL_YAMYAM;
2771 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2774 case EL_SP_SNIKSNAK:
2775 MovDir[x][y] = MV_UP;
2778 case EL_SP_ELECTRON:
2779 MovDir[x][y] = MV_LEFT;
2786 Feld[x][y] = EL_MOLE;
2787 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2791 if (IS_CUSTOM_ELEMENT(element))
2793 struct ElementInfo *ei = &element_info[element];
2794 int move_direction_initial = ei->move_direction_initial;
2795 int move_pattern = ei->move_pattern;
2797 if (move_direction_initial == MV_START_PREVIOUS)
2799 if (MovDir[x][y] != MV_NONE)
2802 move_direction_initial = MV_START_AUTOMATIC;
2805 if (move_direction_initial == MV_START_RANDOM)
2806 MovDir[x][y] = 1 << RND(4);
2807 else if (move_direction_initial & MV_ANY_DIRECTION)
2808 MovDir[x][y] = move_direction_initial;
2809 else if (move_pattern == MV_ALL_DIRECTIONS ||
2810 move_pattern == MV_TURNING_LEFT ||
2811 move_pattern == MV_TURNING_RIGHT ||
2812 move_pattern == MV_TURNING_LEFT_RIGHT ||
2813 move_pattern == MV_TURNING_RIGHT_LEFT ||
2814 move_pattern == MV_TURNING_RANDOM)
2815 MovDir[x][y] = 1 << RND(4);
2816 else if (move_pattern == MV_HORIZONTAL)
2817 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2818 else if (move_pattern == MV_VERTICAL)
2819 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2820 else if (move_pattern & MV_ANY_DIRECTION)
2821 MovDir[x][y] = element_info[element].move_pattern;
2822 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2823 move_pattern == MV_ALONG_RIGHT_SIDE)
2825 /* use random direction as default start direction */
2826 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2827 MovDir[x][y] = 1 << RND(4);
2829 for (i = 0; i < NUM_DIRECTIONS; i++)
2831 int x1 = x + xy[i][0];
2832 int y1 = y + xy[i][1];
2834 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2836 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2837 MovDir[x][y] = direction[0][i];
2839 MovDir[x][y] = direction[1][i];
2848 MovDir[x][y] = 1 << RND(4);
2850 if (element != EL_BUG &&
2851 element != EL_SPACESHIP &&
2852 element != EL_BD_BUTTERFLY &&
2853 element != EL_BD_FIREFLY)
2856 for (i = 0; i < NUM_DIRECTIONS; i++)
2858 int x1 = x + xy[i][0];
2859 int y1 = y + xy[i][1];
2861 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2863 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2865 MovDir[x][y] = direction[0][i];
2868 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2869 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2871 MovDir[x][y] = direction[1][i];
2880 GfxDir[x][y] = MovDir[x][y];
2883 void InitAmoebaNr(int x, int y)
2886 int group_nr = AmoebeNachbarNr(x, y);
2890 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2892 if (AmoebaCnt[i] == 0)
2900 AmoebaNr[x][y] = group_nr;
2901 AmoebaCnt[group_nr]++;
2902 AmoebaCnt2[group_nr]++;
2908 boolean raise_level = FALSE;
2910 if (local_player->MovPos)
2913 if (tape.auto_play) /* tape might already be stopped here */
2914 tape.auto_play_level_solved = TRUE;
2916 local_player->LevelSolved = FALSE;
2918 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2922 if (!tape.playing && setup.sound_loops)
2923 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2924 SND_CTRL_PLAY_LOOP);
2926 while (TimeLeft > 0)
2928 if (!tape.playing && !setup.sound_loops)
2929 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2931 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2934 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2939 RaiseScore(level.score[SC_TIME_BONUS]);
2942 DrawGameValue_Time(TimeLeft);
2950 if (!tape.playing && setup.sound_loops)
2951 StopSound(SND_GAME_LEVELTIME_BONUS);
2953 else if (level.time == 0) /* level without time limit */
2955 if (!tape.playing && setup.sound_loops)
2956 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2957 SND_CTRL_PLAY_LOOP);
2959 while (TimePlayed < 999)
2961 if (!tape.playing && !setup.sound_loops)
2962 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2964 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2967 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2972 RaiseScore(level.score[SC_TIME_BONUS]);
2975 DrawGameValue_Time(TimePlayed);
2983 if (!tape.playing && setup.sound_loops)
2984 StopSound(SND_GAME_LEVELTIME_BONUS);
2987 /* close exit door after last player */
2988 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2989 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2990 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2992 int element = Feld[ExitX][ExitY];
2994 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2995 EL_SP_EXIT_CLOSING);
2997 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3000 /* player disappears */
3001 if (ExitX >= 0 && ExitY >= 0)
3002 DrawLevelField(ExitX, ExitY);
3008 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
3010 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
3016 CloseDoor(DOOR_CLOSE_1);
3021 SaveTape(tape.level_nr); /* Ask to save tape */
3024 if (level_nr == leveldir_current->handicap_level)
3026 leveldir_current->handicap_level++;
3027 SaveLevelSetup_SeriesInfo();
3030 if (level_editor_test_game)
3031 local_player->score = -1; /* no highscore when playing from editor */
3032 else if (level_nr < leveldir_current->last_level)
3033 raise_level = TRUE; /* advance to next level */
3035 if ((hi_pos = NewHiScore()) >= 0)
3037 game_status = GAME_MODE_SCORES;
3038 DrawHallOfFame(hi_pos);
3047 game_status = GAME_MODE_MAIN;
3064 LoadScore(level_nr);
3066 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3067 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3070 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3072 if (local_player->score > highscore[k].Score)
3074 /* player has made it to the hall of fame */
3076 if (k < MAX_SCORE_ENTRIES - 1)
3078 int m = MAX_SCORE_ENTRIES - 1;
3081 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3082 if (strEqual(setup.player_name, highscore[l].Name))
3084 if (m == k) /* player's new highscore overwrites his old one */
3088 for (l = m; l > k; l--)
3090 strcpy(highscore[l].Name, highscore[l - 1].Name);
3091 highscore[l].Score = highscore[l - 1].Score;
3098 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3099 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3100 highscore[k].Score = local_player->score;
3106 else if (!strncmp(setup.player_name, highscore[k].Name,
3107 MAX_PLAYER_NAME_LEN))
3108 break; /* player already there with a higher score */
3114 SaveScore(level_nr);
3119 inline static int getElementMoveStepsize(int x, int y)
3121 int element = Feld[x][y];
3122 int direction = MovDir[x][y];
3123 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3124 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3125 int horiz_move = (dx != 0);
3126 int sign = (horiz_move ? dx : dy);
3127 int step = sign * element_info[element].move_stepsize;
3129 /* special values for move stepsize for spring and things on conveyor belt */
3133 if (element == EL_SPRING)
3134 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3135 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3136 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3137 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3139 if (CAN_FALL(element) &&
3140 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3141 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3142 else if (element == EL_SPRING)
3143 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3150 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3152 if (player->GfxAction != action || player->GfxDir != dir)
3155 printf("Player frame reset! (%d => %d, %d => %d)\n",
3156 player->GfxAction, action, player->GfxDir, dir);
3159 player->GfxAction = action;
3160 player->GfxDir = dir;
3162 player->StepFrame = 0;
3166 #if USE_GFX_RESET_GFX_ANIMATION
3167 static void ResetGfxFrame(int x, int y, boolean redraw)
3169 int element = Feld[x][y];
3170 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3171 int last_gfx_frame = GfxFrame[x][y];
3173 if (graphic_info[graphic].anim_global_sync)
3174 GfxFrame[x][y] = FrameCounter;
3175 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3176 GfxFrame[x][y] = CustomValue[x][y];
3177 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3178 GfxFrame[x][y] = element_info[element].collect_score;
3179 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3180 GfxFrame[x][y] = ChangeDelay[x][y];
3182 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3183 DrawLevelGraphicAnimation(x, y, graphic);
3187 static void ResetGfxAnimation(int x, int y)
3190 int element, graphic;
3193 GfxAction[x][y] = ACTION_DEFAULT;
3194 GfxDir[x][y] = MovDir[x][y];
3198 element = Feld[x][y];
3199 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3201 if (graphic_info[graphic].anim_global_sync)
3202 GfxFrame[x][y] = FrameCounter;
3203 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3204 GfxFrame[x][y] = CustomValue[x][y];
3205 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3206 GfxFrame[x][y] = element_info[element].collect_score;
3207 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3208 GfxFrame[x][y] = ChangeDelay[x][y];
3211 #if USE_GFX_RESET_GFX_ANIMATION
3212 ResetGfxFrame(x, y, FALSE);
3216 static void ResetRandomAnimationValue(int x, int y)
3218 GfxRandom[x][y] = INIT_GFX_RANDOM();
3221 void InitMovingField(int x, int y, int direction)
3223 int element = Feld[x][y];
3227 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3228 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3232 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3233 ResetGfxAnimation(x, y);
3235 MovDir[x][y] = direction;
3236 GfxDir[x][y] = direction;
3237 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3238 ACTION_FALLING : ACTION_MOVING);
3241 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3243 if (graphic_info[graphic].anim_global_sync)
3244 GfxFrame[x][y] = FrameCounter;
3245 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3246 GfxFrame[x][y] = CustomValue[x][y];
3247 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3248 GfxFrame[x][y] = element_info[element].collect_score;
3249 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3250 GfxFrame[x][y] = ChangeDelay[x][y];
3253 /* this is needed for CEs with property "can move" / "not moving" */
3255 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3257 if (Feld[newx][newy] == EL_EMPTY)
3258 Feld[newx][newy] = EL_BLOCKED;
3260 MovDir[newx][newy] = MovDir[x][y];
3262 #if USE_NEW_CUSTOM_VALUE
3263 CustomValue[newx][newy] = CustomValue[x][y];
3266 GfxFrame[newx][newy] = GfxFrame[x][y];
3267 GfxRandom[newx][newy] = GfxRandom[x][y];
3268 GfxAction[newx][newy] = GfxAction[x][y];
3269 GfxDir[newx][newy] = GfxDir[x][y];
3273 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3275 int direction = MovDir[x][y];
3277 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3278 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3280 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3281 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3288 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3290 int oldx = x, oldy = y;
3291 int direction = MovDir[x][y];
3293 if (direction == MV_LEFT)
3295 else if (direction == MV_RIGHT)
3297 else if (direction == MV_UP)
3299 else if (direction == MV_DOWN)
3302 *comes_from_x = oldx;
3303 *comes_from_y = oldy;
3306 int MovingOrBlocked2Element(int x, int y)
3308 int element = Feld[x][y];
3310 if (element == EL_BLOCKED)
3314 Blocked2Moving(x, y, &oldx, &oldy);
3315 return Feld[oldx][oldy];
3321 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3323 /* like MovingOrBlocked2Element(), but if element is moving
3324 and (x,y) is the field the moving element is just leaving,
3325 return EL_BLOCKED instead of the element value */
3326 int element = Feld[x][y];
3328 if (IS_MOVING(x, y))
3330 if (element == EL_BLOCKED)
3334 Blocked2Moving(x, y, &oldx, &oldy);
3335 return Feld[oldx][oldy];
3344 static void RemoveField(int x, int y)
3346 Feld[x][y] = EL_EMPTY;
3352 #if USE_NEW_CUSTOM_VALUE
3353 CustomValue[x][y] = 0;
3357 ChangeDelay[x][y] = 0;
3358 ChangePage[x][y] = -1;
3359 Pushed[x][y] = FALSE;
3362 ExplodeField[x][y] = EX_TYPE_NONE;
3365 GfxElement[x][y] = EL_UNDEFINED;
3366 GfxAction[x][y] = ACTION_DEFAULT;
3367 GfxDir[x][y] = MV_NONE;
3370 void RemoveMovingField(int x, int y)
3372 int oldx = x, oldy = y, newx = x, newy = y;
3373 int element = Feld[x][y];
3374 int next_element = EL_UNDEFINED;
3376 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3379 if (IS_MOVING(x, y))
3381 Moving2Blocked(x, y, &newx, &newy);
3383 if (Feld[newx][newy] != EL_BLOCKED)
3385 /* element is moving, but target field is not free (blocked), but
3386 already occupied by something different (example: acid pool);
3387 in this case, only remove the moving field, but not the target */
3389 RemoveField(oldx, oldy);
3391 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3393 DrawLevelField(oldx, oldy);
3398 else if (element == EL_BLOCKED)
3400 Blocked2Moving(x, y, &oldx, &oldy);
3401 if (!IS_MOVING(oldx, oldy))
3405 if (element == EL_BLOCKED &&
3406 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3407 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3408 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3409 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3410 next_element = get_next_element(Feld[oldx][oldy]);
3412 RemoveField(oldx, oldy);
3413 RemoveField(newx, newy);
3415 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3417 if (next_element != EL_UNDEFINED)
3418 Feld[oldx][oldy] = next_element;
3420 DrawLevelField(oldx, oldy);
3421 DrawLevelField(newx, newy);
3424 void DrawDynamite(int x, int y)
3426 int sx = SCREENX(x), sy = SCREENY(y);
3427 int graphic = el2img(Feld[x][y]);
3430 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3433 if (IS_WALKABLE_INSIDE(Back[x][y]))
3437 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3438 else if (Store[x][y])
3439 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3441 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3443 if (Back[x][y] || Store[x][y])
3444 DrawGraphicThruMask(sx, sy, graphic, frame);
3446 DrawGraphic(sx, sy, graphic, frame);
3449 void CheckDynamite(int x, int y)
3451 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3455 if (MovDelay[x][y] != 0)
3458 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3464 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3471 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3473 boolean num_checked_players = 0;
3476 for (i = 0; i < MAX_PLAYERS; i++)
3478 if (stored_player[i].active)
3480 int sx = stored_player[i].jx;
3481 int sy = stored_player[i].jy;
3483 if (num_checked_players == 0)
3490 *sx1 = MIN(*sx1, sx);
3491 *sy1 = MIN(*sy1, sy);
3492 *sx2 = MAX(*sx2, sx);
3493 *sy2 = MAX(*sy2, sy);
3496 num_checked_players++;
3501 static boolean checkIfAllPlayersFitToScreen_RND()
3503 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3505 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3507 return (sx2 - sx1 < SCR_FIELDX &&
3508 sy2 - sy1 < SCR_FIELDY);
3511 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3513 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3515 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3517 *sx = (sx1 + sx2) / 2;
3518 *sy = (sy1 + sy2) / 2;
3522 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3523 int center_x, int center_y)
3525 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3527 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3529 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3530 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3533 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3537 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3539 return (max_dx <= SCR_FIELDX / 2 &&
3540 max_dy <= SCR_FIELDY / 2);
3548 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3549 boolean quick_relocation)
3551 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3552 boolean no_delay = (tape.warp_forward);
3553 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3554 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3556 if (quick_relocation)
3558 int offset = (setup.scroll_delay ? 3 : 0);
3565 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3567 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3568 x > SBX_Right + MIDPOSX ? SBX_Right :
3571 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3572 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3577 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3578 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3579 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3581 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3582 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3583 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3585 /* don't scroll over playfield boundaries */
3586 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3587 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3589 /* don't scroll over playfield boundaries */
3590 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3591 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3594 RedrawPlayfield(TRUE, 0,0,0,0);
3598 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3599 x > SBX_Right + MIDPOSX ? SBX_Right :
3602 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3603 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3606 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3608 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3611 int fx = FX, fy = FY;
3613 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3614 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3616 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3622 fx += dx * TILEX / 2;
3623 fy += dy * TILEY / 2;
3625 ScrollLevel(dx, dy);
3628 /* scroll in two steps of half tile size to make things smoother */
3629 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3631 Delay(wait_delay_value);
3633 /* scroll second step to align at full tile size */
3635 Delay(wait_delay_value);
3640 Delay(wait_delay_value);
3646 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3648 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3649 boolean no_delay = (tape.warp_forward);
3650 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3651 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3652 int jx = player->jx;
3653 int jy = player->jy;
3655 if (quick_relocation)
3657 int offset = (setup.scroll_delay ? 3 : 0);
3659 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3661 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3662 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3663 player->jx - MIDPOSX);
3665 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3666 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3667 player->jy - MIDPOSY);
3671 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3672 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3673 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3675 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3676 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3677 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3679 /* don't scroll over playfield boundaries */
3680 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3681 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3683 /* don't scroll over playfield boundaries */
3684 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3685 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3688 RedrawPlayfield(TRUE, 0,0,0,0);
3692 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3693 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3694 player->jx - MIDPOSX);
3696 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3697 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3698 player->jy - MIDPOSY);
3700 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3702 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3705 int fx = FX, fy = FY;
3707 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3708 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3710 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3716 fx += dx * TILEX / 2;
3717 fy += dy * TILEY / 2;
3719 ScrollLevel(dx, dy);
3722 /* scroll in two steps of half tile size to make things smoother */
3723 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3725 Delay(wait_delay_value);
3727 /* scroll second step to align at full tile size */
3729 Delay(wait_delay_value);
3734 Delay(wait_delay_value);
3740 void RelocatePlayer(int jx, int jy, int el_player_raw)
3742 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3743 int player_nr = GET_PLAYER_NR(el_player);
3744 struct PlayerInfo *player = &stored_player[player_nr];
3745 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3746 boolean no_delay = (tape.warp_forward);
3747 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3748 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3749 int old_jx = player->jx;
3750 int old_jy = player->jy;
3751 int old_element = Feld[old_jx][old_jy];
3752 int element = Feld[jx][jy];
3753 boolean player_relocated = (old_jx != jx || old_jy != jy);
3755 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3756 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3757 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3758 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3759 int leave_side_horiz = move_dir_horiz;
3760 int leave_side_vert = move_dir_vert;
3761 int enter_side = enter_side_horiz | enter_side_vert;
3762 int leave_side = leave_side_horiz | leave_side_vert;
3764 if (player->GameOver) /* do not reanimate dead player */
3767 if (!player_relocated) /* no need to relocate the player */
3770 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3772 RemoveField(jx, jy); /* temporarily remove newly placed player */
3773 DrawLevelField(jx, jy);
3776 if (player->present)
3778 while (player->MovPos)
3780 ScrollPlayer(player, SCROLL_GO_ON);
3781 ScrollScreen(NULL, SCROLL_GO_ON);
3783 AdvanceFrameAndPlayerCounters(player->index_nr);
3788 Delay(wait_delay_value);
3791 DrawPlayer(player); /* needed here only to cleanup last field */
3792 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3794 player->is_moving = FALSE;
3797 if (IS_CUSTOM_ELEMENT(old_element))
3798 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3800 player->index_bit, leave_side);
3802 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3804 player->index_bit, leave_side);
3806 Feld[jx][jy] = el_player;
3807 InitPlayerField(jx, jy, el_player, TRUE);
3809 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3811 Feld[jx][jy] = element;
3812 InitField(jx, jy, FALSE);
3816 /* only visually relocate centered player */
3818 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3819 level.instant_relocation);
3821 if (player->index_nr == game.centered_player_nr)
3822 DrawRelocatePlayer(player, level.instant_relocation);
3825 if (player == local_player) /* only visually relocate local player */
3826 DrawRelocatePlayer(player, level.instant_relocation);
3829 TestIfPlayerTouchesBadThing(jx, jy);
3830 TestIfPlayerTouchesCustomElement(jx, jy);
3832 if (IS_CUSTOM_ELEMENT(element))
3833 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3834 player->index_bit, enter_side);
3836 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3837 player->index_bit, enter_side);
3840 void Explode(int ex, int ey, int phase, int mode)
3846 /* !!! eliminate this variable !!! */
3847 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3849 if (game.explosions_delayed)
3851 ExplodeField[ex][ey] = mode;
3855 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3857 int center_element = Feld[ex][ey];
3858 int artwork_element, explosion_element; /* set these values later */
3861 /* --- This is only really needed (and now handled) in "Impact()". --- */
3862 /* do not explode moving elements that left the explode field in time */
3863 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3864 center_element == EL_EMPTY &&
3865 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3870 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3871 if (mode == EX_TYPE_NORMAL ||
3872 mode == EX_TYPE_CENTER ||
3873 mode == EX_TYPE_CROSS)
3874 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3877 /* remove things displayed in background while burning dynamite */
3878 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3881 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3883 /* put moving element to center field (and let it explode there) */
3884 center_element = MovingOrBlocked2Element(ex, ey);
3885 RemoveMovingField(ex, ey);
3886 Feld[ex][ey] = center_element;
3889 /* now "center_element" is finally determined -- set related values now */
3890 artwork_element = center_element; /* for custom player artwork */
3891 explosion_element = center_element; /* for custom player artwork */
3893 if (IS_PLAYER(ex, ey))
3895 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3897 artwork_element = stored_player[player_nr].artwork_element;
3899 if (level.use_explosion_element[player_nr])
3901 explosion_element = level.explosion_element[player_nr];
3902 artwork_element = explosion_element;
3907 if (mode == EX_TYPE_NORMAL ||
3908 mode == EX_TYPE_CENTER ||
3909 mode == EX_TYPE_CROSS)
3910 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3914 last_phase = element_info[explosion_element].explosion_delay + 1;
3916 last_phase = element_info[center_element].explosion_delay + 1;
3919 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3921 int xx = x - ex + 1;
3922 int yy = y - ey + 1;
3925 if (!IN_LEV_FIELD(x, y) ||
3926 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3927 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3930 element = Feld[x][y];
3932 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3934 element = MovingOrBlocked2Element(x, y);
3936 if (!IS_EXPLOSION_PROOF(element))
3937 RemoveMovingField(x, y);
3940 /* indestructible elements can only explode in center (but not flames) */
3941 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3942 mode == EX_TYPE_BORDER)) ||
3943 element == EL_FLAMES)
3946 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3947 behaviour, for example when touching a yamyam that explodes to rocks
3948 with active deadly shield, a rock is created under the player !!! */
3949 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3951 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3952 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3953 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3955 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3958 if (IS_ACTIVE_BOMB(element))
3960 /* re-activate things under the bomb like gate or penguin */
3961 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3968 /* save walkable background elements while explosion on same tile */
3969 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3970 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3971 Back[x][y] = element;
3973 /* ignite explodable elements reached by other explosion */
3974 if (element == EL_EXPLOSION)
3975 element = Store2[x][y];
3977 if (AmoebaNr[x][y] &&
3978 (element == EL_AMOEBA_FULL ||
3979 element == EL_BD_AMOEBA ||
3980 element == EL_AMOEBA_GROWING))
3982 AmoebaCnt[AmoebaNr[x][y]]--;
3983 AmoebaCnt2[AmoebaNr[x][y]]--;
3988 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3991 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3993 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3995 switch(StorePlayer[ex][ey])
3998 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
4001 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
4004 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
4008 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
4013 if (PLAYERINFO(ex, ey)->use_murphy)
4014 Store[x][y] = EL_EMPTY;
4017 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4018 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4019 else if (ELEM_IS_PLAYER(center_element))
4020 Store[x][y] = EL_EMPTY;
4021 else if (center_element == EL_YAMYAM)
4022 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4023 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4024 Store[x][y] = element_info[center_element].content.e[xx][yy];
4026 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4027 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4028 otherwise) -- FIX THIS !!! */
4029 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4030 Store[x][y] = element_info[element].content.e[1][1];
4032 else if (!CAN_EXPLODE(element))
4033 Store[x][y] = element_info[element].content.e[1][1];
4036 Store[x][y] = EL_EMPTY;
4038 else if (center_element == EL_MOLE)
4039 Store[x][y] = EL_EMERALD_RED;
4040 else if (center_element == EL_PENGUIN)
4041 Store[x][y] = EL_EMERALD_PURPLE;
4042 else if (center_element == EL_BUG)
4043 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
4044 else if (center_element == EL_BD_BUTTERFLY)
4045 Store[x][y] = EL_BD_DIAMOND;
4046 else if (center_element == EL_SP_ELECTRON)
4047 Store[x][y] = EL_SP_INFOTRON;
4048 else if (center_element == EL_AMOEBA_TO_DIAMOND)
4049 Store[x][y] = level.amoeba_content;
4050 else if (center_element == EL_YAMYAM)
4051 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4052 else if (IS_CUSTOM_ELEMENT(center_element) &&
4053 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4054 Store[x][y] = element_info[center_element].content.e[xx][yy];
4055 else if (element == EL_WALL_EMERALD)
4056 Store[x][y] = EL_EMERALD;
4057 else if (element == EL_WALL_DIAMOND)
4058 Store[x][y] = EL_DIAMOND;
4059 else if (element == EL_WALL_BD_DIAMOND)
4060 Store[x][y] = EL_BD_DIAMOND;
4061 else if (element == EL_WALL_EMERALD_YELLOW)
4062 Store[x][y] = EL_EMERALD_YELLOW;
4063 else if (element == EL_WALL_EMERALD_RED)
4064 Store[x][y] = EL_EMERALD_RED;
4065 else if (element == EL_WALL_EMERALD_PURPLE)
4066 Store[x][y] = EL_EMERALD_PURPLE;
4067 else if (element == EL_WALL_PEARL)
4068 Store[x][y] = EL_PEARL;
4069 else if (element == EL_WALL_CRYSTAL)
4070 Store[x][y] = EL_CRYSTAL;
4071 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
4072 Store[x][y] = element_info[element].content.e[1][1];
4074 Store[x][y] = EL_EMPTY;
4077 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4078 center_element == EL_AMOEBA_TO_DIAMOND)
4079 Store2[x][y] = element;
4081 Feld[x][y] = EL_EXPLOSION;
4082 GfxElement[x][y] = artwork_element;
4085 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
4086 x, y, artwork_element, EL_NAME(artwork_element));
4089 ExplodePhase[x][y] = 1;
4090 ExplodeDelay[x][y] = last_phase;
4095 if (center_element == EL_YAMYAM)
4096 game.yamyam_content_nr =
4097 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4109 GfxFrame[x][y] = 0; /* restart explosion animation */
4111 last_phase = ExplodeDelay[x][y];
4113 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4117 /* activate this even in non-DEBUG version until cause for crash in
4118 getGraphicAnimationFrame() (see below) is found and eliminated */
4124 /* this can happen if the player leaves an explosion just in time */
4125 if (GfxElement[x][y] == EL_UNDEFINED)
4126 GfxElement[x][y] = EL_EMPTY;
4128 if (GfxElement[x][y] == EL_UNDEFINED)
4131 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4132 printf("Explode(): This should never happen!\n");
4135 GfxElement[x][y] = EL_EMPTY;
4141 border_element = Store2[x][y];
4142 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4143 border_element = StorePlayer[x][y];
4145 if (phase == element_info[border_element].ignition_delay ||
4146 phase == last_phase)
4148 boolean border_explosion = FALSE;
4150 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4151 !PLAYER_EXPLOSION_PROTECTED(x, y))
4153 KillPlayerUnlessExplosionProtected(x, y);
4154 border_explosion = TRUE;
4156 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4158 Feld[x][y] = Store2[x][y];
4161 border_explosion = TRUE;
4163 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4165 AmoebeUmwandeln(x, y);
4167 border_explosion = TRUE;
4170 /* if an element just explodes due to another explosion (chain-reaction),
4171 do not immediately end the new explosion when it was the last frame of
4172 the explosion (as it would be done in the following "if"-statement!) */
4173 if (border_explosion && phase == last_phase)
4177 if (phase == last_phase)
4181 element = Feld[x][y] = Store[x][y];
4182 Store[x][y] = Store2[x][y] = 0;
4183 GfxElement[x][y] = EL_UNDEFINED;
4185 /* player can escape from explosions and might therefore be still alive */
4186 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4187 element <= EL_PLAYER_IS_EXPLODING_4)
4189 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4190 int explosion_element = EL_PLAYER_1 + player_nr;
4191 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4192 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4194 if (level.use_explosion_element[player_nr])
4195 explosion_element = level.explosion_element[player_nr];
4197 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4198 element_info[explosion_element].content.e[xx][yy]);
4201 /* restore probably existing indestructible background element */
4202 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4203 element = Feld[x][y] = Back[x][y];
4206 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4207 GfxDir[x][y] = MV_NONE;
4208 ChangeDelay[x][y] = 0;
4209 ChangePage[x][y] = -1;
4211 #if USE_NEW_CUSTOM_VALUE
4212 CustomValue[x][y] = 0;
4215 InitField_WithBug2(x, y, FALSE);
4217 DrawLevelField(x, y);
4219 TestIfElementTouchesCustomElement(x, y);
4221 if (GFX_CRUMBLED(element))
4222 DrawLevelFieldCrumbledSandNeighbours(x, y);
4224 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4225 StorePlayer[x][y] = 0;
4227 if (ELEM_IS_PLAYER(element))
4228 RelocatePlayer(x, y, element);
4230 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4232 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4233 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4236 DrawLevelFieldCrumbledSand(x, y);
4238 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4240 DrawLevelElement(x, y, Back[x][y]);
4241 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4243 else if (IS_WALKABLE_UNDER(Back[x][y]))
4245 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4246 DrawLevelElementThruMask(x, y, Back[x][y]);
4248 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4249 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4253 void DynaExplode(int ex, int ey)
4256 int dynabomb_element = Feld[ex][ey];
4257 int dynabomb_size = 1;
4258 boolean dynabomb_xl = FALSE;
4259 struct PlayerInfo *player;
4260 static int xy[4][2] =
4268 if (IS_ACTIVE_BOMB(dynabomb_element))
4270 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4271 dynabomb_size = player->dynabomb_size;
4272 dynabomb_xl = player->dynabomb_xl;
4273 player->dynabombs_left++;
4276 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4278 for (i = 0; i < NUM_DIRECTIONS; i++)
4280 for (j = 1; j <= dynabomb_size; j++)
4282 int x = ex + j * xy[i][0];
4283 int y = ey + j * xy[i][1];
4286 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4289 element = Feld[x][y];
4291 /* do not restart explosions of fields with active bombs */
4292 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4295 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4297 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4298 !IS_DIGGABLE(element) && !dynabomb_xl)
4304 void Bang(int x, int y)
4306 int element = MovingOrBlocked2Element(x, y);
4307 int explosion_type = EX_TYPE_NORMAL;
4309 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4311 struct PlayerInfo *player = PLAYERINFO(x, y);
4313 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4314 player->element_nr);
4316 if (level.use_explosion_element[player->index_nr])
4318 int explosion_element = level.explosion_element[player->index_nr];
4320 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4321 explosion_type = EX_TYPE_CROSS;
4322 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4323 explosion_type = EX_TYPE_CENTER;
4331 case EL_BD_BUTTERFLY:
4334 case EL_DARK_YAMYAM:
4338 RaiseScoreElement(element);
4341 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4342 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4343 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4344 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4345 case EL_DYNABOMB_INCREASE_NUMBER:
4346 case EL_DYNABOMB_INCREASE_SIZE:
4347 case EL_DYNABOMB_INCREASE_POWER:
4348 explosion_type = EX_TYPE_DYNA;
4353 case EL_LAMP_ACTIVE:
4354 case EL_AMOEBA_TO_DIAMOND:
4355 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4356 explosion_type = EX_TYPE_CENTER;
4360 if (element_info[element].explosion_type == EXPLODES_CROSS)
4361 explosion_type = EX_TYPE_CROSS;
4362 else if (element_info[element].explosion_type == EXPLODES_1X1)
4363 explosion_type = EX_TYPE_CENTER;
4367 if (explosion_type == EX_TYPE_DYNA)
4370 Explode(x, y, EX_PHASE_START, explosion_type);
4372 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4375 void SplashAcid(int x, int y)
4377 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4378 (!IN_LEV_FIELD(x - 1, y - 2) ||
4379 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4380 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4382 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4383 (!IN_LEV_FIELD(x + 1, y - 2) ||
4384 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4385 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4387 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4390 static void InitBeltMovement()
4392 static int belt_base_element[4] =
4394 EL_CONVEYOR_BELT_1_LEFT,
4395 EL_CONVEYOR_BELT_2_LEFT,
4396 EL_CONVEYOR_BELT_3_LEFT,
4397 EL_CONVEYOR_BELT_4_LEFT
4399 static int belt_base_active_element[4] =
4401 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4402 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4403 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4404 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4409 /* set frame order for belt animation graphic according to belt direction */
4410 for (i = 0; i < NUM_BELTS; i++)
4414 for (j = 0; j < NUM_BELT_PARTS; j++)
4416 int element = belt_base_active_element[belt_nr] + j;
4417 int graphic = el2img(element);
4419 if (game.belt_dir[i] == MV_LEFT)
4420 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4422 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4427 SCAN_PLAYFIELD(x, y)
4429 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4432 int element = Feld[x][y];
4434 for (i = 0; i < NUM_BELTS; i++)
4436 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4438 int e_belt_nr = getBeltNrFromBeltElement(element);
4441 if (e_belt_nr == belt_nr)
4443 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4445 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4452 static void ToggleBeltSwitch(int x, int y)
4454 static int belt_base_element[4] =
4456 EL_CONVEYOR_BELT_1_LEFT,
4457 EL_CONVEYOR_BELT_2_LEFT,
4458 EL_CONVEYOR_BELT_3_LEFT,
4459 EL_CONVEYOR_BELT_4_LEFT
4461 static int belt_base_active_element[4] =
4463 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4464 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4465 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4466 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4468 static int belt_base_switch_element[4] =
4470 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4471 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4472 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4473 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4475 static int belt_move_dir[4] =
4483 int element = Feld[x][y];
4484 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4485 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4486 int belt_dir = belt_move_dir[belt_dir_nr];
4489 if (!IS_BELT_SWITCH(element))
4492 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4493 game.belt_dir[belt_nr] = belt_dir;
4495 if (belt_dir_nr == 3)
4498 /* set frame order for belt animation graphic according to belt direction */
4499 for (i = 0; i < NUM_BELT_PARTS; i++)
4501 int element = belt_base_active_element[belt_nr] + i;
4502 int graphic = el2img(element);
4504 if (belt_dir == MV_LEFT)
4505 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4507 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4511 SCAN_PLAYFIELD(xx, yy)
4513 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4516 int element = Feld[xx][yy];
4518 if (IS_BELT_SWITCH(element))
4520 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4522 if (e_belt_nr == belt_nr)
4524 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4525 DrawLevelField(xx, yy);
4528 else if (IS_BELT(element) && belt_dir != MV_NONE)
4530 int e_belt_nr = getBeltNrFromBeltElement(element);
4532 if (e_belt_nr == belt_nr)
4534 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4536 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4537 DrawLevelField(xx, yy);
4540 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4542 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4544 if (e_belt_nr == belt_nr)
4546 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4548 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4549 DrawLevelField(xx, yy);
4555 static void ToggleSwitchgateSwitch(int x, int y)
4559 game.switchgate_pos = !game.switchgate_pos;
4562 SCAN_PLAYFIELD(xx, yy)
4564 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4567 int element = Feld[xx][yy];
4569 #if !USE_BOTH_SWITCHGATE_SWITCHES
4570 if (element == EL_SWITCHGATE_SWITCH_UP ||
4571 element == EL_SWITCHGATE_SWITCH_DOWN)
4573 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4574 DrawLevelField(xx, yy);
4577 if (element == EL_SWITCHGATE_SWITCH_UP)
4579 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4580 DrawLevelField(xx, yy);
4582 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4584 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4585 DrawLevelField(xx, yy);
4588 else if (element == EL_SWITCHGATE_OPEN ||
4589 element == EL_SWITCHGATE_OPENING)
4591 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4593 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4595 else if (element == EL_SWITCHGATE_CLOSED ||
4596 element == EL_SWITCHGATE_CLOSING)
4598 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4600 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4605 static int getInvisibleActiveFromInvisibleElement(int element)
4607 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4608 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4609 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4613 static int getInvisibleFromInvisibleActiveElement(int element)
4615 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4616 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4617 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4621 static void RedrawAllLightSwitchesAndInvisibleElements()
4626 SCAN_PLAYFIELD(x, y)
4628 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4631 int element = Feld[x][y];
4633 if (element == EL_LIGHT_SWITCH &&
4634 game.light_time_left > 0)
4636 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4637 DrawLevelField(x, y);
4639 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4640 game.light_time_left == 0)
4642 Feld[x][y] = EL_LIGHT_SWITCH;
4643 DrawLevelField(x, y);
4645 else if (element == EL_EMC_DRIPPER &&
4646 game.light_time_left > 0)
4648 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4649 DrawLevelField(x, y);
4651 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4652 game.light_time_left == 0)
4654 Feld[x][y] = EL_EMC_DRIPPER;
4655 DrawLevelField(x, y);
4657 else if (element == EL_INVISIBLE_STEELWALL ||
4658 element == EL_INVISIBLE_WALL ||
4659 element == EL_INVISIBLE_SAND)
4661 if (game.light_time_left > 0)
4662 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4664 DrawLevelField(x, y);
4666 /* uncrumble neighbour fields, if needed */
4667 if (element == EL_INVISIBLE_SAND)
4668 DrawLevelFieldCrumbledSandNeighbours(x, y);
4670 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4671 element == EL_INVISIBLE_WALL_ACTIVE ||
4672 element == EL_INVISIBLE_SAND_ACTIVE)
4674 if (game.light_time_left == 0)
4675 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4677 DrawLevelField(x, y);
4679 /* re-crumble neighbour fields, if needed */
4680 if (element == EL_INVISIBLE_SAND)
4681 DrawLevelFieldCrumbledSandNeighbours(x, y);
4686 static void RedrawAllInvisibleElementsForLenses()
4691 SCAN_PLAYFIELD(x, y)
4693 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4696 int element = Feld[x][y];
4698 if (element == EL_EMC_DRIPPER &&
4699 game.lenses_time_left > 0)
4701 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4702 DrawLevelField(x, y);
4704 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4705 game.lenses_time_left == 0)
4707 Feld[x][y] = EL_EMC_DRIPPER;
4708 DrawLevelField(x, y);
4710 else if (element == EL_INVISIBLE_STEELWALL ||
4711 element == EL_INVISIBLE_WALL ||
4712 element == EL_INVISIBLE_SAND)
4714 if (game.lenses_time_left > 0)
4715 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4717 DrawLevelField(x, y);
4719 /* uncrumble neighbour fields, if needed */
4720 if (element == EL_INVISIBLE_SAND)
4721 DrawLevelFieldCrumbledSandNeighbours(x, y);
4723 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4724 element == EL_INVISIBLE_WALL_ACTIVE ||
4725 element == EL_INVISIBLE_SAND_ACTIVE)
4727 if (game.lenses_time_left == 0)
4728 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4730 DrawLevelField(x, y);
4732 /* re-crumble neighbour fields, if needed */
4733 if (element == EL_INVISIBLE_SAND)
4734 DrawLevelFieldCrumbledSandNeighbours(x, y);
4739 static void RedrawAllInvisibleElementsForMagnifier()
4744 SCAN_PLAYFIELD(x, y)
4746 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4749 int element = Feld[x][y];
4751 if (element == EL_EMC_FAKE_GRASS &&
4752 game.magnify_time_left > 0)
4754 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4755 DrawLevelField(x, y);
4757 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4758 game.magnify_time_left == 0)
4760 Feld[x][y] = EL_EMC_FAKE_GRASS;
4761 DrawLevelField(x, y);
4763 else if (IS_GATE_GRAY(element) &&
4764 game.magnify_time_left > 0)
4766 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4767 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4768 IS_EM_GATE_GRAY(element) ?
4769 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4770 IS_EMC_GATE_GRAY(element) ?
4771 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4773 DrawLevelField(x, y);
4775 else if (IS_GATE_GRAY_ACTIVE(element) &&
4776 game.magnify_time_left == 0)
4778 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4779 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4780 IS_EM_GATE_GRAY_ACTIVE(element) ?
4781 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4782 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4783 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4785 DrawLevelField(x, y);
4790 static void ToggleLightSwitch(int x, int y)
4792 int element = Feld[x][y];
4794 game.light_time_left =
4795 (element == EL_LIGHT_SWITCH ?
4796 level.time_light * FRAMES_PER_SECOND : 0);
4798 RedrawAllLightSwitchesAndInvisibleElements();
4801 static void ActivateTimegateSwitch(int x, int y)
4805 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4808 SCAN_PLAYFIELD(xx, yy)
4810 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4813 int element = Feld[xx][yy];
4815 if (element == EL_TIMEGATE_CLOSED ||
4816 element == EL_TIMEGATE_CLOSING)
4818 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4819 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4823 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4825 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4826 DrawLevelField(xx, yy);
4832 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4835 void Impact(int x, int y)
4837 boolean last_line = (y == lev_fieldy - 1);
4838 boolean object_hit = FALSE;
4839 boolean impact = (last_line || object_hit);
4840 int element = Feld[x][y];
4841 int smashed = EL_STEELWALL;
4843 if (!last_line) /* check if element below was hit */
4845 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4848 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4849 MovDir[x][y + 1] != MV_DOWN ||
4850 MovPos[x][y + 1] <= TILEY / 2));
4852 /* do not smash moving elements that left the smashed field in time */
4853 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4854 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4857 #if USE_QUICKSAND_IMPACT_BUGFIX
4858 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4860 RemoveMovingField(x, y + 1);
4861 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4862 Feld[x][y + 2] = EL_ROCK;
4863 DrawLevelField(x, y + 2);
4870 smashed = MovingOrBlocked2Element(x, y + 1);
4872 impact = (last_line || object_hit);
4875 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4877 SplashAcid(x, y + 1);
4881 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4882 /* only reset graphic animation if graphic really changes after impact */
4884 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4886 ResetGfxAnimation(x, y);
4887 DrawLevelField(x, y);
4890 if (impact && CAN_EXPLODE_IMPACT(element))
4895 else if (impact && element == EL_PEARL)
4897 ResetGfxAnimation(x, y);
4899 Feld[x][y] = EL_PEARL_BREAKING;
4900 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4903 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4905 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4910 if (impact && element == EL_AMOEBA_DROP)
4912 if (object_hit && IS_PLAYER(x, y + 1))
4913 KillPlayerUnlessEnemyProtected(x, y + 1);
4914 else if (object_hit && smashed == EL_PENGUIN)
4918 Feld[x][y] = EL_AMOEBA_GROWING;
4919 Store[x][y] = EL_AMOEBA_WET;
4921 ResetRandomAnimationValue(x, y);
4926 if (object_hit) /* check which object was hit */
4928 if (CAN_PASS_MAGIC_WALL(element) &&
4929 (smashed == EL_MAGIC_WALL ||
4930 smashed == EL_BD_MAGIC_WALL))
4933 int activated_magic_wall =
4934 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4935 EL_BD_MAGIC_WALL_ACTIVE);
4937 /* activate magic wall / mill */
4939 SCAN_PLAYFIELD(xx, yy)
4941 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4943 if (Feld[xx][yy] == smashed)
4944 Feld[xx][yy] = activated_magic_wall;
4946 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4947 game.magic_wall_active = TRUE;
4949 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4950 SND_MAGIC_WALL_ACTIVATING :
4951 SND_BD_MAGIC_WALL_ACTIVATING));
4954 if (IS_PLAYER(x, y + 1))
4956 if (CAN_SMASH_PLAYER(element))
4958 KillPlayerUnlessEnemyProtected(x, y + 1);
4962 else if (smashed == EL_PENGUIN)
4964 if (CAN_SMASH_PLAYER(element))
4970 else if (element == EL_BD_DIAMOND)
4972 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4978 else if (((element == EL_SP_INFOTRON ||
4979 element == EL_SP_ZONK) &&
4980 (smashed == EL_SP_SNIKSNAK ||
4981 smashed == EL_SP_ELECTRON ||
4982 smashed == EL_SP_DISK_ORANGE)) ||
4983 (element == EL_SP_INFOTRON &&
4984 smashed == EL_SP_DISK_YELLOW))
4989 else if (CAN_SMASH_EVERYTHING(element))
4991 if (IS_CLASSIC_ENEMY(smashed) ||
4992 CAN_EXPLODE_SMASHED(smashed))
4997 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4999 if (smashed == EL_LAMP ||
5000 smashed == EL_LAMP_ACTIVE)
5005 else if (smashed == EL_NUT)
5007 Feld[x][y + 1] = EL_NUT_BREAKING;
5008 PlayLevelSound(x, y, SND_NUT_BREAKING);
5009 RaiseScoreElement(EL_NUT);
5012 else if (smashed == EL_PEARL)
5014 ResetGfxAnimation(x, y);
5016 Feld[x][y + 1] = EL_PEARL_BREAKING;
5017 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5020 else if (smashed == EL_DIAMOND)
5022 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5023 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5026 else if (IS_BELT_SWITCH(smashed))
5028 ToggleBeltSwitch(x, y + 1);
5030 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5031 smashed == EL_SWITCHGATE_SWITCH_DOWN)
5033 ToggleSwitchgateSwitch(x, y + 1);
5035 else if (smashed == EL_LIGHT_SWITCH ||
5036 smashed == EL_LIGHT_SWITCH_ACTIVE)
5038 ToggleLightSwitch(x, y + 1);
5043 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5046 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5048 CheckElementChangeBySide(x, y + 1, smashed, element,
5049 CE_SWITCHED, CH_SIDE_TOP);
5050 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5056 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5061 /* play sound of magic wall / mill */
5063 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5064 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5066 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5067 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5068 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5069 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5074 /* play sound of object that hits the ground */
5075 if (last_line || object_hit)
5076 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5079 inline static void TurnRoundExt(int x, int y)
5091 { 0, 0 }, { 0, 0 }, { 0, 0 },
5096 int left, right, back;
5100 { MV_DOWN, MV_UP, MV_RIGHT },
5101 { MV_UP, MV_DOWN, MV_LEFT },
5103 { MV_LEFT, MV_RIGHT, MV_DOWN },
5107 { MV_RIGHT, MV_LEFT, MV_UP }
5110 int element = Feld[x][y];
5111 int move_pattern = element_info[element].move_pattern;
5113 int old_move_dir = MovDir[x][y];
5114 int left_dir = turn[old_move_dir].left;
5115 int right_dir = turn[old_move_dir].right;
5116 int back_dir = turn[old_move_dir].back;
5118 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5119 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5120 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5121 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5123 int left_x = x + left_dx, left_y = y + left_dy;
5124 int right_x = x + right_dx, right_y = y + right_dy;
5125 int move_x = x + move_dx, move_y = y + move_dy;
5129 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5131 TestIfBadThingTouchesOtherBadThing(x, y);
5133 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5134 MovDir[x][y] = right_dir;
5135 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5136 MovDir[x][y] = left_dir;
5138 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5140 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5143 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5145 TestIfBadThingTouchesOtherBadThing(x, y);
5147 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5148 MovDir[x][y] = left_dir;
5149 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5150 MovDir[x][y] = right_dir;
5152 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5154 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5157 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5159 TestIfBadThingTouchesOtherBadThing(x, y);
5161 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5162 MovDir[x][y] = left_dir;
5163 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5164 MovDir[x][y] = right_dir;
5166 if (MovDir[x][y] != old_move_dir)
5169 else if (element == EL_YAMYAM)
5171 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5172 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5174 if (can_turn_left && can_turn_right)
5175 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5176 else if (can_turn_left)
5177 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5178 else if (can_turn_right)
5179 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5181 MovDir[x][y] = back_dir;
5183 MovDelay[x][y] = 16 + 16 * RND(3);
5185 else if (element == EL_DARK_YAMYAM)
5187 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5189 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5192 if (can_turn_left && can_turn_right)
5193 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5194 else if (can_turn_left)
5195 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5196 else if (can_turn_right)
5197 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5199 MovDir[x][y] = back_dir;
5201 MovDelay[x][y] = 16 + 16 * RND(3);
5203 else if (element == EL_PACMAN)
5205 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5206 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5208 if (can_turn_left && can_turn_right)
5209 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5210 else if (can_turn_left)
5211 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5212 else if (can_turn_right)
5213 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5215 MovDir[x][y] = back_dir;
5217 MovDelay[x][y] = 6 + RND(40);
5219 else if (element == EL_PIG)
5221 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5222 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5223 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5224 boolean should_turn_left, should_turn_right, should_move_on;
5226 int rnd = RND(rnd_value);
5228 should_turn_left = (can_turn_left &&
5230 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5231 y + back_dy + left_dy)));
5232 should_turn_right = (can_turn_right &&
5234 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5235 y + back_dy + right_dy)));
5236 should_move_on = (can_move_on &&
5239 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5240 y + move_dy + left_dy) ||
5241 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5242 y + move_dy + right_dy)));
5244 if (should_turn_left || should_turn_right || should_move_on)
5246 if (should_turn_left && should_turn_right && should_move_on)
5247 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5248 rnd < 2 * rnd_value / 3 ? right_dir :
5250 else if (should_turn_left && should_turn_right)
5251 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5252 else if (should_turn_left && should_move_on)
5253 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5254 else if (should_turn_right && should_move_on)
5255 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5256 else if (should_turn_left)
5257 MovDir[x][y] = left_dir;
5258 else if (should_turn_right)
5259 MovDir[x][y] = right_dir;
5260 else if (should_move_on)
5261 MovDir[x][y] = old_move_dir;
5263 else if (can_move_on && rnd > rnd_value / 8)
5264 MovDir[x][y] = old_move_dir;
5265 else if (can_turn_left && can_turn_right)
5266 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5267 else if (can_turn_left && rnd > rnd_value / 8)
5268 MovDir[x][y] = left_dir;
5269 else if (can_turn_right && rnd > rnd_value/8)
5270 MovDir[x][y] = right_dir;
5272 MovDir[x][y] = back_dir;
5274 xx = x + move_xy[MovDir[x][y]].dx;
5275 yy = y + move_xy[MovDir[x][y]].dy;
5277 if (!IN_LEV_FIELD(xx, yy) ||
5278 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5279 MovDir[x][y] = old_move_dir;
5283 else if (element == EL_DRAGON)
5285 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5286 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5287 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5289 int rnd = RND(rnd_value);
5291 if (can_move_on && rnd > rnd_value / 8)
5292 MovDir[x][y] = old_move_dir;
5293 else if (can_turn_left && can_turn_right)
5294 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5295 else if (can_turn_left && rnd > rnd_value / 8)
5296 MovDir[x][y] = left_dir;
5297 else if (can_turn_right && rnd > rnd_value / 8)
5298 MovDir[x][y] = right_dir;
5300 MovDir[x][y] = back_dir;
5302 xx = x + move_xy[MovDir[x][y]].dx;
5303 yy = y + move_xy[MovDir[x][y]].dy;
5305 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5306 MovDir[x][y] = old_move_dir;
5310 else if (element == EL_MOLE)
5312 boolean can_move_on =
5313 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5314 IS_AMOEBOID(Feld[move_x][move_y]) ||
5315 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5318 boolean can_turn_left =
5319 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5320 IS_AMOEBOID(Feld[left_x][left_y])));
5322 boolean can_turn_right =
5323 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5324 IS_AMOEBOID(Feld[right_x][right_y])));
5326 if (can_turn_left && can_turn_right)
5327 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5328 else if (can_turn_left)
5329 MovDir[x][y] = left_dir;
5331 MovDir[x][y] = right_dir;
5334 if (MovDir[x][y] != old_move_dir)
5337 else if (element == EL_BALLOON)
5339 MovDir[x][y] = game.wind_direction;
5342 else if (element == EL_SPRING)
5344 #if USE_NEW_SPRING_BUMPER
5345 if (MovDir[x][y] & MV_HORIZONTAL)
5347 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5348 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5350 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5351 ResetGfxAnimation(move_x, move_y);
5352 DrawLevelField(move_x, move_y);
5354 MovDir[x][y] = back_dir;
5356 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5357 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5358 MovDir[x][y] = MV_NONE;
5361 if (MovDir[x][y] & MV_HORIZONTAL &&
5362 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5363 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5364 MovDir[x][y] = MV_NONE;
5369 else if (element == EL_ROBOT ||
5370 element == EL_SATELLITE ||
5371 element == EL_PENGUIN ||
5372 element == EL_EMC_ANDROID)
5374 int attr_x = -1, attr_y = -1;
5385 for (i = 0; i < MAX_PLAYERS; i++)
5387 struct PlayerInfo *player = &stored_player[i];
5388 int jx = player->jx, jy = player->jy;
5390 if (!player->active)
5394 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5402 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5403 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5404 game.engine_version < VERSION_IDENT(3,1,0,0)))
5410 if (element == EL_PENGUIN)
5413 static int xy[4][2] =
5421 for (i = 0; i < NUM_DIRECTIONS; i++)
5423 int ex = x + xy[i][0];
5424 int ey = y + xy[i][1];
5426 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5435 MovDir[x][y] = MV_NONE;
5437 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5438 else if (attr_x > x)
5439 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5441 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5442 else if (attr_y > y)
5443 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5445 if (element == EL_ROBOT)
5449 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5450 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5451 Moving2Blocked(x, y, &newx, &newy);
5453 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5454 MovDelay[x][y] = 8 + 8 * !RND(3);
5456 MovDelay[x][y] = 16;
5458 else if (element == EL_PENGUIN)
5464 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5466 boolean first_horiz = RND(2);
5467 int new_move_dir = MovDir[x][y];
5470 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5471 Moving2Blocked(x, y, &newx, &newy);
5473 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5477 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5478 Moving2Blocked(x, y, &newx, &newy);
5480 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5483 MovDir[x][y] = old_move_dir;
5487 else if (element == EL_SATELLITE)
5493 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5495 boolean first_horiz = RND(2);
5496 int new_move_dir = MovDir[x][y];
5499 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5500 Moving2Blocked(x, y, &newx, &newy);
5502 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5506 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5507 Moving2Blocked(x, y, &newx, &newy);
5509 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5512 MovDir[x][y] = old_move_dir;
5516 else if (element == EL_EMC_ANDROID)
5518 static int check_pos[16] =
5520 -1, /* 0 => (invalid) */
5521 7, /* 1 => MV_LEFT */
5522 3, /* 2 => MV_RIGHT */
5523 -1, /* 3 => (invalid) */
5525 0, /* 5 => MV_LEFT | MV_UP */
5526 2, /* 6 => MV_RIGHT | MV_UP */
5527 -1, /* 7 => (invalid) */
5528 5, /* 8 => MV_DOWN */
5529 6, /* 9 => MV_LEFT | MV_DOWN */
5530 4, /* 10 => MV_RIGHT | MV_DOWN */
5531 -1, /* 11 => (invalid) */
5532 -1, /* 12 => (invalid) */
5533 -1, /* 13 => (invalid) */
5534 -1, /* 14 => (invalid) */
5535 -1, /* 15 => (invalid) */
5543 { -1, -1, MV_LEFT | MV_UP },
5545 { +1, -1, MV_RIGHT | MV_UP },
5546 { +1, 0, MV_RIGHT },
5547 { +1, +1, MV_RIGHT | MV_DOWN },
5549 { -1, +1, MV_LEFT | MV_DOWN },
5552 int start_pos, check_order;
5553 boolean can_clone = FALSE;
5556 /* check if there is any free field around current position */
5557 for (i = 0; i < 8; i++)
5559 int newx = x + check_xy[i].dx;
5560 int newy = y + check_xy[i].dy;
5562 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5570 if (can_clone) /* randomly find an element to clone */
5574 start_pos = check_pos[RND(8)];
5575 check_order = (RND(2) ? -1 : +1);
5577 for (i = 0; i < 8; i++)
5579 int pos_raw = start_pos + i * check_order;
5580 int pos = (pos_raw + 8) % 8;
5581 int newx = x + check_xy[pos].dx;
5582 int newy = y + check_xy[pos].dy;
5584 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5586 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5587 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5589 Store[x][y] = Feld[newx][newy];
5598 if (can_clone) /* randomly find a direction to move */
5602 start_pos = check_pos[RND(8)];
5603 check_order = (RND(2) ? -1 : +1);
5605 for (i = 0; i < 8; i++)
5607 int pos_raw = start_pos + i * check_order;
5608 int pos = (pos_raw + 8) % 8;
5609 int newx = x + check_xy[pos].dx;
5610 int newy = y + check_xy[pos].dy;
5611 int new_move_dir = check_xy[pos].dir;
5613 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5615 MovDir[x][y] = new_move_dir;
5616 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5625 if (can_clone) /* cloning and moving successful */
5628 /* cannot clone -- try to move towards player */
5630 start_pos = check_pos[MovDir[x][y] & 0x0f];
5631 check_order = (RND(2) ? -1 : +1);
5633 for (i = 0; i < 3; i++)
5635 /* first check start_pos, then previous/next or (next/previous) pos */
5636 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5637 int pos = (pos_raw + 8) % 8;
5638 int newx = x + check_xy[pos].dx;
5639 int newy = y + check_xy[pos].dy;
5640 int new_move_dir = check_xy[pos].dir;
5642 if (IS_PLAYER(newx, newy))
5645 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5647 MovDir[x][y] = new_move_dir;
5648 MovDelay[x][y] = level.android_move_time * 8 + 1;
5655 else if (move_pattern == MV_TURNING_LEFT ||
5656 move_pattern == MV_TURNING_RIGHT ||
5657 move_pattern == MV_TURNING_LEFT_RIGHT ||
5658 move_pattern == MV_TURNING_RIGHT_LEFT ||
5659 move_pattern == MV_TURNING_RANDOM ||
5660 move_pattern == MV_ALL_DIRECTIONS)
5662 boolean can_turn_left =
5663 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5664 boolean can_turn_right =
5665 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5667 if (element_info[element].move_stepsize == 0) /* "not moving" */
5670 if (move_pattern == MV_TURNING_LEFT)
5671 MovDir[x][y] = left_dir;
5672 else if (move_pattern == MV_TURNING_RIGHT)
5673 MovDir[x][y] = right_dir;
5674 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5675 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5676 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5677 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5678 else if (move_pattern == MV_TURNING_RANDOM)
5679 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5680 can_turn_right && !can_turn_left ? right_dir :
5681 RND(2) ? left_dir : right_dir);
5682 else if (can_turn_left && can_turn_right)
5683 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5684 else if (can_turn_left)
5685 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5686 else if (can_turn_right)
5687 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5689 MovDir[x][y] = back_dir;
5691 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5693 else if (move_pattern == MV_HORIZONTAL ||
5694 move_pattern == MV_VERTICAL)
5696 if (move_pattern & old_move_dir)
5697 MovDir[x][y] = back_dir;
5698 else if (move_pattern == MV_HORIZONTAL)
5699 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5700 else if (move_pattern == MV_VERTICAL)
5701 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5703 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5705 else if (move_pattern & MV_ANY_DIRECTION)
5707 MovDir[x][y] = move_pattern;
5708 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5710 else if (move_pattern & MV_WIND_DIRECTION)
5712 MovDir[x][y] = game.wind_direction;
5713 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5715 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5717 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5718 MovDir[x][y] = left_dir;
5719 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5720 MovDir[x][y] = right_dir;
5722 if (MovDir[x][y] != old_move_dir)
5723 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5725 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5727 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5728 MovDir[x][y] = right_dir;
5729 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5730 MovDir[x][y] = left_dir;
5732 if (MovDir[x][y] != old_move_dir)
5733 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5735 else if (move_pattern == MV_TOWARDS_PLAYER ||
5736 move_pattern == MV_AWAY_FROM_PLAYER)
5738 int attr_x = -1, attr_y = -1;
5740 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5751 for (i = 0; i < MAX_PLAYERS; i++)
5753 struct PlayerInfo *player = &stored_player[i];
5754 int jx = player->jx, jy = player->jy;
5756 if (!player->active)
5760 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5768 MovDir[x][y] = MV_NONE;
5770 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5771 else if (attr_x > x)
5772 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5774 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5775 else if (attr_y > y)
5776 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5778 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5780 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5782 boolean first_horiz = RND(2);
5783 int new_move_dir = MovDir[x][y];
5785 if (element_info[element].move_stepsize == 0) /* "not moving" */
5787 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5788 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5794 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5795 Moving2Blocked(x, y, &newx, &newy);
5797 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5801 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5802 Moving2Blocked(x, y, &newx, &newy);
5804 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5807 MovDir[x][y] = old_move_dir;
5810 else if (move_pattern == MV_WHEN_PUSHED ||
5811 move_pattern == MV_WHEN_DROPPED)
5813 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5814 MovDir[x][y] = MV_NONE;
5818 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5820 static int test_xy[7][2] =
5830 static int test_dir[7] =
5840 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5841 int move_preference = -1000000; /* start with very low preference */
5842 int new_move_dir = MV_NONE;
5843 int start_test = RND(4);
5846 for (i = 0; i < NUM_DIRECTIONS; i++)
5848 int move_dir = test_dir[start_test + i];
5849 int move_dir_preference;
5851 xx = x + test_xy[start_test + i][0];
5852 yy = y + test_xy[start_test + i][1];
5854 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5855 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5857 new_move_dir = move_dir;
5862 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5865 move_dir_preference = -1 * RunnerVisit[xx][yy];
5866 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5867 move_dir_preference = PlayerVisit[xx][yy];
5869 if (move_dir_preference > move_preference)
5871 /* prefer field that has not been visited for the longest time */
5872 move_preference = move_dir_preference;
5873 new_move_dir = move_dir;
5875 else if (move_dir_preference == move_preference &&
5876 move_dir == old_move_dir)
5878 /* prefer last direction when all directions are preferred equally */
5879 move_preference = move_dir_preference;
5880 new_move_dir = move_dir;
5884 MovDir[x][y] = new_move_dir;
5885 if (old_move_dir != new_move_dir)
5886 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5890 static void TurnRound(int x, int y)
5892 int direction = MovDir[x][y];
5894 int element, graphic;
5899 GfxDir[x][y] = MovDir[x][y];
5901 if (direction != MovDir[x][y])
5905 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5908 ResetGfxFrame(x, y, FALSE);
5910 element = Feld[x][y];
5911 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5913 if (graphic_info[graphic].anim_global_sync)
5914 GfxFrame[x][y] = FrameCounter;
5915 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5916 GfxFrame[x][y] = CustomValue[x][y];
5917 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5918 GfxFrame[x][y] = element_info[element].collect_score;
5919 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5920 GfxFrame[x][y] = ChangeDelay[x][y];
5924 static boolean JustBeingPushed(int x, int y)
5928 for (i = 0; i < MAX_PLAYERS; i++)
5930 struct PlayerInfo *player = &stored_player[i];
5932 if (player->active && player->is_pushing && player->MovPos)
5934 int next_jx = player->jx + (player->jx - player->last_jx);
5935 int next_jy = player->jy + (player->jy - player->last_jy);
5937 if (x == next_jx && y == next_jy)
5945 void StartMoving(int x, int y)
5947 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5948 int element = Feld[x][y];
5953 if (MovDelay[x][y] == 0)
5954 GfxAction[x][y] = ACTION_DEFAULT;
5956 if (CAN_FALL(element) && y < lev_fieldy - 1)
5958 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5959 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5960 if (JustBeingPushed(x, y))
5963 if (element == EL_QUICKSAND_FULL)
5965 if (IS_FREE(x, y + 1))
5967 InitMovingField(x, y, MV_DOWN);
5968 started_moving = TRUE;
5970 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5971 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5972 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5973 Store[x][y] = EL_ROCK;
5975 Store[x][y] = EL_ROCK;
5978 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5980 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5982 if (!MovDelay[x][y])
5983 MovDelay[x][y] = TILEY + 1;
5992 Feld[x][y] = EL_QUICKSAND_EMPTY;
5993 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5994 Store[x][y + 1] = Store[x][y];
5997 PlayLevelSoundAction(x, y, ACTION_FILLING);
6000 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6001 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6003 InitMovingField(x, y, MV_DOWN);
6004 started_moving = TRUE;
6006 Feld[x][y] = EL_QUICKSAND_FILLING;
6007 Store[x][y] = element;
6009 PlayLevelSoundAction(x, y, ACTION_FILLING);
6011 else if (element == EL_MAGIC_WALL_FULL)
6013 if (IS_FREE(x, y + 1))
6015 InitMovingField(x, y, MV_DOWN);
6016 started_moving = TRUE;
6018 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6019 Store[x][y] = EL_CHANGED(Store[x][y]);
6021 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6023 if (!MovDelay[x][y])
6024 MovDelay[x][y] = TILEY/4 + 1;
6033 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6034 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6035 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6039 else if (element == EL_BD_MAGIC_WALL_FULL)
6041 if (IS_FREE(x, y + 1))
6043 InitMovingField(x, y, MV_DOWN);
6044 started_moving = TRUE;
6046 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6047 Store[x][y] = EL_CHANGED2(Store[x][y]);
6049 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6051 if (!MovDelay[x][y])
6052 MovDelay[x][y] = TILEY/4 + 1;
6061 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6062 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6063 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
6067 else if (CAN_PASS_MAGIC_WALL(element) &&
6068 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6069 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
6071 InitMovingField(x, y, MV_DOWN);
6072 started_moving = TRUE;
6075 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6076 EL_BD_MAGIC_WALL_FILLING);
6077 Store[x][y] = element;
6079 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6081 SplashAcid(x, y + 1);
6083 InitMovingField(x, y, MV_DOWN);
6084 started_moving = TRUE;
6086 Store[x][y] = EL_ACID;
6088 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6089 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6091 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6092 CAN_FALL(element) && WasJustFalling[x][y] &&
6093 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6095 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6096 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6097 (Feld[x][y + 1] == EL_BLOCKED)))
6099 /* this is needed for a special case not covered by calling "Impact()"
6100 from "ContinueMoving()": if an element moves to a tile directly below
6101 another element which was just falling on that tile (which was empty
6102 in the previous frame), the falling element above would just stop
6103 instead of smashing the element below (in previous version, the above
6104 element was just checked for "moving" instead of "falling", resulting
6105 in incorrect smashes caused by horizontal movement of the above
6106 element; also, the case of the player being the element to smash was
6107 simply not covered here... :-/ ) */
6109 CheckCollision[x][y] = 0;
6113 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6115 if (MovDir[x][y] == MV_NONE)
6117 InitMovingField(x, y, MV_DOWN);
6118 started_moving = TRUE;
6121 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6123 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6124 MovDir[x][y] = MV_DOWN;
6126 InitMovingField(x, y, MV_DOWN);
6127 started_moving = TRUE;
6129 else if (element == EL_AMOEBA_DROP)
6131 Feld[x][y] = EL_AMOEBA_GROWING;
6132 Store[x][y] = EL_AMOEBA_WET;
6134 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6135 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6136 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6137 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6139 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6140 (IS_FREE(x - 1, y + 1) ||
6141 Feld[x - 1][y + 1] == EL_ACID));
6142 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6143 (IS_FREE(x + 1, y + 1) ||
6144 Feld[x + 1][y + 1] == EL_ACID));
6145 boolean can_fall_any = (can_fall_left || can_fall_right);
6146 boolean can_fall_both = (can_fall_left && can_fall_right);
6147 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6149 #if USE_NEW_ALL_SLIPPERY
6150 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6152 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6153 can_fall_right = FALSE;
6154 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6155 can_fall_left = FALSE;
6156 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6157 can_fall_right = FALSE;
6158 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6159 can_fall_left = FALSE;
6161 can_fall_any = (can_fall_left || can_fall_right);
6162 can_fall_both = FALSE;
6165 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6167 if (slippery_type == SLIPPERY_ONLY_LEFT)
6168 can_fall_right = FALSE;
6169 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6170 can_fall_left = FALSE;
6171 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6172 can_fall_right = FALSE;
6173 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6174 can_fall_left = FALSE;
6176 can_fall_any = (can_fall_left || can_fall_right);
6177 can_fall_both = (can_fall_left && can_fall_right);
6181 #if USE_NEW_ALL_SLIPPERY
6183 #if USE_NEW_SP_SLIPPERY
6184 /* !!! better use the same properties as for custom elements here !!! */
6185 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6186 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6188 can_fall_right = FALSE; /* slip down on left side */
6189 can_fall_both = FALSE;
6194 #if USE_NEW_ALL_SLIPPERY
6197 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6198 can_fall_right = FALSE; /* slip down on left side */
6200 can_fall_left = !(can_fall_right = RND(2));
6202 can_fall_both = FALSE;
6207 if (game.emulation == EMU_BOULDERDASH ||
6208 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6209 can_fall_right = FALSE; /* slip down on left side */
6211 can_fall_left = !(can_fall_right = RND(2));
6213 can_fall_both = FALSE;
6219 /* if not determined otherwise, prefer left side for slipping down */
6220 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6221 started_moving = TRUE;
6225 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6227 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6230 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6231 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6232 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6233 int belt_dir = game.belt_dir[belt_nr];
6235 if ((belt_dir == MV_LEFT && left_is_free) ||
6236 (belt_dir == MV_RIGHT && right_is_free))
6238 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6240 InitMovingField(x, y, belt_dir);
6241 started_moving = TRUE;
6243 Pushed[x][y] = TRUE;
6244 Pushed[nextx][y] = TRUE;
6246 GfxAction[x][y] = ACTION_DEFAULT;
6250 MovDir[x][y] = 0; /* if element was moving, stop it */
6255 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6257 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6259 if (CAN_MOVE(element) && !started_moving)
6262 int move_pattern = element_info[element].move_pattern;
6267 if (MovDir[x][y] == MV_NONE)
6269 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6270 x, y, element, element_info[element].token_name);
6271 printf("StartMoving(): This should never happen!\n");
6276 Moving2Blocked(x, y, &newx, &newy);
6278 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6281 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6282 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6284 WasJustMoving[x][y] = 0;
6285 CheckCollision[x][y] = 0;
6287 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6289 if (Feld[x][y] != element) /* element has changed */
6293 if (!MovDelay[x][y]) /* start new movement phase */
6295 /* all objects that can change their move direction after each step
6296 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6298 if (element != EL_YAMYAM &&
6299 element != EL_DARK_YAMYAM &&
6300 element != EL_PACMAN &&
6301 !(move_pattern & MV_ANY_DIRECTION) &&
6302 move_pattern != MV_TURNING_LEFT &&
6303 move_pattern != MV_TURNING_RIGHT &&
6304 move_pattern != MV_TURNING_LEFT_RIGHT &&
6305 move_pattern != MV_TURNING_RIGHT_LEFT &&
6306 move_pattern != MV_TURNING_RANDOM)
6310 if (MovDelay[x][y] && (element == EL_BUG ||
6311 element == EL_SPACESHIP ||
6312 element == EL_SP_SNIKSNAK ||
6313 element == EL_SP_ELECTRON ||
6314 element == EL_MOLE))
6315 DrawLevelField(x, y);
6319 if (MovDelay[x][y]) /* wait some time before next movement */
6323 if (element == EL_ROBOT ||
6324 element == EL_YAMYAM ||
6325 element == EL_DARK_YAMYAM)
6327 DrawLevelElementAnimationIfNeeded(x, y, element);
6328 PlayLevelSoundAction(x, y, ACTION_WAITING);
6330 else if (element == EL_SP_ELECTRON)
6331 DrawLevelElementAnimationIfNeeded(x, y, element);
6332 else if (element == EL_DRAGON)
6335 int dir = MovDir[x][y];
6336 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6337 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6338 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6339 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6340 dir == MV_UP ? IMG_FLAMES_1_UP :
6341 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6342 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6344 GfxAction[x][y] = ACTION_ATTACKING;
6346 if (IS_PLAYER(x, y))
6347 DrawPlayerField(x, y);
6349 DrawLevelField(x, y);
6351 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6353 for (i = 1; i <= 3; i++)
6355 int xx = x + i * dx;
6356 int yy = y + i * dy;
6357 int sx = SCREENX(xx);
6358 int sy = SCREENY(yy);
6359 int flame_graphic = graphic + (i - 1);
6361 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6366 int flamed = MovingOrBlocked2Element(xx, yy);
6370 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6372 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6373 RemoveMovingField(xx, yy);
6375 RemoveField(xx, yy);
6377 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6380 RemoveMovingField(xx, yy);
6383 ChangeDelay[xx][yy] = 0;
6385 Feld[xx][yy] = EL_FLAMES;
6387 if (IN_SCR_FIELD(sx, sy))
6389 DrawLevelFieldCrumbledSand(xx, yy);
6390 DrawGraphic(sx, sy, flame_graphic, frame);
6395 if (Feld[xx][yy] == EL_FLAMES)
6396 Feld[xx][yy] = EL_EMPTY;
6397 DrawLevelField(xx, yy);
6402 if (MovDelay[x][y]) /* element still has to wait some time */
6404 PlayLevelSoundAction(x, y, ACTION_WAITING);
6410 /* now make next step */
6412 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6414 if (DONT_COLLIDE_WITH(element) &&
6415 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6416 !PLAYER_ENEMY_PROTECTED(newx, newy))
6418 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6423 else if (CAN_MOVE_INTO_ACID(element) &&
6424 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6425 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6426 (MovDir[x][y] == MV_DOWN ||
6427 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6429 SplashAcid(newx, newy);
6430 Store[x][y] = EL_ACID;
6432 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6434 if (Feld[newx][newy] == EL_EXIT_OPEN)
6437 DrawLevelField(x, y);
6439 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6440 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6441 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6443 local_player->friends_still_needed--;
6444 if (!local_player->friends_still_needed &&
6445 !local_player->GameOver && AllPlayersGone)
6446 local_player->LevelSolved = local_player->GameOver = TRUE;
6450 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6452 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6453 DrawLevelField(newx, newy);
6455 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6457 else if (!IS_FREE(newx, newy))
6459 GfxAction[x][y] = ACTION_WAITING;
6461 if (IS_PLAYER(x, y))
6462 DrawPlayerField(x, y);
6464 DrawLevelField(x, y);
6469 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6471 if (IS_FOOD_PIG(Feld[newx][newy]))
6473 if (IS_MOVING(newx, newy))
6474 RemoveMovingField(newx, newy);
6477 Feld[newx][newy] = EL_EMPTY;
6478 DrawLevelField(newx, newy);
6481 PlayLevelSound(x, y, SND_PIG_DIGGING);
6483 else if (!IS_FREE(newx, newy))
6485 if (IS_PLAYER(x, y))
6486 DrawPlayerField(x, y);
6488 DrawLevelField(x, y);
6493 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6495 if (Store[x][y] != EL_EMPTY)
6497 boolean can_clone = FALSE;
6500 /* check if element to clone is still there */
6501 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6503 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6511 /* cannot clone or target field not free anymore -- do not clone */
6512 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6513 Store[x][y] = EL_EMPTY;
6516 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6518 if (IS_MV_DIAGONAL(MovDir[x][y]))
6520 int diagonal_move_dir = MovDir[x][y];
6521 int stored = Store[x][y];
6522 int change_delay = 8;
6525 /* android is moving diagonally */
6527 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6529 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6530 GfxElement[x][y] = EL_EMC_ANDROID;
6531 GfxAction[x][y] = ACTION_SHRINKING;
6532 GfxDir[x][y] = diagonal_move_dir;
6533 ChangeDelay[x][y] = change_delay;
6535 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6538 DrawLevelGraphicAnimation(x, y, graphic);
6539 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6541 if (Feld[newx][newy] == EL_ACID)
6543 SplashAcid(newx, newy);
6548 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6550 Store[newx][newy] = EL_EMC_ANDROID;
6551 GfxElement[newx][newy] = EL_EMC_ANDROID;
6552 GfxAction[newx][newy] = ACTION_GROWING;
6553 GfxDir[newx][newy] = diagonal_move_dir;
6554 ChangeDelay[newx][newy] = change_delay;
6556 graphic = el_act_dir2img(GfxElement[newx][newy],
6557 GfxAction[newx][newy], GfxDir[newx][newy]);
6559 DrawLevelGraphicAnimation(newx, newy, graphic);
6560 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6566 Feld[newx][newy] = EL_EMPTY;
6567 DrawLevelField(newx, newy);
6569 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6572 else if (!IS_FREE(newx, newy))
6575 if (IS_PLAYER(x, y))
6576 DrawPlayerField(x, y);
6578 DrawLevelField(x, y);
6584 else if (IS_CUSTOM_ELEMENT(element) &&
6585 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6587 int new_element = Feld[newx][newy];
6589 if (!IS_FREE(newx, newy))
6591 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6592 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6595 /* no element can dig solid indestructible elements */
6596 if (IS_INDESTRUCTIBLE(new_element) &&
6597 !IS_DIGGABLE(new_element) &&
6598 !IS_COLLECTIBLE(new_element))
6601 if (AmoebaNr[newx][newy] &&
6602 (new_element == EL_AMOEBA_FULL ||
6603 new_element == EL_BD_AMOEBA ||
6604 new_element == EL_AMOEBA_GROWING))
6606 AmoebaCnt[AmoebaNr[newx][newy]]--;
6607 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6610 if (IS_MOVING(newx, newy))
6611 RemoveMovingField(newx, newy);
6614 RemoveField(newx, newy);
6615 DrawLevelField(newx, newy);
6618 /* if digged element was about to explode, prevent the explosion */
6619 ExplodeField[newx][newy] = EX_TYPE_NONE;
6621 PlayLevelSoundAction(x, y, action);
6624 Store[newx][newy] = EL_EMPTY;
6626 /* this makes it possible to leave the removed element again */
6627 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6628 Store[newx][newy] = new_element;
6630 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6632 int move_leave_element = element_info[element].move_leave_element;
6634 /* this makes it possible to leave the removed element again */
6635 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6636 new_element : move_leave_element);
6640 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6642 RunnerVisit[x][y] = FrameCounter;
6643 PlayerVisit[x][y] /= 8; /* expire player visit path */
6646 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6648 if (!IS_FREE(newx, newy))
6650 if (IS_PLAYER(x, y))
6651 DrawPlayerField(x, y);
6653 DrawLevelField(x, y);
6659 boolean wanna_flame = !RND(10);
6660 int dx = newx - x, dy = newy - y;
6661 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6662 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6663 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6664 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6665 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6666 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6669 IS_CLASSIC_ENEMY(element1) ||
6670 IS_CLASSIC_ENEMY(element2)) &&
6671 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6672 element1 != EL_FLAMES && element2 != EL_FLAMES)
6674 ResetGfxAnimation(x, y);
6675 GfxAction[x][y] = ACTION_ATTACKING;
6677 if (IS_PLAYER(x, y))
6678 DrawPlayerField(x, y);
6680 DrawLevelField(x, y);
6682 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6684 MovDelay[x][y] = 50;
6688 RemoveField(newx, newy);
6690 Feld[newx][newy] = EL_FLAMES;
6691 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6694 RemoveField(newx1, newy1);
6696 Feld[newx1][newy1] = EL_FLAMES;
6698 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6701 RemoveField(newx2, newy2);
6703 Feld[newx2][newy2] = EL_FLAMES;
6710 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6711 Feld[newx][newy] == EL_DIAMOND)
6713 if (IS_MOVING(newx, newy))
6714 RemoveMovingField(newx, newy);
6717 Feld[newx][newy] = EL_EMPTY;
6718 DrawLevelField(newx, newy);
6721 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6723 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6724 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6726 if (AmoebaNr[newx][newy])
6728 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6729 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6730 Feld[newx][newy] == EL_BD_AMOEBA)
6731 AmoebaCnt[AmoebaNr[newx][newy]]--;
6736 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6738 RemoveMovingField(newx, newy);
6741 if (IS_MOVING(newx, newy))
6743 RemoveMovingField(newx, newy);
6748 Feld[newx][newy] = EL_EMPTY;
6749 DrawLevelField(newx, newy);
6752 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6754 else if ((element == EL_PACMAN || element == EL_MOLE)
6755 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6757 if (AmoebaNr[newx][newy])
6759 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6760 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6761 Feld[newx][newy] == EL_BD_AMOEBA)
6762 AmoebaCnt[AmoebaNr[newx][newy]]--;
6765 if (element == EL_MOLE)
6767 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6768 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6770 ResetGfxAnimation(x, y);
6771 GfxAction[x][y] = ACTION_DIGGING;
6772 DrawLevelField(x, y);
6774 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6776 return; /* wait for shrinking amoeba */
6778 else /* element == EL_PACMAN */
6780 Feld[newx][newy] = EL_EMPTY;
6781 DrawLevelField(newx, newy);
6782 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6785 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6786 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6787 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6789 /* wait for shrinking amoeba to completely disappear */
6792 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6794 /* object was running against a wall */
6799 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6800 if (move_pattern & MV_ANY_DIRECTION &&
6801 move_pattern == MovDir[x][y])
6803 int blocking_element =
6804 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6806 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6809 element = Feld[x][y]; /* element might have changed */
6813 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6814 DrawLevelElementAnimation(x, y, element);
6816 if (DONT_TOUCH(element))
6817 TestIfBadThingTouchesPlayer(x, y);
6822 InitMovingField(x, y, MovDir[x][y]);
6824 PlayLevelSoundAction(x, y, ACTION_MOVING);
6828 ContinueMoving(x, y);
6831 void ContinueMoving(int x, int y)
6833 int element = Feld[x][y];
6834 struct ElementInfo *ei = &element_info[element];
6835 int direction = MovDir[x][y];
6836 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6837 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6838 int newx = x + dx, newy = y + dy;
6839 int stored = Store[x][y];
6840 int stored_new = Store[newx][newy];
6841 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6842 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6843 boolean last_line = (newy == lev_fieldy - 1);
6845 MovPos[x][y] += getElementMoveStepsize(x, y);
6847 if (pushed_by_player) /* special case: moving object pushed by player */
6848 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6850 if (ABS(MovPos[x][y]) < TILEX)
6852 DrawLevelField(x, y);
6854 return; /* element is still moving */
6857 /* element reached destination field */
6859 Feld[x][y] = EL_EMPTY;
6860 Feld[newx][newy] = element;
6861 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6863 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6865 element = Feld[newx][newy] = EL_ACID;
6867 else if (element == EL_MOLE)
6869 Feld[x][y] = EL_SAND;
6871 DrawLevelFieldCrumbledSandNeighbours(x, y);
6873 else if (element == EL_QUICKSAND_FILLING)
6875 element = Feld[newx][newy] = get_next_element(element);
6876 Store[newx][newy] = Store[x][y];
6878 else if (element == EL_QUICKSAND_EMPTYING)
6880 Feld[x][y] = get_next_element(element);
6881 element = Feld[newx][newy] = Store[x][y];
6883 else if (element == EL_MAGIC_WALL_FILLING)
6885 element = Feld[newx][newy] = get_next_element(element);
6886 if (!game.magic_wall_active)
6887 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6888 Store[newx][newy] = Store[x][y];
6890 else if (element == EL_MAGIC_WALL_EMPTYING)
6892 Feld[x][y] = get_next_element(element);
6893 if (!game.magic_wall_active)
6894 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6895 element = Feld[newx][newy] = Store[x][y];
6897 #if USE_NEW_CUSTOM_VALUE
6898 InitField(newx, newy, FALSE);
6901 else if (element == EL_BD_MAGIC_WALL_FILLING)
6903 element = Feld[newx][newy] = get_next_element(element);
6904 if (!game.magic_wall_active)
6905 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6906 Store[newx][newy] = Store[x][y];
6908 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6910 Feld[x][y] = get_next_element(element);
6911 if (!game.magic_wall_active)
6912 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6913 element = Feld[newx][newy] = Store[x][y];
6915 #if USE_NEW_CUSTOM_VALUE
6916 InitField(newx, newy, FALSE);
6919 else if (element == EL_AMOEBA_DROPPING)
6921 Feld[x][y] = get_next_element(element);
6922 element = Feld[newx][newy] = Store[x][y];
6924 else if (element == EL_SOKOBAN_OBJECT)
6927 Feld[x][y] = Back[x][y];
6929 if (Back[newx][newy])
6930 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6932 Back[x][y] = Back[newx][newy] = 0;
6935 Store[x][y] = EL_EMPTY;
6940 MovDelay[newx][newy] = 0;
6943 if (CAN_CHANGE_OR_HAS_ACTION(element))
6945 if (CAN_CHANGE(element))
6948 /* copy element change control values to new field */
6949 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6950 ChangePage[newx][newy] = ChangePage[x][y];
6951 ChangeCount[newx][newy] = ChangeCount[x][y];
6952 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6955 #if USE_NEW_CUSTOM_VALUE
6956 CustomValue[newx][newy] = CustomValue[x][y];
6962 #if USE_NEW_CUSTOM_VALUE
6963 CustomValue[newx][newy] = CustomValue[x][y];
6967 ChangeDelay[x][y] = 0;
6968 ChangePage[x][y] = -1;
6969 ChangeCount[x][y] = 0;
6970 ChangeEvent[x][y] = -1;
6972 #if USE_NEW_CUSTOM_VALUE
6973 CustomValue[x][y] = 0;
6976 /* copy animation control values to new field */
6977 GfxFrame[newx][newy] = GfxFrame[x][y];
6978 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6979 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6980 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6982 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6984 /* some elements can leave other elements behind after moving */
6986 if (ei->move_leave_element != EL_EMPTY &&
6987 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6988 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6990 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6991 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6992 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6995 int move_leave_element = ei->move_leave_element;
6999 /* this makes it possible to leave the removed element again */
7000 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7001 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7003 /* this makes it possible to leave the removed element again */
7004 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7005 move_leave_element = stored;
7008 /* this makes it possible to leave the removed element again */
7009 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7010 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7011 move_leave_element = stored;
7014 Feld[x][y] = move_leave_element;
7016 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7017 MovDir[x][y] = direction;
7019 InitField(x, y, FALSE);
7021 if (GFX_CRUMBLED(Feld[x][y]))
7022 DrawLevelFieldCrumbledSandNeighbours(x, y);
7024 if (ELEM_IS_PLAYER(move_leave_element))
7025 RelocatePlayer(x, y, move_leave_element);
7028 /* do this after checking for left-behind element */
7029 ResetGfxAnimation(x, y); /* reset animation values for old field */
7031 if (!CAN_MOVE(element) ||
7032 (CAN_FALL(element) && direction == MV_DOWN &&
7033 (element == EL_SPRING ||
7034 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7035 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7036 GfxDir[x][y] = MovDir[newx][newy] = 0;
7038 DrawLevelField(x, y);
7039 DrawLevelField(newx, newy);
7041 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7043 /* prevent pushed element from moving on in pushed direction */
7044 if (pushed_by_player && CAN_MOVE(element) &&
7045 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7046 !(element_info[element].move_pattern & direction))
7047 TurnRound(newx, newy);
7049 /* prevent elements on conveyor belt from moving on in last direction */
7050 if (pushed_by_conveyor && CAN_FALL(element) &&
7051 direction & MV_HORIZONTAL)
7052 MovDir[newx][newy] = 0;
7054 if (!pushed_by_player)
7056 int nextx = newx + dx, nexty = newy + dy;
7057 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7059 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7061 if (CAN_FALL(element) && direction == MV_DOWN)
7062 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7064 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7065 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7068 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7070 TestIfBadThingTouchesPlayer(newx, newy);
7071 TestIfBadThingTouchesFriend(newx, newy);
7073 if (!IS_CUSTOM_ELEMENT(element))
7074 TestIfBadThingTouchesOtherBadThing(newx, newy);
7076 else if (element == EL_PENGUIN)
7077 TestIfFriendTouchesBadThing(newx, newy);
7079 /* give the player one last chance (one more frame) to move away */
7080 if (CAN_FALL(element) && direction == MV_DOWN &&
7081 (last_line || (!IS_FREE(x, newy + 1) &&
7082 (!IS_PLAYER(x, newy + 1) ||
7083 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7086 if (pushed_by_player && !game.use_change_when_pushing_bug)
7088 int push_side = MV_DIR_OPPOSITE(direction);
7089 struct PlayerInfo *player = PLAYERINFO(x, y);
7091 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7092 player->index_bit, push_side);
7093 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7094 player->index_bit, push_side);
7097 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7098 MovDelay[newx][newy] = 1;
7100 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7102 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7105 if (ChangePage[newx][newy] != -1) /* delayed change */
7107 int page = ChangePage[newx][newy];
7108 struct ElementChangeInfo *change = &ei->change_page[page];
7110 ChangePage[newx][newy] = -1;
7112 if (change->can_change)
7114 if (ChangeElement(newx, newy, element, page))
7116 if (change->post_change_function)
7117 change->post_change_function(newx, newy);
7121 if (change->has_action)
7122 ExecuteCustomElementAction(newx, newy, element, page);
7126 TestIfElementHitsCustomElement(newx, newy, direction);
7127 TestIfPlayerTouchesCustomElement(newx, newy);
7128 TestIfElementTouchesCustomElement(newx, newy);
7131 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7132 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7133 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7134 MV_DIR_OPPOSITE(direction));
7138 int AmoebeNachbarNr(int ax, int ay)
7141 int element = Feld[ax][ay];
7143 static int xy[4][2] =
7151 for (i = 0; i < NUM_DIRECTIONS; i++)
7153 int x = ax + xy[i][0];
7154 int y = ay + xy[i][1];
7156 if (!IN_LEV_FIELD(x, y))
7159 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7160 group_nr = AmoebaNr[x][y];
7166 void AmoebenVereinigen(int ax, int ay)
7168 int i, x, y, xx, yy;
7169 int new_group_nr = AmoebaNr[ax][ay];
7170 static int xy[4][2] =
7178 if (new_group_nr == 0)
7181 for (i = 0; i < NUM_DIRECTIONS; i++)
7186 if (!IN_LEV_FIELD(x, y))
7189 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7190 Feld[x][y] == EL_BD_AMOEBA ||
7191 Feld[x][y] == EL_AMOEBA_DEAD) &&
7192 AmoebaNr[x][y] != new_group_nr)
7194 int old_group_nr = AmoebaNr[x][y];
7196 if (old_group_nr == 0)
7199 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7200 AmoebaCnt[old_group_nr] = 0;
7201 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7202 AmoebaCnt2[old_group_nr] = 0;
7205 SCAN_PLAYFIELD(xx, yy)
7207 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7210 if (AmoebaNr[xx][yy] == old_group_nr)
7211 AmoebaNr[xx][yy] = new_group_nr;
7217 void AmoebeUmwandeln(int ax, int ay)
7221 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7223 int group_nr = AmoebaNr[ax][ay];
7228 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7229 printf("AmoebeUmwandeln(): This should never happen!\n");
7235 SCAN_PLAYFIELD(x, y)
7237 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7240 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7243 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7247 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7248 SND_AMOEBA_TURNING_TO_GEM :
7249 SND_AMOEBA_TURNING_TO_ROCK));
7254 static int xy[4][2] =
7262 for (i = 0; i < NUM_DIRECTIONS; i++)
7267 if (!IN_LEV_FIELD(x, y))
7270 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7272 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7273 SND_AMOEBA_TURNING_TO_GEM :
7274 SND_AMOEBA_TURNING_TO_ROCK));
7281 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7284 int group_nr = AmoebaNr[ax][ay];
7285 boolean done = FALSE;
7290 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7291 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7297 SCAN_PLAYFIELD(x, y)
7299 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7302 if (AmoebaNr[x][y] == group_nr &&
7303 (Feld[x][y] == EL_AMOEBA_DEAD ||
7304 Feld[x][y] == EL_BD_AMOEBA ||
7305 Feld[x][y] == EL_AMOEBA_GROWING))
7308 Feld[x][y] = new_element;
7309 InitField(x, y, FALSE);
7310 DrawLevelField(x, y);
7316 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7317 SND_BD_AMOEBA_TURNING_TO_ROCK :
7318 SND_BD_AMOEBA_TURNING_TO_GEM));
7321 void AmoebeWaechst(int x, int y)
7323 static unsigned long sound_delay = 0;
7324 static unsigned long sound_delay_value = 0;
7326 if (!MovDelay[x][y]) /* start new growing cycle */
7330 if (DelayReached(&sound_delay, sound_delay_value))
7332 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7333 sound_delay_value = 30;
7337 if (MovDelay[x][y]) /* wait some time before growing bigger */
7340 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7342 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7343 6 - MovDelay[x][y]);
7345 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7348 if (!MovDelay[x][y])
7350 Feld[x][y] = Store[x][y];
7352 DrawLevelField(x, y);
7357 void AmoebaDisappearing(int x, int y)
7359 static unsigned long sound_delay = 0;
7360 static unsigned long sound_delay_value = 0;
7362 if (!MovDelay[x][y]) /* start new shrinking cycle */
7366 if (DelayReached(&sound_delay, sound_delay_value))
7367 sound_delay_value = 30;
7370 if (MovDelay[x][y]) /* wait some time before shrinking */
7373 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7375 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7376 6 - MovDelay[x][y]);
7378 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7381 if (!MovDelay[x][y])
7383 Feld[x][y] = EL_EMPTY;
7384 DrawLevelField(x, y);
7386 /* don't let mole enter this field in this cycle;
7387 (give priority to objects falling to this field from above) */
7393 void AmoebeAbleger(int ax, int ay)
7396 int element = Feld[ax][ay];
7397 int graphic = el2img(element);
7398 int newax = ax, neway = ay;
7399 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7400 static int xy[4][2] =
7408 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7410 Feld[ax][ay] = EL_AMOEBA_DEAD;
7411 DrawLevelField(ax, ay);
7415 if (IS_ANIMATED(graphic))
7416 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7418 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7419 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7421 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7424 if (MovDelay[ax][ay])
7428 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7431 int x = ax + xy[start][0];
7432 int y = ay + xy[start][1];
7434 if (!IN_LEV_FIELD(x, y))
7437 if (IS_FREE(x, y) ||
7438 CAN_GROW_INTO(Feld[x][y]) ||
7439 Feld[x][y] == EL_QUICKSAND_EMPTY)
7445 if (newax == ax && neway == ay)
7448 else /* normal or "filled" (BD style) amoeba */
7451 boolean waiting_for_player = FALSE;
7453 for (i = 0; i < NUM_DIRECTIONS; i++)
7455 int j = (start + i) % 4;
7456 int x = ax + xy[j][0];
7457 int y = ay + xy[j][1];
7459 if (!IN_LEV_FIELD(x, y))
7462 if (IS_FREE(x, y) ||
7463 CAN_GROW_INTO(Feld[x][y]) ||
7464 Feld[x][y] == EL_QUICKSAND_EMPTY)
7470 else if (IS_PLAYER(x, y))
7471 waiting_for_player = TRUE;
7474 if (newax == ax && neway == ay) /* amoeba cannot grow */
7476 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7478 Feld[ax][ay] = EL_AMOEBA_DEAD;
7479 DrawLevelField(ax, ay);
7480 AmoebaCnt[AmoebaNr[ax][ay]]--;
7482 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7484 if (element == EL_AMOEBA_FULL)
7485 AmoebeUmwandeln(ax, ay);
7486 else if (element == EL_BD_AMOEBA)
7487 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7492 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7494 /* amoeba gets larger by growing in some direction */
7496 int new_group_nr = AmoebaNr[ax][ay];
7499 if (new_group_nr == 0)
7501 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7502 printf("AmoebeAbleger(): This should never happen!\n");
7507 AmoebaNr[newax][neway] = new_group_nr;
7508 AmoebaCnt[new_group_nr]++;
7509 AmoebaCnt2[new_group_nr]++;
7511 /* if amoeba touches other amoeba(s) after growing, unify them */
7512 AmoebenVereinigen(newax, neway);
7514 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7516 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7522 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7523 (neway == lev_fieldy - 1 && newax != ax))
7525 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7526 Store[newax][neway] = element;
7528 else if (neway == ay || element == EL_EMC_DRIPPER)
7530 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7532 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7536 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7537 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7538 Store[ax][ay] = EL_AMOEBA_DROP;
7539 ContinueMoving(ax, ay);
7543 DrawLevelField(newax, neway);
7546 void Life(int ax, int ay)
7550 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7553 int element = Feld[ax][ay];
7554 int graphic = el2img(element);
7555 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7557 boolean changed = FALSE;
7559 if (IS_ANIMATED(graphic))
7560 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7565 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7566 MovDelay[ax][ay] = life_time;
7568 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7571 if (MovDelay[ax][ay])
7575 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7577 int xx = ax+x1, yy = ay+y1;
7580 if (!IN_LEV_FIELD(xx, yy))
7583 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7585 int x = xx+x2, y = yy+y2;
7587 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7590 if (((Feld[x][y] == element ||
7591 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7593 (IS_FREE(x, y) && Stop[x][y]))
7597 if (xx == ax && yy == ay) /* field in the middle */
7599 if (nachbarn < life_parameter[0] ||
7600 nachbarn > life_parameter[1])
7602 Feld[xx][yy] = EL_EMPTY;
7604 DrawLevelField(xx, yy);
7605 Stop[xx][yy] = TRUE;
7609 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7610 { /* free border field */
7611 if (nachbarn >= life_parameter[2] &&
7612 nachbarn <= life_parameter[3])
7614 Feld[xx][yy] = element;
7615 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7617 DrawLevelField(xx, yy);
7618 Stop[xx][yy] = TRUE;
7625 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7626 SND_GAME_OF_LIFE_GROWING);
7629 static void InitRobotWheel(int x, int y)
7631 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7634 static void RunRobotWheel(int x, int y)
7636 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7639 static void StopRobotWheel(int x, int y)
7641 if (ZX == x && ZY == y)
7645 static void InitTimegateWheel(int x, int y)
7647 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7650 static void RunTimegateWheel(int x, int y)
7652 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7655 static void InitMagicBallDelay(int x, int y)
7658 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7660 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7664 static void ActivateMagicBall(int bx, int by)
7668 if (level.ball_random)
7670 int pos_border = RND(8); /* select one of the eight border elements */
7671 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7672 int xx = pos_content % 3;
7673 int yy = pos_content / 3;
7678 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7679 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7683 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7685 int xx = x - bx + 1;
7686 int yy = y - by + 1;
7688 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7689 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7693 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7696 static void InitDiagonalMovingElement(int x, int y)
7699 MovDelay[x][y] = level.android_move_time;
7703 void CheckExit(int x, int y)
7705 if (local_player->gems_still_needed > 0 ||
7706 local_player->sokobanfields_still_needed > 0 ||
7707 local_player->lights_still_needed > 0)
7709 int element = Feld[x][y];
7710 int graphic = el2img(element);
7712 if (IS_ANIMATED(graphic))
7713 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7718 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7721 Feld[x][y] = EL_EXIT_OPENING;
7723 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7726 void CheckExitSP(int x, int y)
7728 if (local_player->gems_still_needed > 0)
7730 int element = Feld[x][y];
7731 int graphic = el2img(element);
7733 if (IS_ANIMATED(graphic))
7734 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7739 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7742 Feld[x][y] = EL_SP_EXIT_OPENING;
7744 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7747 static void CloseAllOpenTimegates()
7752 SCAN_PLAYFIELD(x, y)
7754 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7757 int element = Feld[x][y];
7759 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7761 Feld[x][y] = EL_TIMEGATE_CLOSING;
7763 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7768 void EdelsteinFunkeln(int x, int y)
7770 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7773 if (Feld[x][y] == EL_BD_DIAMOND)
7776 if (MovDelay[x][y] == 0) /* next animation frame */
7777 MovDelay[x][y] = 11 * !SimpleRND(500);
7779 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7783 if (setup.direct_draw && MovDelay[x][y])
7784 SetDrawtoField(DRAW_BUFFERED);
7786 DrawLevelElementAnimation(x, y, Feld[x][y]);
7788 if (MovDelay[x][y] != 0)
7790 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7791 10 - MovDelay[x][y]);
7793 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7795 if (setup.direct_draw)
7799 dest_x = FX + SCREENX(x) * TILEX;
7800 dest_y = FY + SCREENY(y) * TILEY;
7802 BlitBitmap(drawto_field, window,
7803 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7804 SetDrawtoField(DRAW_DIRECT);
7810 void MauerWaechst(int x, int y)
7814 if (!MovDelay[x][y]) /* next animation frame */
7815 MovDelay[x][y] = 3 * delay;
7817 if (MovDelay[x][y]) /* wait some time before next frame */
7821 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7823 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7824 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7826 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7829 if (!MovDelay[x][y])
7831 if (MovDir[x][y] == MV_LEFT)
7833 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7834 DrawLevelField(x - 1, y);
7836 else if (MovDir[x][y] == MV_RIGHT)
7838 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7839 DrawLevelField(x + 1, y);
7841 else if (MovDir[x][y] == MV_UP)
7843 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7844 DrawLevelField(x, y - 1);
7848 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7849 DrawLevelField(x, y + 1);
7852 Feld[x][y] = Store[x][y];
7854 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7855 DrawLevelField(x, y);
7860 void MauerAbleger(int ax, int ay)
7862 int element = Feld[ax][ay];
7863 int graphic = el2img(element);
7864 boolean oben_frei = FALSE, unten_frei = FALSE;
7865 boolean links_frei = FALSE, rechts_frei = FALSE;
7866 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7867 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7868 boolean new_wall = FALSE;
7870 if (IS_ANIMATED(graphic))
7871 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7873 if (!MovDelay[ax][ay]) /* start building new wall */
7874 MovDelay[ax][ay] = 6;
7876 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7879 if (MovDelay[ax][ay])
7883 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7885 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7887 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7889 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7892 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7893 element == EL_EXPANDABLE_WALL_ANY)
7897 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7898 Store[ax][ay-1] = element;
7899 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7900 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7901 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7902 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7907 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7908 Store[ax][ay+1] = element;
7909 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7910 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7911 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7912 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7917 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7918 element == EL_EXPANDABLE_WALL_ANY ||
7919 element == EL_EXPANDABLE_WALL ||
7920 element == EL_BD_EXPANDABLE_WALL)
7924 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7925 Store[ax-1][ay] = element;
7926 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7927 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7928 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7929 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7935 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7936 Store[ax+1][ay] = element;
7937 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7938 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7939 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7940 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7945 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7946 DrawLevelField(ax, ay);
7948 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7950 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7951 unten_massiv = TRUE;
7952 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7953 links_massiv = TRUE;
7954 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7955 rechts_massiv = TRUE;
7957 if (((oben_massiv && unten_massiv) ||
7958 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7959 element == EL_EXPANDABLE_WALL) &&
7960 ((links_massiv && rechts_massiv) ||
7961 element == EL_EXPANDABLE_WALL_VERTICAL))
7962 Feld[ax][ay] = EL_WALL;
7965 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7968 void CheckForDragon(int x, int y)
7971 boolean dragon_found = FALSE;
7972 static int xy[4][2] =
7980 for (i = 0; i < NUM_DIRECTIONS; i++)
7982 for (j = 0; j < 4; j++)
7984 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7986 if (IN_LEV_FIELD(xx, yy) &&
7987 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7989 if (Feld[xx][yy] == EL_DRAGON)
7990 dragon_found = TRUE;
7999 for (i = 0; i < NUM_DIRECTIONS; i++)
8001 for (j = 0; j < 3; j++)
8003 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8005 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8007 Feld[xx][yy] = EL_EMPTY;
8008 DrawLevelField(xx, yy);
8017 static void InitBuggyBase(int x, int y)
8019 int element = Feld[x][y];
8020 int activating_delay = FRAMES_PER_SECOND / 4;
8023 (element == EL_SP_BUGGY_BASE ?
8024 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8025 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8027 element == EL_SP_BUGGY_BASE_ACTIVE ?
8028 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8031 static void WarnBuggyBase(int x, int y)
8034 static int xy[4][2] =
8042 for (i = 0; i < NUM_DIRECTIONS; i++)
8044 int xx = x + xy[i][0];
8045 int yy = y + xy[i][1];
8047 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8049 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8056 static void InitTrap(int x, int y)
8058 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8061 static void ActivateTrap(int x, int y)
8063 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8066 static void ChangeActiveTrap(int x, int y)
8068 int graphic = IMG_TRAP_ACTIVE;
8070 /* if new animation frame was drawn, correct crumbled sand border */
8071 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8072 DrawLevelFieldCrumbledSand(x, y);
8075 static int getSpecialActionElement(int element, int number, int base_element)
8077 return (element != EL_EMPTY ? element :
8078 number != -1 ? base_element + number - 1 :
8082 static int getModifiedActionNumber(int value_old, int operator, int operand,
8083 int value_min, int value_max)
8085 int value_new = (operator == CA_MODE_SET ? operand :
8086 operator == CA_MODE_ADD ? value_old + operand :
8087 operator == CA_MODE_SUBTRACT ? value_old - operand :
8088 operator == CA_MODE_MULTIPLY ? value_old * operand :
8089 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8090 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8093 return (value_new < value_min ? value_min :
8094 value_new > value_max ? value_max :
8098 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8100 struct ElementInfo *ei = &element_info[element];
8101 struct ElementChangeInfo *change = &ei->change_page[page];
8102 int target_element = change->target_element;
8103 int action_type = change->action_type;
8104 int action_mode = change->action_mode;
8105 int action_arg = change->action_arg;
8108 if (!change->has_action)
8111 /* ---------- determine action paramater values -------------------------- */
8113 int level_time_value =
8114 (level.time > 0 ? TimeLeft :
8117 int action_arg_element =
8118 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8119 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8120 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8123 int action_arg_direction =
8124 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8125 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8126 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8127 change->actual_trigger_side :
8128 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8129 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8132 int action_arg_number_min =
8133 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8136 int action_arg_number_max =
8137 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8138 action_type == CA_SET_LEVEL_GEMS ? 999 :
8139 action_type == CA_SET_LEVEL_TIME ? 9999 :
8140 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8141 action_type == CA_SET_CE_VALUE ? 9999 :
8142 action_type == CA_SET_CE_SCORE ? 9999 :
8145 int action_arg_number_reset =
8146 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8147 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8148 action_type == CA_SET_LEVEL_TIME ? level.time :
8149 action_type == CA_SET_LEVEL_SCORE ? 0 :
8151 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8153 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8155 action_type == CA_SET_CE_SCORE ? 0 :
8158 int action_arg_number =
8159 (action_arg <= CA_ARG_MAX ? action_arg :
8160 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8161 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8162 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8163 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8164 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8165 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8166 #if USE_NEW_CUSTOM_VALUE
8167 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8169 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8171 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8172 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8173 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8174 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8175 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8176 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8177 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8178 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8179 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8180 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8181 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8184 int action_arg_number_old =
8185 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8186 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8187 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8188 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8189 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8192 int action_arg_number_new =
8193 getModifiedActionNumber(action_arg_number_old,
8194 action_mode, action_arg_number,
8195 action_arg_number_min, action_arg_number_max);
8197 int trigger_player_bits =
8198 (change->actual_trigger_player >= EL_PLAYER_1 &&
8199 change->actual_trigger_player <= EL_PLAYER_4 ?
8200 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8203 int action_arg_player_bits =
8204 (action_arg >= CA_ARG_PLAYER_1 &&
8205 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8206 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8209 /* ---------- execute action -------------------------------------------- */
8218 /* ---------- level actions ------------------------------------------- */
8220 case CA_RESTART_LEVEL:
8222 game.restart_level = TRUE;
8227 case CA_SHOW_ENVELOPE:
8229 int element = getSpecialActionElement(action_arg_element,
8230 action_arg_number, EL_ENVELOPE_1);
8232 if (IS_ENVELOPE(element))
8233 local_player->show_envelope = element;
8238 case CA_SET_LEVEL_TIME:
8240 if (level.time > 0) /* only modify limited time value */
8242 TimeLeft = action_arg_number_new;
8244 DrawGameValue_Time(TimeLeft);
8246 if (!TimeLeft && setup.time_limit)
8247 for (i = 0; i < MAX_PLAYERS; i++)
8248 KillPlayer(&stored_player[i]);
8254 case CA_SET_LEVEL_SCORE:
8256 local_player->score = action_arg_number_new;
8258 DrawGameValue_Score(local_player->score);
8263 case CA_SET_LEVEL_GEMS:
8265 local_player->gems_still_needed = action_arg_number_new;
8267 DrawGameValue_Emeralds(local_player->gems_still_needed);
8272 #if !USE_PLAYER_GRAVITY
8273 case CA_SET_LEVEL_GRAVITY:
8275 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8276 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8277 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8283 case CA_SET_LEVEL_WIND:
8285 game.wind_direction = action_arg_direction;
8290 /* ---------- player actions ------------------------------------------ */
8292 case CA_MOVE_PLAYER:
8294 /* automatically move to the next field in specified direction */
8295 for (i = 0; i < MAX_PLAYERS; i++)
8296 if (trigger_player_bits & (1 << i))
8297 stored_player[i].programmed_action = action_arg_direction;
8302 case CA_EXIT_PLAYER:
8304 for (i = 0; i < MAX_PLAYERS; i++)
8305 if (action_arg_player_bits & (1 << i))
8306 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8311 case CA_KILL_PLAYER:
8313 for (i = 0; i < MAX_PLAYERS; i++)
8314 if (action_arg_player_bits & (1 << i))
8315 KillPlayer(&stored_player[i]);
8320 case CA_SET_PLAYER_KEYS:
8322 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8323 int element = getSpecialActionElement(action_arg_element,
8324 action_arg_number, EL_KEY_1);
8326 if (IS_KEY(element))
8328 for (i = 0; i < MAX_PLAYERS; i++)
8330 if (trigger_player_bits & (1 << i))
8332 stored_player[i].key[KEY_NR(element)] = key_state;
8335 DrawGameDoorValues();
8337 DrawGameValue_Keys(stored_player[i].key);
8340 redraw_mask |= REDRAW_DOOR_1;
8348 case CA_SET_PLAYER_SPEED:
8350 for (i = 0; i < MAX_PLAYERS; i++)
8352 if (trigger_player_bits & (1 << i))
8354 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8356 if (action_arg == CA_ARG_SPEED_FASTER &&
8357 stored_player[i].cannot_move)
8359 action_arg_number = STEPSIZE_VERY_SLOW;
8361 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8362 action_arg == CA_ARG_SPEED_FASTER)
8364 action_arg_number = 2;
8365 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8368 else if (action_arg == CA_ARG_NUMBER_RESET)
8370 action_arg_number = level.initial_player_stepsize[i];
8374 getModifiedActionNumber(move_stepsize,
8377 action_arg_number_min,
8378 action_arg_number_max);
8381 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8383 /* make sure that value is power of 2 */
8384 move_stepsize = (1 << log_2(move_stepsize));
8386 /* do no immediately change -- the player might just be moving */
8387 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8389 stored_player[i].cannot_move =
8390 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8398 case CA_SET_PLAYER_SHIELD:
8400 for (i = 0; i < MAX_PLAYERS; i++)
8402 if (trigger_player_bits & (1 << i))
8404 if (action_arg == CA_ARG_SHIELD_OFF)
8406 stored_player[i].shield_normal_time_left = 0;
8407 stored_player[i].shield_deadly_time_left = 0;
8409 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8411 stored_player[i].shield_normal_time_left = 999999;
8413 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8415 stored_player[i].shield_normal_time_left = 999999;
8416 stored_player[i].shield_deadly_time_left = 999999;
8424 #if USE_PLAYER_GRAVITY
8425 case CA_SET_PLAYER_GRAVITY:
8427 for (i = 0; i < MAX_PLAYERS; i++)
8429 if (trigger_player_bits & (1 << i))
8431 stored_player[i].gravity =
8432 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8433 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8434 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8435 stored_player[i].gravity);
8443 case CA_SET_PLAYER_ARTWORK:
8445 for (i = 0; i < MAX_PLAYERS; i++)
8447 if (trigger_player_bits & (1 << i))
8449 int artwork_element = action_arg_element;
8451 if (action_arg == CA_ARG_ELEMENT_RESET)
8453 (level.use_artwork_element[i] ? level.artwork_element[i] :
8454 stored_player[i].element_nr);
8456 stored_player[i].artwork_element = artwork_element;
8458 SetPlayerWaiting(&stored_player[i], FALSE);
8460 /* set number of special actions for bored and sleeping animation */
8461 stored_player[i].num_special_action_bored =
8462 get_num_special_action(artwork_element,
8463 ACTION_BORING_1, ACTION_BORING_LAST);
8464 stored_player[i].num_special_action_sleeping =
8465 get_num_special_action(artwork_element,
8466 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8473 /* ---------- CE actions ---------------------------------------------- */
8475 case CA_SET_CE_VALUE:
8477 #if USE_NEW_CUSTOM_VALUE
8478 int last_ce_value = CustomValue[x][y];
8480 CustomValue[x][y] = action_arg_number_new;
8483 printf("::: CE value == %d\n", CustomValue[x][y]);
8486 if (CustomValue[x][y] != last_ce_value)
8488 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8489 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8491 if (CustomValue[x][y] == 0)
8494 printf("::: CE_VALUE_GETS_ZERO\n");
8497 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8498 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8501 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8511 case CA_SET_CE_SCORE:
8513 #if USE_NEW_CUSTOM_VALUE
8514 int last_ce_score = ei->collect_score;
8516 ei->collect_score = action_arg_number_new;
8519 printf("::: CE score == %d\n", ei->collect_score);
8522 if (ei->collect_score != last_ce_score)
8524 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8525 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8527 if (ei->collect_score == 0)
8532 printf("::: CE_SCORE_GETS_ZERO\n");
8535 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8536 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8539 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8544 This is a very special case that seems to be a mixture between
8545 CheckElementChange() and CheckTriggeredElementChange(): while
8546 the first one only affects single elements that are triggered
8547 directly, the second one affects multiple elements in the playfield
8548 that are triggered indirectly by another element. This is a third
8549 case: Changing the CE score always affects multiple identical CEs,
8550 so every affected CE must be checked, not only the single CE for
8551 which the CE score was changed in the first place (as every instance
8552 of that CE shares the same CE score, and therefore also can change)!
8554 SCAN_PLAYFIELD(xx, yy)
8556 if (Feld[xx][yy] == element)
8557 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8558 CE_SCORE_GETS_ZERO);
8569 /* ---------- engine actions ------------------------------------------ */
8571 case CA_SET_ENGINE_SCAN_MODE:
8573 InitPlayfieldScanMode(action_arg);
8583 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8585 int old_element = Feld[x][y];
8586 int new_element = get_element_from_group_element(element);
8587 int previous_move_direction = MovDir[x][y];
8588 #if USE_NEW_CUSTOM_VALUE
8589 int last_ce_value = CustomValue[x][y];
8591 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8592 boolean add_player_onto_element = (new_element_is_player &&
8593 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8594 /* this breaks SnakeBite when a snake is
8595 halfway through a door that closes */
8596 /* NOW FIXED AT LEVEL INIT IN files.c */
8597 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8599 IS_WALKABLE(old_element));
8602 /* check if element under the player changes from accessible to unaccessible
8603 (needed for special case of dropping element which then changes) */
8604 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8605 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8613 if (!add_player_onto_element)
8615 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8616 RemoveMovingField(x, y);
8620 Feld[x][y] = new_element;
8622 #if !USE_GFX_RESET_GFX_ANIMATION
8623 ResetGfxAnimation(x, y);
8624 ResetRandomAnimationValue(x, y);
8627 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8628 MovDir[x][y] = previous_move_direction;
8630 #if USE_NEW_CUSTOM_VALUE
8631 if (element_info[new_element].use_last_ce_value)
8632 CustomValue[x][y] = last_ce_value;
8635 InitField_WithBug1(x, y, FALSE);
8637 new_element = Feld[x][y]; /* element may have changed */
8639 #if USE_GFX_RESET_GFX_ANIMATION
8640 ResetGfxAnimation(x, y);
8641 ResetRandomAnimationValue(x, y);
8644 DrawLevelField(x, y);
8646 if (GFX_CRUMBLED(new_element))
8647 DrawLevelFieldCrumbledSandNeighbours(x, y);
8651 /* check if element under the player changes from accessible to unaccessible
8652 (needed for special case of dropping element which then changes) */
8653 /* (must be checked after creating new element for walkable group elements) */
8654 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8655 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8663 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8664 if (new_element_is_player)
8665 RelocatePlayer(x, y, new_element);
8668 ChangeCount[x][y]++; /* count number of changes in the same frame */
8670 TestIfBadThingTouchesPlayer(x, y);
8671 TestIfPlayerTouchesCustomElement(x, y);
8672 TestIfElementTouchesCustomElement(x, y);
8675 static void CreateField(int x, int y, int element)
8677 CreateFieldExt(x, y, element, FALSE);
8680 static void CreateElementFromChange(int x, int y, int element)
8682 element = GET_VALID_RUNTIME_ELEMENT(element);
8684 #if USE_STOP_CHANGED_ELEMENTS
8685 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8687 int old_element = Feld[x][y];
8689 /* prevent changed element from moving in same engine frame
8690 unless both old and new element can either fall or move */
8691 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8692 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8697 CreateFieldExt(x, y, element, TRUE);
8700 static boolean ChangeElement(int x, int y, int element, int page)
8702 struct ElementInfo *ei = &element_info[element];
8703 struct ElementChangeInfo *change = &ei->change_page[page];
8704 int ce_value = CustomValue[x][y];
8705 int ce_score = ei->collect_score;
8707 int old_element = Feld[x][y];
8709 /* always use default change event to prevent running into a loop */
8710 if (ChangeEvent[x][y] == -1)
8711 ChangeEvent[x][y] = CE_DELAY;
8713 if (ChangeEvent[x][y] == CE_DELAY)
8715 /* reset actual trigger element, trigger player and action element */
8716 change->actual_trigger_element = EL_EMPTY;
8717 change->actual_trigger_player = EL_PLAYER_1;
8718 change->actual_trigger_side = CH_SIDE_NONE;
8719 change->actual_trigger_ce_value = 0;
8720 change->actual_trigger_ce_score = 0;
8723 /* do not change elements more than a specified maximum number of changes */
8724 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8727 ChangeCount[x][y]++; /* count number of changes in the same frame */
8729 if (change->explode)
8736 if (change->use_target_content)
8738 boolean complete_replace = TRUE;
8739 boolean can_replace[3][3];
8742 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8745 boolean is_walkable;
8746 boolean is_diggable;
8747 boolean is_collectible;
8748 boolean is_removable;
8749 boolean is_destructible;
8750 int ex = x + xx - 1;
8751 int ey = y + yy - 1;
8752 int content_element = change->target_content.e[xx][yy];
8755 can_replace[xx][yy] = TRUE;
8757 if (ex == x && ey == y) /* do not check changing element itself */
8760 if (content_element == EL_EMPTY_SPACE)
8762 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8767 if (!IN_LEV_FIELD(ex, ey))
8769 can_replace[xx][yy] = FALSE;
8770 complete_replace = FALSE;
8777 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8778 e = MovingOrBlocked2Element(ex, ey);
8780 is_empty = (IS_FREE(ex, ey) ||
8781 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8783 is_walkable = (is_empty || IS_WALKABLE(e));
8784 is_diggable = (is_empty || IS_DIGGABLE(e));
8785 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8786 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8787 is_removable = (is_diggable || is_collectible);
8789 can_replace[xx][yy] =
8790 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8791 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8792 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8793 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8794 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8795 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8796 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8798 if (!can_replace[xx][yy])
8799 complete_replace = FALSE;
8802 if (!change->only_if_complete || complete_replace)
8804 boolean something_has_changed = FALSE;
8806 if (change->only_if_complete && change->use_random_replace &&
8807 RND(100) < change->random_percentage)
8810 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8812 int ex = x + xx - 1;
8813 int ey = y + yy - 1;
8814 int content_element;
8816 if (can_replace[xx][yy] && (!change->use_random_replace ||
8817 RND(100) < change->random_percentage))
8819 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8820 RemoveMovingField(ex, ey);
8822 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8824 content_element = change->target_content.e[xx][yy];
8825 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8826 ce_value, ce_score);
8828 CreateElementFromChange(ex, ey, target_element);
8830 something_has_changed = TRUE;
8832 /* for symmetry reasons, freeze newly created border elements */
8833 if (ex != x || ey != y)
8834 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8838 if (something_has_changed)
8840 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8841 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8847 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8848 ce_value, ce_score);
8850 if (element == EL_DIAGONAL_GROWING ||
8851 element == EL_DIAGONAL_SHRINKING)
8853 target_element = Store[x][y];
8855 Store[x][y] = EL_EMPTY;
8858 CreateElementFromChange(x, y, target_element);
8860 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8861 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8864 /* this uses direct change before indirect change */
8865 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8870 #if USE_NEW_DELAYED_ACTION
8872 static void HandleElementChange(int x, int y, int page)
8874 int element = MovingOrBlocked2Element(x, y);
8875 struct ElementInfo *ei = &element_info[element];
8876 struct ElementChangeInfo *change = &ei->change_page[page];
8879 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8880 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8883 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8884 x, y, element, element_info[element].token_name);
8885 printf("HandleElementChange(): This should never happen!\n");
8890 /* this can happen with classic bombs on walkable, changing elements */
8891 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8894 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8895 ChangeDelay[x][y] = 0;
8901 if (ChangeDelay[x][y] == 0) /* initialize element change */
8903 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8905 if (change->can_change)
8907 ResetGfxAnimation(x, y);
8908 ResetRandomAnimationValue(x, y);
8910 if (change->pre_change_function)
8911 change->pre_change_function(x, y);
8915 ChangeDelay[x][y]--;
8917 if (ChangeDelay[x][y] != 0) /* continue element change */
8919 if (change->can_change)
8921 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8923 if (IS_ANIMATED(graphic))
8924 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8926 if (change->change_function)
8927 change->change_function(x, y);
8930 else /* finish element change */
8932 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8934 page = ChangePage[x][y];
8935 ChangePage[x][y] = -1;
8937 change = &ei->change_page[page];
8940 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8942 ChangeDelay[x][y] = 1; /* try change after next move step */
8943 ChangePage[x][y] = page; /* remember page to use for change */
8948 if (change->can_change)
8950 if (ChangeElement(x, y, element, page))
8952 if (change->post_change_function)
8953 change->post_change_function(x, y);
8957 if (change->has_action)
8958 ExecuteCustomElementAction(x, y, element, page);
8964 static void HandleElementChange(int x, int y, int page)
8966 int element = MovingOrBlocked2Element(x, y);
8967 struct ElementInfo *ei = &element_info[element];
8968 struct ElementChangeInfo *change = &ei->change_page[page];
8971 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8974 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8975 x, y, element, element_info[element].token_name);
8976 printf("HandleElementChange(): This should never happen!\n");
8981 /* this can happen with classic bombs on walkable, changing elements */
8982 if (!CAN_CHANGE(element))
8985 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8986 ChangeDelay[x][y] = 0;
8992 if (ChangeDelay[x][y] == 0) /* initialize element change */
8994 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8996 ResetGfxAnimation(x, y);
8997 ResetRandomAnimationValue(x, y);
8999 if (change->pre_change_function)
9000 change->pre_change_function(x, y);
9003 ChangeDelay[x][y]--;
9005 if (ChangeDelay[x][y] != 0) /* continue element change */
9007 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9009 if (IS_ANIMATED(graphic))
9010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9012 if (change->change_function)
9013 change->change_function(x, y);
9015 else /* finish element change */
9017 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9019 page = ChangePage[x][y];
9020 ChangePage[x][y] = -1;
9022 change = &ei->change_page[page];
9025 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9027 ChangeDelay[x][y] = 1; /* try change after next move step */
9028 ChangePage[x][y] = page; /* remember page to use for change */
9033 if (ChangeElement(x, y, element, page))
9035 if (change->post_change_function)
9036 change->post_change_function(x, y);
9043 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9044 int trigger_element,
9050 boolean change_done_any = FALSE;
9051 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9054 if (!(trigger_events[trigger_element][trigger_event]))
9057 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9059 int element = EL_CUSTOM_START + i;
9060 boolean change_done = FALSE;
9063 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9064 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9067 for (p = 0; p < element_info[element].num_change_pages; p++)
9069 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9071 if (change->can_change_or_has_action &&
9072 change->has_event[trigger_event] &&
9073 change->trigger_side & trigger_side &&
9074 change->trigger_player & trigger_player &&
9075 change->trigger_page & trigger_page_bits &&
9076 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9078 change->actual_trigger_element = trigger_element;
9079 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9080 change->actual_trigger_side = trigger_side;
9081 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9082 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9084 if ((change->can_change && !change_done) || change->has_action)
9089 SCAN_PLAYFIELD(x, y)
9091 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9094 if (Feld[x][y] == element)
9096 if (change->can_change && !change_done)
9098 ChangeDelay[x][y] = 1;
9099 ChangeEvent[x][y] = trigger_event;
9101 HandleElementChange(x, y, p);
9103 #if USE_NEW_DELAYED_ACTION
9104 else if (change->has_action)
9106 ExecuteCustomElementAction(x, y, element, p);
9107 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9110 if (change->has_action)
9112 ExecuteCustomElementAction(x, y, element, p);
9113 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9119 if (change->can_change)
9122 change_done_any = TRUE;
9129 return change_done_any;
9132 static boolean CheckElementChangeExt(int x, int y,
9134 int trigger_element,
9139 boolean change_done = FALSE;
9142 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9143 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9146 if (Feld[x][y] == EL_BLOCKED)
9148 Blocked2Moving(x, y, &x, &y);
9149 element = Feld[x][y];
9153 /* check if element has already changed */
9154 if (Feld[x][y] != element)
9157 /* check if element has already changed or is about to change after moving */
9158 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9159 Feld[x][y] != element) ||
9161 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9162 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9163 ChangePage[x][y] != -1)))
9167 for (p = 0; p < element_info[element].num_change_pages; p++)
9169 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9171 boolean check_trigger_element =
9172 (trigger_event == CE_TOUCHING_X ||
9173 trigger_event == CE_HITTING_X ||
9174 trigger_event == CE_HIT_BY_X);
9176 if (change->can_change_or_has_action &&
9177 change->has_event[trigger_event] &&
9178 change->trigger_side & trigger_side &&
9179 change->trigger_player & trigger_player &&
9180 (!check_trigger_element ||
9181 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9183 change->actual_trigger_element = trigger_element;
9184 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9185 change->actual_trigger_side = trigger_side;
9186 change->actual_trigger_ce_value = CustomValue[x][y];
9187 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9189 /* special case: trigger element not at (x,y) position for some events */
9190 if (check_trigger_element)
9202 { 0, 0 }, { 0, 0 }, { 0, 0 },
9206 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9207 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9209 change->actual_trigger_ce_value = CustomValue[xx][yy];
9210 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9213 if (change->can_change && !change_done)
9215 ChangeDelay[x][y] = 1;
9216 ChangeEvent[x][y] = trigger_event;
9218 HandleElementChange(x, y, p);
9222 #if USE_NEW_DELAYED_ACTION
9223 else if (change->has_action)
9225 ExecuteCustomElementAction(x, y, element, p);
9226 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9229 if (change->has_action)
9231 ExecuteCustomElementAction(x, y, element, p);
9232 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9241 static void PlayPlayerSound(struct PlayerInfo *player)
9243 int jx = player->jx, jy = player->jy;
9244 int sound_element = player->artwork_element;
9245 int last_action = player->last_action_waiting;
9246 int action = player->action_waiting;
9248 if (player->is_waiting)
9250 if (action != last_action)
9251 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9253 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9257 if (action != last_action)
9258 StopSound(element_info[sound_element].sound[last_action]);
9260 if (last_action == ACTION_SLEEPING)
9261 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9265 static void PlayAllPlayersSound()
9269 for (i = 0; i < MAX_PLAYERS; i++)
9270 if (stored_player[i].active)
9271 PlayPlayerSound(&stored_player[i]);
9274 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9276 boolean last_waiting = player->is_waiting;
9277 int move_dir = player->MovDir;
9279 player->dir_waiting = move_dir;
9280 player->last_action_waiting = player->action_waiting;
9284 if (!last_waiting) /* not waiting -> waiting */
9286 player->is_waiting = TRUE;
9288 player->frame_counter_bored =
9290 game.player_boring_delay_fixed +
9291 SimpleRND(game.player_boring_delay_random);
9292 player->frame_counter_sleeping =
9294 game.player_sleeping_delay_fixed +
9295 SimpleRND(game.player_sleeping_delay_random);
9298 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9300 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9304 if (game.player_sleeping_delay_fixed +
9305 game.player_sleeping_delay_random > 0 &&
9306 player->anim_delay_counter == 0 &&
9307 player->post_delay_counter == 0 &&
9308 FrameCounter >= player->frame_counter_sleeping)
9309 player->is_sleeping = TRUE;
9310 else if (game.player_boring_delay_fixed +
9311 game.player_boring_delay_random > 0 &&
9312 FrameCounter >= player->frame_counter_bored)
9313 player->is_bored = TRUE;
9315 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9316 player->is_bored ? ACTION_BORING :
9320 if (player->is_sleeping && player->use_murphy)
9322 /* special case for sleeping Murphy when leaning against non-free tile */
9324 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9325 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9326 !IS_MOVING(player->jx - 1, player->jy)))
9328 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9329 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9330 !IS_MOVING(player->jx + 1, player->jy)))
9331 move_dir = MV_RIGHT;
9333 player->is_sleeping = FALSE;
9335 player->dir_waiting = move_dir;
9339 if (player->is_sleeping)
9341 if (player->num_special_action_sleeping > 0)
9343 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9345 int last_special_action = player->special_action_sleeping;
9346 int num_special_action = player->num_special_action_sleeping;
9347 int special_action =
9348 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9349 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9350 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9351 last_special_action + 1 : ACTION_SLEEPING);
9352 int special_graphic =
9353 el_act_dir2img(player->artwork_element, special_action, move_dir);
9355 player->anim_delay_counter =
9356 graphic_info[special_graphic].anim_delay_fixed +
9357 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9358 player->post_delay_counter =
9359 graphic_info[special_graphic].post_delay_fixed +
9360 SimpleRND(graphic_info[special_graphic].post_delay_random);
9362 player->special_action_sleeping = special_action;
9365 if (player->anim_delay_counter > 0)
9367 player->action_waiting = player->special_action_sleeping;
9368 player->anim_delay_counter--;
9370 else if (player->post_delay_counter > 0)
9372 player->post_delay_counter--;
9376 else if (player->is_bored)
9378 if (player->num_special_action_bored > 0)
9380 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9382 int special_action =
9383 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9384 int special_graphic =
9385 el_act_dir2img(player->artwork_element, special_action, move_dir);
9387 player->anim_delay_counter =
9388 graphic_info[special_graphic].anim_delay_fixed +
9389 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9390 player->post_delay_counter =
9391 graphic_info[special_graphic].post_delay_fixed +
9392 SimpleRND(graphic_info[special_graphic].post_delay_random);
9394 player->special_action_bored = special_action;
9397 if (player->anim_delay_counter > 0)
9399 player->action_waiting = player->special_action_bored;
9400 player->anim_delay_counter--;
9402 else if (player->post_delay_counter > 0)
9404 player->post_delay_counter--;
9409 else if (last_waiting) /* waiting -> not waiting */
9411 player->is_waiting = FALSE;
9412 player->is_bored = FALSE;
9413 player->is_sleeping = FALSE;
9415 player->frame_counter_bored = -1;
9416 player->frame_counter_sleeping = -1;
9418 player->anim_delay_counter = 0;
9419 player->post_delay_counter = 0;
9421 player->dir_waiting = player->MovDir;
9422 player->action_waiting = ACTION_DEFAULT;
9424 player->special_action_bored = ACTION_DEFAULT;
9425 player->special_action_sleeping = ACTION_DEFAULT;
9429 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9431 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9432 int left = player_action & JOY_LEFT;
9433 int right = player_action & JOY_RIGHT;
9434 int up = player_action & JOY_UP;
9435 int down = player_action & JOY_DOWN;
9436 int button1 = player_action & JOY_BUTTON_1;
9437 int button2 = player_action & JOY_BUTTON_2;
9438 int dx = (left ? -1 : right ? 1 : 0);
9439 int dy = (up ? -1 : down ? 1 : 0);
9441 if (!player->active || tape.pausing)
9447 snapped = SnapField(player, dx, dy);
9451 dropped = DropElement(player);
9453 moved = MovePlayer(player, dx, dy);
9456 if (tape.single_step && tape.recording && !tape.pausing)
9458 if (button1 || (dropped && !moved))
9460 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9461 SnapField(player, 0, 0); /* stop snapping */
9465 SetPlayerWaiting(player, FALSE);
9467 return player_action;
9471 /* no actions for this player (no input at player's configured device) */
9473 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9474 SnapField(player, 0, 0);
9475 CheckGravityMovementWhenNotMoving(player);
9477 if (player->MovPos == 0)
9478 SetPlayerWaiting(player, TRUE);
9480 if (player->MovPos == 0) /* needed for tape.playing */
9481 player->is_moving = FALSE;
9483 player->is_dropping = FALSE;
9484 player->is_dropping_pressed = FALSE;
9485 player->drop_pressed_delay = 0;
9491 static void CheckLevelTime()
9495 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9497 if (level.native_em_level->lev->home == 0) /* all players at home */
9499 local_player->LevelSolved = TRUE;
9500 AllPlayersGone = TRUE;
9502 level.native_em_level->lev->home = -1;
9505 if (level.native_em_level->ply[0]->alive == 0 &&
9506 level.native_em_level->ply[1]->alive == 0 &&
9507 level.native_em_level->ply[2]->alive == 0 &&
9508 level.native_em_level->ply[3]->alive == 0) /* all dead */
9509 AllPlayersGone = TRUE;
9512 if (TimeFrames >= FRAMES_PER_SECOND)
9517 for (i = 0; i < MAX_PLAYERS; i++)
9519 struct PlayerInfo *player = &stored_player[i];
9521 if (SHIELD_ON(player))
9523 player->shield_normal_time_left--;
9525 if (player->shield_deadly_time_left > 0)
9526 player->shield_deadly_time_left--;
9530 if (!level.use_step_counter)
9538 if (TimeLeft <= 10 && setup.time_limit)
9539 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9541 DrawGameValue_Time(TimeLeft);
9543 if (!TimeLeft && setup.time_limit)
9545 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9546 level.native_em_level->lev->killed_out_of_time = TRUE;
9548 for (i = 0; i < MAX_PLAYERS; i++)
9549 KillPlayer(&stored_player[i]);
9552 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9553 DrawGameValue_Time(TimePlayed);
9555 level.native_em_level->lev->time =
9556 (level.time == 0 ? TimePlayed : TimeLeft);
9559 if (tape.recording || tape.playing)
9560 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9564 void AdvanceFrameAndPlayerCounters(int player_nr)
9569 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9570 FrameCounter, FrameCounter + 1);
9573 /* advance frame counters (global frame counter and time frame counter) */
9577 /* advance player counters (counters for move delay, move animation etc.) */
9578 for (i = 0; i < MAX_PLAYERS; i++)
9580 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9581 int move_delay_value = stored_player[i].move_delay_value;
9582 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9584 if (!advance_player_counters) /* not all players may be affected */
9587 #if USE_NEW_PLAYER_ANIM
9588 if (move_frames == 0) /* less than one move per game frame */
9590 int stepsize = TILEX / move_delay_value;
9591 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9592 int count = (stored_player[i].is_moving ?
9593 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9595 if (count % delay == 0)
9600 stored_player[i].Frame += move_frames;
9602 if (stored_player[i].MovPos != 0)
9603 stored_player[i].StepFrame += move_frames;
9605 if (stored_player[i].move_delay > 0)
9606 stored_player[i].move_delay--;
9608 /* due to bugs in previous versions, counter must count up, not down */
9609 if (stored_player[i].push_delay != -1)
9610 stored_player[i].push_delay++;
9612 if (stored_player[i].drop_delay > 0)
9613 stored_player[i].drop_delay--;
9615 if (stored_player[i].is_dropping_pressed)
9616 stored_player[i].drop_pressed_delay++;
9620 void StartGameActions(boolean init_network_game, boolean record_tape,
9623 unsigned long new_random_seed = InitRND(random_seed);
9626 TapeStartRecording(new_random_seed);
9628 #if defined(NETWORK_AVALIABLE)
9629 if (init_network_game)
9631 SendToServer_StartPlaying();
9639 game_status = GAME_MODE_PLAYING;
9646 static unsigned long game_frame_delay = 0;
9647 unsigned long game_frame_delay_value;
9648 byte *recorded_player_action;
9649 byte summarized_player_action = 0;
9650 byte tape_action[MAX_PLAYERS];
9653 if (game.restart_level)
9654 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9656 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9658 if (level.native_em_level->lev->home == 0) /* all players at home */
9660 local_player->LevelSolved = TRUE;
9661 AllPlayersGone = TRUE;
9663 level.native_em_level->lev->home = -1;
9666 if (level.native_em_level->ply[0]->alive == 0 &&
9667 level.native_em_level->ply[1]->alive == 0 &&
9668 level.native_em_level->ply[2]->alive == 0 &&
9669 level.native_em_level->ply[3]->alive == 0) /* all dead */
9670 AllPlayersGone = TRUE;
9673 if (local_player->LevelSolved)
9676 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9679 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9682 game_frame_delay_value =
9683 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9685 if (tape.playing && tape.warp_forward && !tape.pausing)
9686 game_frame_delay_value = 0;
9688 /* ---------- main game synchronization point ---------- */
9690 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9692 if (network_playing && !network_player_action_received)
9694 /* try to get network player actions in time */
9696 #if defined(NETWORK_AVALIABLE)
9697 /* last chance to get network player actions without main loop delay */
9701 /* game was quit by network peer */
9702 if (game_status != GAME_MODE_PLAYING)
9705 if (!network_player_action_received)
9706 return; /* failed to get network player actions in time */
9708 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9714 /* at this point we know that we really continue executing the game */
9717 network_player_action_received = FALSE;
9720 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9722 if (tape.set_centered_player)
9724 game.centered_player_nr_next = tape.centered_player_nr_next;
9725 game.set_centered_player = TRUE;
9728 for (i = 0; i < MAX_PLAYERS; i++)
9730 summarized_player_action |= stored_player[i].action;
9732 if (!network_playing)
9733 stored_player[i].effective_action = stored_player[i].action;
9736 #if defined(NETWORK_AVALIABLE)
9737 if (network_playing)
9738 SendToServer_MovePlayer(summarized_player_action);
9741 if (!options.network && !setup.team_mode)
9742 local_player->effective_action = summarized_player_action;
9744 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9746 for (i = 0; i < MAX_PLAYERS; i++)
9747 stored_player[i].effective_action =
9748 (i == game.centered_player_nr ? summarized_player_action : 0);
9751 if (recorded_player_action != NULL)
9752 for (i = 0; i < MAX_PLAYERS; i++)
9753 stored_player[i].effective_action = recorded_player_action[i];
9755 for (i = 0; i < MAX_PLAYERS; i++)
9757 tape_action[i] = stored_player[i].effective_action;
9759 /* (this can only happen in the R'n'D game engine) */
9760 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9761 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9764 /* only record actions from input devices, but not programmed actions */
9766 TapeRecordAction(tape_action);
9768 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9770 GameActions_EM_Main();
9778 void GameActions_EM_Main()
9780 byte effective_action[MAX_PLAYERS];
9781 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9784 for (i = 0; i < MAX_PLAYERS; i++)
9785 effective_action[i] = stored_player[i].effective_action;
9788 printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9791 GameActions_EM(effective_action, warp_mode);
9795 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9798 void GameActions_RND()
9800 int magic_wall_x = 0, magic_wall_y = 0;
9801 int i, x, y, element, graphic;
9803 InitPlayfieldScanModeVars();
9805 #if USE_ONE_MORE_CHANGE_PER_FRAME
9806 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9808 SCAN_PLAYFIELD(x, y)
9810 ChangeCount[x][y] = 0;
9811 ChangeEvent[x][y] = -1;
9817 if (game.set_centered_player)
9819 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9821 /* switching to "all players" only possible if all players fit to screen */
9822 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9824 game.centered_player_nr_next = game.centered_player_nr;
9825 game.set_centered_player = FALSE;
9828 /* do not switch focus to non-existing (or non-active) player */
9829 if (game.centered_player_nr_next >= 0 &&
9830 !stored_player[game.centered_player_nr_next].active)
9832 game.centered_player_nr_next = game.centered_player_nr;
9833 game.set_centered_player = FALSE;
9837 if (game.set_centered_player &&
9838 ScreenMovPos == 0) /* screen currently aligned at tile position */
9842 if (game.centered_player_nr_next == -1)
9844 setScreenCenteredToAllPlayers(&sx, &sy);
9848 sx = stored_player[game.centered_player_nr_next].jx;
9849 sy = stored_player[game.centered_player_nr_next].jy;
9852 game.centered_player_nr = game.centered_player_nr_next;
9853 game.set_centered_player = FALSE;
9855 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9856 DrawGameDoorValues();
9860 for (i = 0; i < MAX_PLAYERS; i++)
9862 int actual_player_action = stored_player[i].effective_action;
9865 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9866 - rnd_equinox_tetrachloride 048
9867 - rnd_equinox_tetrachloride_ii 096
9868 - rnd_emanuel_schmieg 002
9869 - doctor_sloan_ww 001, 020
9871 if (stored_player[i].MovPos == 0)
9872 CheckGravityMovement(&stored_player[i]);
9875 /* overwrite programmed action with tape action */
9876 if (stored_player[i].programmed_action)
9877 actual_player_action = stored_player[i].programmed_action;
9880 PlayerActions(&stored_player[i], actual_player_action);
9882 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9884 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9885 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9888 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9892 network_player_action_received = FALSE;
9895 ScrollScreen(NULL, SCROLL_GO_ON);
9897 /* for backwards compatibility, the following code emulates a fixed bug that
9898 occured when pushing elements (causing elements that just made their last
9899 pushing step to already (if possible) make their first falling step in the
9900 same game frame, which is bad); this code is also needed to use the famous
9901 "spring push bug" which is used in older levels and might be wanted to be
9902 used also in newer levels, but in this case the buggy pushing code is only
9903 affecting the "spring" element and no other elements */
9905 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9907 for (i = 0; i < MAX_PLAYERS; i++)
9909 struct PlayerInfo *player = &stored_player[i];
9913 if (player->active && player->is_pushing && player->is_moving &&
9915 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9916 Feld[x][y] == EL_SPRING))
9918 ContinueMoving(x, y);
9920 /* continue moving after pushing (this is actually a bug) */
9921 if (!IS_MOVING(x, y))
9930 SCAN_PLAYFIELD(x, y)
9932 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9935 ChangeCount[x][y] = 0;
9936 ChangeEvent[x][y] = -1;
9938 /* this must be handled before main playfield loop */
9939 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9942 if (MovDelay[x][y] <= 0)
9946 #if USE_NEW_SNAP_DELAY
9947 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9950 if (MovDelay[x][y] <= 0)
9953 DrawLevelField(x, y);
9955 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9961 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9963 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9964 printf("GameActions(): This should never happen!\n");
9966 ChangePage[x][y] = -1;
9971 if (WasJustMoving[x][y] > 0)
9972 WasJustMoving[x][y]--;
9973 if (WasJustFalling[x][y] > 0)
9974 WasJustFalling[x][y]--;
9975 if (CheckCollision[x][y] > 0)
9976 CheckCollision[x][y]--;
9980 /* reset finished pushing action (not done in ContinueMoving() to allow
9981 continuous pushing animation for elements with zero push delay) */
9982 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9984 ResetGfxAnimation(x, y);
9985 DrawLevelField(x, y);
9989 if (IS_BLOCKED(x, y))
9993 Blocked2Moving(x, y, &oldx, &oldy);
9994 if (!IS_MOVING(oldx, oldy))
9996 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9997 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9998 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9999 printf("GameActions(): This should never happen!\n");
10006 SCAN_PLAYFIELD(x, y)
10008 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10011 element = Feld[x][y];
10012 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10015 printf("::: %d,%d\n", x, y);
10017 if (element == EL_ROCK)
10018 printf("::: Yo man! Rocks can fall!\n");
10022 ResetGfxFrame(x, y, TRUE);
10024 if (graphic_info[graphic].anim_global_sync)
10025 GfxFrame[x][y] = FrameCounter;
10026 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
10028 int old_gfx_frame = GfxFrame[x][y];
10030 GfxFrame[x][y] = CustomValue[x][y];
10033 if (GfxFrame[x][y] != old_gfx_frame)
10035 DrawLevelGraphicAnimation(x, y, graphic);
10037 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
10039 int old_gfx_frame = GfxFrame[x][y];
10041 GfxFrame[x][y] = element_info[element].collect_score;
10044 if (GfxFrame[x][y] != old_gfx_frame)
10046 DrawLevelGraphicAnimation(x, y, graphic);
10048 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
10050 int old_gfx_frame = GfxFrame[x][y];
10052 GfxFrame[x][y] = ChangeDelay[x][y];
10055 if (GfxFrame[x][y] != old_gfx_frame)
10057 DrawLevelGraphicAnimation(x, y, graphic);
10061 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10062 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10063 ResetRandomAnimationValue(x, y);
10065 SetRandomAnimationValue(x, y);
10067 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10069 if (IS_INACTIVE(element))
10071 if (IS_ANIMATED(graphic))
10072 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10077 /* this may take place after moving, so 'element' may have changed */
10078 if (IS_CHANGING(x, y) &&
10079 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10081 int page = element_info[element].event_page_nr[CE_DELAY];
10083 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
10087 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10091 if (element == EL_CUSTOM_255)
10092 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10096 HandleElementChange(x, y, page);
10098 if (CAN_CHANGE(element))
10099 HandleElementChange(x, y, page);
10101 if (HAS_ACTION(element))
10102 ExecuteCustomElementAction(x, y, element, page);
10107 element = Feld[x][y];
10108 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10111 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10115 element = Feld[x][y];
10116 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10118 if (IS_ANIMATED(graphic) &&
10119 !IS_MOVING(x, y) &&
10121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10123 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10124 EdelsteinFunkeln(x, y);
10126 else if ((element == EL_ACID ||
10127 element == EL_EXIT_OPEN ||
10128 element == EL_SP_EXIT_OPEN ||
10129 element == EL_SP_TERMINAL ||
10130 element == EL_SP_TERMINAL_ACTIVE ||
10131 element == EL_EXTRA_TIME ||
10132 element == EL_SHIELD_NORMAL ||
10133 element == EL_SHIELD_DEADLY) &&
10134 IS_ANIMATED(graphic))
10135 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10136 else if (IS_MOVING(x, y))
10137 ContinueMoving(x, y);
10138 else if (IS_ACTIVE_BOMB(element))
10139 CheckDynamite(x, y);
10140 else if (element == EL_AMOEBA_GROWING)
10141 AmoebeWaechst(x, y);
10142 else if (element == EL_AMOEBA_SHRINKING)
10143 AmoebaDisappearing(x, y);
10145 #if !USE_NEW_AMOEBA_CODE
10146 else if (IS_AMOEBALIVE(element))
10147 AmoebeAbleger(x, y);
10150 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10152 else if (element == EL_EXIT_CLOSED)
10154 else if (element == EL_SP_EXIT_CLOSED)
10156 else if (element == EL_EXPANDABLE_WALL_GROWING)
10157 MauerWaechst(x, y);
10158 else if (element == EL_EXPANDABLE_WALL ||
10159 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10160 element == EL_EXPANDABLE_WALL_VERTICAL ||
10161 element == EL_EXPANDABLE_WALL_ANY ||
10162 element == EL_BD_EXPANDABLE_WALL)
10163 MauerAbleger(x, y);
10164 else if (element == EL_FLAMES)
10165 CheckForDragon(x, y);
10166 else if (element == EL_EXPLOSION)
10167 ; /* drawing of correct explosion animation is handled separately */
10168 else if (element == EL_ELEMENT_SNAPPING ||
10169 element == EL_DIAGONAL_SHRINKING ||
10170 element == EL_DIAGONAL_GROWING)
10173 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10175 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10178 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10179 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10182 if (element == EL_CUSTOM_255 ||
10183 element == EL_CUSTOM_256)
10184 DrawLevelGraphicAnimation(x, y, graphic);
10187 if (IS_BELT_ACTIVE(element))
10188 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10190 if (game.magic_wall_active)
10192 int jx = local_player->jx, jy = local_player->jy;
10194 /* play the element sound at the position nearest to the player */
10195 if ((element == EL_MAGIC_WALL_FULL ||
10196 element == EL_MAGIC_WALL_ACTIVE ||
10197 element == EL_MAGIC_WALL_EMPTYING ||
10198 element == EL_BD_MAGIC_WALL_FULL ||
10199 element == EL_BD_MAGIC_WALL_ACTIVE ||
10200 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10201 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10209 #if USE_NEW_AMOEBA_CODE
10210 /* new experimental amoeba growth stuff */
10211 if (!(FrameCounter % 8))
10213 static unsigned long random = 1684108901;
10215 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10217 x = RND(lev_fieldx);
10218 y = RND(lev_fieldy);
10219 element = Feld[x][y];
10221 if (!IS_PLAYER(x,y) &&
10222 (element == EL_EMPTY ||
10223 CAN_GROW_INTO(element) ||
10224 element == EL_QUICKSAND_EMPTY ||
10225 element == EL_ACID_SPLASH_LEFT ||
10226 element == EL_ACID_SPLASH_RIGHT))
10228 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10229 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10230 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10231 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10232 Feld[x][y] = EL_AMOEBA_DROP;
10235 random = random * 129 + 1;
10241 if (game.explosions_delayed)
10244 game.explosions_delayed = FALSE;
10247 SCAN_PLAYFIELD(x, y)
10249 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10252 element = Feld[x][y];
10254 if (ExplodeField[x][y])
10255 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10256 else if (element == EL_EXPLOSION)
10257 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10259 ExplodeField[x][y] = EX_TYPE_NONE;
10262 game.explosions_delayed = TRUE;
10265 if (game.magic_wall_active)
10267 if (!(game.magic_wall_time_left % 4))
10269 int element = Feld[magic_wall_x][magic_wall_y];
10271 if (element == EL_BD_MAGIC_WALL_FULL ||
10272 element == EL_BD_MAGIC_WALL_ACTIVE ||
10273 element == EL_BD_MAGIC_WALL_EMPTYING)
10274 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10276 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10279 if (game.magic_wall_time_left > 0)
10281 game.magic_wall_time_left--;
10282 if (!game.magic_wall_time_left)
10285 SCAN_PLAYFIELD(x, y)
10287 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10290 element = Feld[x][y];
10292 if (element == EL_MAGIC_WALL_ACTIVE ||
10293 element == EL_MAGIC_WALL_FULL)
10295 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10296 DrawLevelField(x, y);
10298 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10299 element == EL_BD_MAGIC_WALL_FULL)
10301 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10302 DrawLevelField(x, y);
10306 game.magic_wall_active = FALSE;
10311 if (game.light_time_left > 0)
10313 game.light_time_left--;
10315 if (game.light_time_left == 0)
10316 RedrawAllLightSwitchesAndInvisibleElements();
10319 if (game.timegate_time_left > 0)
10321 game.timegate_time_left--;
10323 if (game.timegate_time_left == 0)
10324 CloseAllOpenTimegates();
10327 if (game.lenses_time_left > 0)
10329 game.lenses_time_left--;
10331 if (game.lenses_time_left == 0)
10332 RedrawAllInvisibleElementsForLenses();
10335 if (game.magnify_time_left > 0)
10337 game.magnify_time_left--;
10339 if (game.magnify_time_left == 0)
10340 RedrawAllInvisibleElementsForMagnifier();
10343 for (i = 0; i < MAX_PLAYERS; i++)
10345 struct PlayerInfo *player = &stored_player[i];
10347 if (SHIELD_ON(player))
10349 if (player->shield_deadly_time_left)
10350 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10351 else if (player->shield_normal_time_left)
10352 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10359 PlayAllPlayersSound();
10361 if (options.debug) /* calculate frames per second */
10363 static unsigned long fps_counter = 0;
10364 static int fps_frames = 0;
10365 unsigned long fps_delay_ms = Counter() - fps_counter;
10369 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10371 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10374 fps_counter = Counter();
10377 redraw_mask |= REDRAW_FPS;
10380 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10382 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10384 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10386 local_player->show_envelope = 0;
10389 /* use random number generator in every frame to make it less predictable */
10390 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10394 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10396 int min_x = x, min_y = y, max_x = x, max_y = y;
10399 for (i = 0; i < MAX_PLAYERS; i++)
10401 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10403 if (!stored_player[i].active || &stored_player[i] == player)
10406 min_x = MIN(min_x, jx);
10407 min_y = MIN(min_y, jy);
10408 max_x = MAX(max_x, jx);
10409 max_y = MAX(max_y, jy);
10412 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10415 static boolean AllPlayersInVisibleScreen()
10419 for (i = 0; i < MAX_PLAYERS; i++)
10421 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10423 if (!stored_player[i].active)
10426 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10433 void ScrollLevel(int dx, int dy)
10435 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10438 BlitBitmap(drawto_field, drawto_field,
10439 FX + TILEX * (dx == -1) - softscroll_offset,
10440 FY + TILEY * (dy == -1) - softscroll_offset,
10441 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10442 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10443 FX + TILEX * (dx == 1) - softscroll_offset,
10444 FY + TILEY * (dy == 1) - softscroll_offset);
10448 x = (dx == 1 ? BX1 : BX2);
10449 for (y = BY1; y <= BY2; y++)
10450 DrawScreenField(x, y);
10455 y = (dy == 1 ? BY1 : BY2);
10456 for (x = BX1; x <= BX2; x++)
10457 DrawScreenField(x, y);
10460 redraw_mask |= REDRAW_FIELD;
10463 static boolean canFallDown(struct PlayerInfo *player)
10465 int jx = player->jx, jy = player->jy;
10467 return (IN_LEV_FIELD(jx, jy + 1) &&
10468 (IS_FREE(jx, jy + 1) ||
10469 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10470 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10471 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10474 static boolean canPassField(int x, int y, int move_dir)
10476 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10477 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10478 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10479 int nextx = x + dx;
10480 int nexty = y + dy;
10481 int element = Feld[x][y];
10483 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10484 !CAN_MOVE(element) &&
10485 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10486 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10487 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10490 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10492 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10493 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10494 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10498 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10499 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10500 (IS_DIGGABLE(Feld[newx][newy]) ||
10501 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10502 canPassField(newx, newy, move_dir)));
10505 static void CheckGravityMovement(struct PlayerInfo *player)
10507 #if USE_PLAYER_GRAVITY
10508 if (player->gravity && !player->programmed_action)
10510 if (game.gravity && !player->programmed_action)
10513 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10514 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10515 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10516 int jx = player->jx, jy = player->jy;
10517 boolean player_is_moving_to_valid_field =
10518 (!player_is_snapping &&
10519 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10520 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10521 boolean player_can_fall_down = canFallDown(player);
10523 if (player_can_fall_down &&
10524 !player_is_moving_to_valid_field)
10525 player->programmed_action = MV_DOWN;
10529 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10531 return CheckGravityMovement(player);
10533 #if USE_PLAYER_GRAVITY
10534 if (player->gravity && !player->programmed_action)
10536 if (game.gravity && !player->programmed_action)
10539 int jx = player->jx, jy = player->jy;
10540 boolean field_under_player_is_free =
10541 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10542 boolean player_is_standing_on_valid_field =
10543 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10544 (IS_WALKABLE(Feld[jx][jy]) &&
10545 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10547 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10548 player->programmed_action = MV_DOWN;
10553 MovePlayerOneStep()
10554 -----------------------------------------------------------------------------
10555 dx, dy: direction (non-diagonal) to try to move the player to
10556 real_dx, real_dy: direction as read from input device (can be diagonal)
10559 boolean MovePlayerOneStep(struct PlayerInfo *player,
10560 int dx, int dy, int real_dx, int real_dy)
10562 int jx = player->jx, jy = player->jy;
10563 int new_jx = jx + dx, new_jy = jy + dy;
10564 #if !USE_FIXED_DONT_RUN_INTO
10568 boolean player_can_move = !player->cannot_move;
10570 if (!player->active || (!dx && !dy))
10571 return MP_NO_ACTION;
10573 player->MovDir = (dx < 0 ? MV_LEFT :
10574 dx > 0 ? MV_RIGHT :
10576 dy > 0 ? MV_DOWN : MV_NONE);
10578 if (!IN_LEV_FIELD(new_jx, new_jy))
10579 return MP_NO_ACTION;
10581 if (!player_can_move)
10584 if (player->MovPos == 0)
10586 player->is_moving = FALSE;
10587 player->is_digging = FALSE;
10588 player->is_collecting = FALSE;
10589 player->is_snapping = FALSE;
10590 player->is_pushing = FALSE;
10593 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10594 SnapField(player, 0, 0);
10598 return MP_NO_ACTION;
10603 if (!options.network && game.centered_player_nr == -1 &&
10604 !AllPlayersInSight(player, new_jx, new_jy))
10605 return MP_NO_ACTION;
10607 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10608 return MP_NO_ACTION;
10611 #if !USE_FIXED_DONT_RUN_INTO
10612 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10614 /* (moved to DigField()) */
10615 if (player_can_move && DONT_RUN_INTO(element))
10617 if (element == EL_ACID && dx == 0 && dy == 1)
10619 SplashAcid(new_jx, new_jy);
10620 Feld[jx][jy] = EL_PLAYER_1;
10621 InitMovingField(jx, jy, MV_DOWN);
10622 Store[jx][jy] = EL_ACID;
10623 ContinueMoving(jx, jy);
10624 BuryPlayer(player);
10627 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10633 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10635 #if USE_FIXED_DONT_RUN_INTO
10636 if (can_move == MP_DONT_RUN_INTO)
10640 if (can_move != MP_MOVING)
10643 #if USE_FIXED_DONT_RUN_INTO
10646 /* check if DigField() has caused relocation of the player */
10647 if (player->jx != jx || player->jy != jy)
10648 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10650 StorePlayer[jx][jy] = 0;
10651 player->last_jx = jx;
10652 player->last_jy = jy;
10653 player->jx = new_jx;
10654 player->jy = new_jy;
10655 StorePlayer[new_jx][new_jy] = player->element_nr;
10657 if (player->move_delay_value_next != -1)
10659 player->move_delay_value = player->move_delay_value_next;
10660 player->move_delay_value_next = -1;
10664 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10666 player->step_counter++;
10668 PlayerVisit[jx][jy] = FrameCounter;
10670 ScrollPlayer(player, SCROLL_INIT);
10675 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10677 int jx = player->jx, jy = player->jy;
10678 int old_jx = jx, old_jy = jy;
10679 int moved = MP_NO_ACTION;
10681 if (!player->active)
10686 if (player->MovPos == 0)
10688 player->is_moving = FALSE;
10689 player->is_digging = FALSE;
10690 player->is_collecting = FALSE;
10691 player->is_snapping = FALSE;
10692 player->is_pushing = FALSE;
10698 if (player->move_delay > 0)
10701 player->move_delay = -1; /* set to "uninitialized" value */
10703 /* store if player is automatically moved to next field */
10704 player->is_auto_moving = (player->programmed_action != MV_NONE);
10706 /* remove the last programmed player action */
10707 player->programmed_action = 0;
10709 if (player->MovPos)
10711 /* should only happen if pre-1.2 tape recordings are played */
10712 /* this is only for backward compatibility */
10714 int original_move_delay_value = player->move_delay_value;
10717 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10721 /* scroll remaining steps with finest movement resolution */
10722 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10724 while (player->MovPos)
10726 ScrollPlayer(player, SCROLL_GO_ON);
10727 ScrollScreen(NULL, SCROLL_GO_ON);
10729 AdvanceFrameAndPlayerCounters(player->index_nr);
10735 player->move_delay_value = original_move_delay_value;
10738 player->is_active = FALSE;
10740 if (player->last_move_dir & MV_HORIZONTAL)
10742 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10743 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10747 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10748 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10751 #if USE_FIXED_BORDER_RUNNING_GFX
10752 if (!moved && !player->is_active)
10754 player->is_moving = FALSE;
10755 player->is_digging = FALSE;
10756 player->is_collecting = FALSE;
10757 player->is_snapping = FALSE;
10758 player->is_pushing = FALSE;
10766 if (moved & MP_MOVING && !ScreenMovPos &&
10767 (player->index_nr == game.centered_player_nr ||
10768 game.centered_player_nr == -1))
10770 if (moved & MP_MOVING && !ScreenMovPos &&
10771 (player == local_player || !options.network))
10774 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10775 int offset = (setup.scroll_delay ? 3 : 0);
10777 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10779 /* actual player has left the screen -- scroll in that direction */
10780 if (jx != old_jx) /* player has moved horizontally */
10781 scroll_x += (jx - old_jx);
10782 else /* player has moved vertically */
10783 scroll_y += (jy - old_jy);
10787 if (jx != old_jx) /* player has moved horizontally */
10789 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10790 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10791 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10793 /* don't scroll over playfield boundaries */
10794 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10795 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10797 /* don't scroll more than one field at a time */
10798 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10800 /* don't scroll against the player's moving direction */
10801 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10802 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10803 scroll_x = old_scroll_x;
10805 else /* player has moved vertically */
10807 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10808 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10809 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10811 /* don't scroll over playfield boundaries */
10812 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10813 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10815 /* don't scroll more than one field at a time */
10816 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10818 /* don't scroll against the player's moving direction */
10819 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10820 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10821 scroll_y = old_scroll_y;
10825 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10828 if (!options.network && game.centered_player_nr == -1 &&
10829 !AllPlayersInVisibleScreen())
10831 scroll_x = old_scroll_x;
10832 scroll_y = old_scroll_y;
10836 if (!options.network && !AllPlayersInVisibleScreen())
10838 scroll_x = old_scroll_x;
10839 scroll_y = old_scroll_y;
10844 ScrollScreen(player, SCROLL_INIT);
10845 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10850 player->StepFrame = 0;
10852 if (moved & MP_MOVING)
10854 if (old_jx != jx && old_jy == jy)
10855 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10856 else if (old_jx == jx && old_jy != jy)
10857 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10859 DrawLevelField(jx, jy); /* for "crumbled sand" */
10861 player->last_move_dir = player->MovDir;
10862 player->is_moving = TRUE;
10863 player->is_snapping = FALSE;
10864 player->is_switching = FALSE;
10865 player->is_dropping = FALSE;
10866 player->is_dropping_pressed = FALSE;
10867 player->drop_pressed_delay = 0;
10871 CheckGravityMovementWhenNotMoving(player);
10873 player->is_moving = FALSE;
10875 /* at this point, the player is allowed to move, but cannot move right now
10876 (e.g. because of something blocking the way) -- ensure that the player
10877 is also allowed to move in the next frame (in old versions before 3.1.1,
10878 the player was forced to wait again for eight frames before next try) */
10880 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10881 player->move_delay = 0; /* allow direct movement in the next frame */
10884 if (player->move_delay == -1) /* not yet initialized by DigField() */
10885 player->move_delay = player->move_delay_value;
10887 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10889 TestIfPlayerTouchesBadThing(jx, jy);
10890 TestIfPlayerTouchesCustomElement(jx, jy);
10893 if (!player->active)
10894 RemovePlayer(player);
10899 void ScrollPlayer(struct PlayerInfo *player, int mode)
10901 int jx = player->jx, jy = player->jy;
10902 int last_jx = player->last_jx, last_jy = player->last_jy;
10903 int move_stepsize = TILEX / player->move_delay_value;
10905 #if USE_NEW_PLAYER_SPEED
10906 if (!player->active)
10909 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10912 if (!player->active || player->MovPos == 0)
10916 if (mode == SCROLL_INIT)
10918 player->actual_frame_counter = FrameCounter;
10919 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10921 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10922 Feld[last_jx][last_jy] == EL_EMPTY)
10924 int last_field_block_delay = 0; /* start with no blocking at all */
10925 int block_delay_adjustment = player->block_delay_adjustment;
10927 /* if player blocks last field, add delay for exactly one move */
10928 if (player->block_last_field)
10930 last_field_block_delay += player->move_delay_value;
10932 /* when blocking enabled, prevent moving up despite gravity */
10933 #if USE_PLAYER_GRAVITY
10934 if (player->gravity && player->MovDir == MV_UP)
10935 block_delay_adjustment = -1;
10937 if (game.gravity && player->MovDir == MV_UP)
10938 block_delay_adjustment = -1;
10942 /* add block delay adjustment (also possible when not blocking) */
10943 last_field_block_delay += block_delay_adjustment;
10945 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10946 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10949 #if USE_NEW_PLAYER_SPEED
10950 if (player->MovPos != 0) /* player has not yet reached destination */
10956 else if (!FrameReached(&player->actual_frame_counter, 1))
10960 printf("::: player->MovPos: %d -> %d\n",
10962 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10965 #if USE_NEW_PLAYER_SPEED
10966 if (player->MovPos != 0)
10968 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10969 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10971 /* before DrawPlayer() to draw correct player graphic for this case */
10972 if (player->MovPos == 0)
10973 CheckGravityMovement(player);
10976 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10977 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10979 /* before DrawPlayer() to draw correct player graphic for this case */
10980 if (player->MovPos == 0)
10981 CheckGravityMovement(player);
10984 if (player->MovPos == 0) /* player reached destination field */
10987 printf("::: player reached destination field\n");
10990 if (player->move_delay_reset_counter > 0)
10992 player->move_delay_reset_counter--;
10994 if (player->move_delay_reset_counter == 0)
10996 /* continue with normal speed after quickly moving through gate */
10997 HALVE_PLAYER_SPEED(player);
10999 /* be able to make the next move without delay */
11000 player->move_delay = 0;
11004 player->last_jx = jx;
11005 player->last_jy = jy;
11007 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11008 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11009 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11011 DrawPlayer(player); /* needed here only to cleanup last field */
11012 RemovePlayer(player);
11014 if (local_player->friends_still_needed == 0 ||
11015 IS_SP_ELEMENT(Feld[jx][jy]))
11016 player->LevelSolved = player->GameOver = TRUE;
11019 /* this breaks one level: "machine", level 000 */
11021 int move_direction = player->MovDir;
11022 int enter_side = MV_DIR_OPPOSITE(move_direction);
11023 int leave_side = move_direction;
11024 int old_jx = last_jx;
11025 int old_jy = last_jy;
11026 int old_element = Feld[old_jx][old_jy];
11027 int new_element = Feld[jx][jy];
11029 if (IS_CUSTOM_ELEMENT(old_element))
11030 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11032 player->index_bit, leave_side);
11034 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11035 CE_PLAYER_LEAVES_X,
11036 player->index_bit, leave_side);
11038 if (IS_CUSTOM_ELEMENT(new_element))
11039 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11040 player->index_bit, enter_side);
11042 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11043 CE_PLAYER_ENTERS_X,
11044 player->index_bit, enter_side);
11046 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11047 CE_MOVE_OF_X, move_direction);
11050 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11052 TestIfPlayerTouchesBadThing(jx, jy);
11053 TestIfPlayerTouchesCustomElement(jx, jy);
11055 /* needed because pushed element has not yet reached its destination,
11056 so it would trigger a change event at its previous field location */
11057 if (!player->is_pushing)
11058 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11060 if (!player->active)
11061 RemovePlayer(player);
11064 if (level.use_step_counter)
11074 if (TimeLeft <= 10 && setup.time_limit)
11075 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11077 DrawGameValue_Time(TimeLeft);
11079 if (!TimeLeft && setup.time_limit)
11080 for (i = 0; i < MAX_PLAYERS; i++)
11081 KillPlayer(&stored_player[i]);
11083 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11084 DrawGameValue_Time(TimePlayed);
11087 if (tape.single_step && tape.recording && !tape.pausing &&
11088 !player->programmed_action)
11089 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11093 void ScrollScreen(struct PlayerInfo *player, int mode)
11095 static unsigned long screen_frame_counter = 0;
11097 if (mode == SCROLL_INIT)
11099 /* set scrolling step size according to actual player's moving speed */
11100 ScrollStepSize = TILEX / player->move_delay_value;
11102 screen_frame_counter = FrameCounter;
11103 ScreenMovDir = player->MovDir;
11104 ScreenMovPos = player->MovPos;
11105 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11108 else if (!FrameReached(&screen_frame_counter, 1))
11113 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11114 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11115 redraw_mask |= REDRAW_FIELD;
11118 ScreenMovDir = MV_NONE;
11121 void TestIfPlayerTouchesCustomElement(int x, int y)
11123 static int xy[4][2] =
11130 static int trigger_sides[4][2] =
11132 /* center side border side */
11133 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11134 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11135 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11136 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11138 static int touch_dir[4] =
11140 MV_LEFT | MV_RIGHT,
11145 int center_element = Feld[x][y]; /* should always be non-moving! */
11148 for (i = 0; i < NUM_DIRECTIONS; i++)
11150 int xx = x + xy[i][0];
11151 int yy = y + xy[i][1];
11152 int center_side = trigger_sides[i][0];
11153 int border_side = trigger_sides[i][1];
11154 int border_element;
11156 if (!IN_LEV_FIELD(xx, yy))
11159 if (IS_PLAYER(x, y))
11161 struct PlayerInfo *player = PLAYERINFO(x, y);
11163 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11164 border_element = Feld[xx][yy]; /* may be moving! */
11165 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11166 border_element = Feld[xx][yy];
11167 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11168 border_element = MovingOrBlocked2Element(xx, yy);
11170 continue; /* center and border element do not touch */
11172 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11173 player->index_bit, border_side);
11174 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11175 CE_PLAYER_TOUCHES_X,
11176 player->index_bit, border_side);
11178 else if (IS_PLAYER(xx, yy))
11180 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11182 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11184 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11185 continue; /* center and border element do not touch */
11188 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11189 player->index_bit, center_side);
11190 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11191 CE_PLAYER_TOUCHES_X,
11192 player->index_bit, center_side);
11198 #if USE_ELEMENT_TOUCHING_BUGFIX
11200 void TestIfElementTouchesCustomElement(int x, int y)
11202 static int xy[4][2] =
11209 static int trigger_sides[4][2] =
11211 /* center side border side */
11212 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11213 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11214 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11215 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11217 static int touch_dir[4] =
11219 MV_LEFT | MV_RIGHT,
11224 boolean change_center_element = FALSE;
11225 int center_element = Feld[x][y]; /* should always be non-moving! */
11226 int border_element_old[NUM_DIRECTIONS];
11229 for (i = 0; i < NUM_DIRECTIONS; i++)
11231 int xx = x + xy[i][0];
11232 int yy = y + xy[i][1];
11233 int border_element;
11235 border_element_old[i] = -1;
11237 if (!IN_LEV_FIELD(xx, yy))
11240 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11241 border_element = Feld[xx][yy]; /* may be moving! */
11242 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11243 border_element = Feld[xx][yy];
11244 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11245 border_element = MovingOrBlocked2Element(xx, yy);
11247 continue; /* center and border element do not touch */
11249 border_element_old[i] = border_element;
11252 for (i = 0; i < NUM_DIRECTIONS; i++)
11254 int xx = x + xy[i][0];
11255 int yy = y + xy[i][1];
11256 int center_side = trigger_sides[i][0];
11257 int border_element = border_element_old[i];
11259 if (border_element == -1)
11262 /* check for change of border element */
11263 CheckElementChangeBySide(xx, yy, border_element, center_element,
11264 CE_TOUCHING_X, center_side);
11267 for (i = 0; i < NUM_DIRECTIONS; i++)
11269 int border_side = trigger_sides[i][1];
11270 int border_element = border_element_old[i];
11272 if (border_element == -1)
11275 /* check for change of center element (but change it only once) */
11276 if (!change_center_element)
11277 change_center_element =
11278 CheckElementChangeBySide(x, y, center_element, border_element,
11279 CE_TOUCHING_X, border_side);
11285 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11287 static int xy[4][2] =
11294 static int trigger_sides[4][2] =
11296 /* center side border side */
11297 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11298 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11299 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11300 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11302 static int touch_dir[4] =
11304 MV_LEFT | MV_RIGHT,
11309 boolean change_center_element = FALSE;
11310 int center_element = Feld[x][y]; /* should always be non-moving! */
11313 for (i = 0; i < NUM_DIRECTIONS; i++)
11315 int xx = x + xy[i][0];
11316 int yy = y + xy[i][1];
11317 int center_side = trigger_sides[i][0];
11318 int border_side = trigger_sides[i][1];
11319 int border_element;
11321 if (!IN_LEV_FIELD(xx, yy))
11324 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11325 border_element = Feld[xx][yy]; /* may be moving! */
11326 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11327 border_element = Feld[xx][yy];
11328 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11329 border_element = MovingOrBlocked2Element(xx, yy);
11331 continue; /* center and border element do not touch */
11333 /* check for change of center element (but change it only once) */
11334 if (!change_center_element)
11335 change_center_element =
11336 CheckElementChangeBySide(x, y, center_element, border_element,
11337 CE_TOUCHING_X, border_side);
11339 /* check for change of border element */
11340 CheckElementChangeBySide(xx, yy, border_element, center_element,
11341 CE_TOUCHING_X, center_side);
11347 void TestIfElementHitsCustomElement(int x, int y, int direction)
11349 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11350 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11351 int hitx = x + dx, hity = y + dy;
11352 int hitting_element = Feld[x][y];
11353 int touched_element;
11355 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11358 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11359 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11361 if (IN_LEV_FIELD(hitx, hity))
11363 int opposite_direction = MV_DIR_OPPOSITE(direction);
11364 int hitting_side = direction;
11365 int touched_side = opposite_direction;
11366 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11367 MovDir[hitx][hity] != direction ||
11368 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11374 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11375 CE_HITTING_X, touched_side);
11377 CheckElementChangeBySide(hitx, hity, touched_element,
11378 hitting_element, CE_HIT_BY_X, hitting_side);
11380 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11381 CE_HIT_BY_SOMETHING, opposite_direction);
11385 /* "hitting something" is also true when hitting the playfield border */
11386 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11387 CE_HITTING_SOMETHING, direction);
11391 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11393 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11394 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11395 int hitx = x + dx, hity = y + dy;
11396 int hitting_element = Feld[x][y];
11397 int touched_element;
11399 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11400 !IS_FREE(hitx, hity) &&
11401 (!IS_MOVING(hitx, hity) ||
11402 MovDir[hitx][hity] != direction ||
11403 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11406 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11410 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11414 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11415 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11417 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11418 EP_CAN_SMASH_EVERYTHING, direction);
11420 if (IN_LEV_FIELD(hitx, hity))
11422 int opposite_direction = MV_DIR_OPPOSITE(direction);
11423 int hitting_side = direction;
11424 int touched_side = opposite_direction;
11426 int touched_element = MovingOrBlocked2Element(hitx, hity);
11429 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11430 MovDir[hitx][hity] != direction ||
11431 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11440 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11441 CE_SMASHED_BY_SOMETHING, opposite_direction);
11443 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11444 CE_OTHER_IS_SMASHING, touched_side);
11446 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11447 CE_OTHER_GETS_SMASHED, hitting_side);
11453 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11455 int i, kill_x = -1, kill_y = -1;
11457 int bad_element = -1;
11458 static int test_xy[4][2] =
11465 static int test_dir[4] =
11473 for (i = 0; i < NUM_DIRECTIONS; i++)
11475 int test_x, test_y, test_move_dir, test_element;
11477 test_x = good_x + test_xy[i][0];
11478 test_y = good_y + test_xy[i][1];
11480 if (!IN_LEV_FIELD(test_x, test_y))
11484 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11486 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11488 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11489 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11491 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11492 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11496 bad_element = test_element;
11502 if (kill_x != -1 || kill_y != -1)
11504 if (IS_PLAYER(good_x, good_y))
11506 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11508 if (player->shield_deadly_time_left > 0 &&
11509 !IS_INDESTRUCTIBLE(bad_element))
11510 Bang(kill_x, kill_y);
11511 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11512 KillPlayer(player);
11515 Bang(good_x, good_y);
11519 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11521 int i, kill_x = -1, kill_y = -1;
11522 int bad_element = Feld[bad_x][bad_y];
11523 static int test_xy[4][2] =
11530 static int touch_dir[4] =
11532 MV_LEFT | MV_RIGHT,
11537 static int test_dir[4] =
11545 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11548 for (i = 0; i < NUM_DIRECTIONS; i++)
11550 int test_x, test_y, test_move_dir, test_element;
11552 test_x = bad_x + test_xy[i][0];
11553 test_y = bad_y + test_xy[i][1];
11554 if (!IN_LEV_FIELD(test_x, test_y))
11558 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11560 test_element = Feld[test_x][test_y];
11562 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11563 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11565 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11566 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11568 /* good thing is player or penguin that does not move away */
11569 if (IS_PLAYER(test_x, test_y))
11571 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11573 if (bad_element == EL_ROBOT && player->is_moving)
11574 continue; /* robot does not kill player if he is moving */
11576 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11578 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11579 continue; /* center and border element do not touch */
11586 else if (test_element == EL_PENGUIN)
11595 if (kill_x != -1 || kill_y != -1)
11597 if (IS_PLAYER(kill_x, kill_y))
11599 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11601 if (player->shield_deadly_time_left > 0 &&
11602 !IS_INDESTRUCTIBLE(bad_element))
11603 Bang(bad_x, bad_y);
11604 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11605 KillPlayer(player);
11608 Bang(kill_x, kill_y);
11612 void TestIfPlayerTouchesBadThing(int x, int y)
11614 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11617 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11619 TestIfGoodThingHitsBadThing(x, y, move_dir);
11622 void TestIfBadThingTouchesPlayer(int x, int y)
11624 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11627 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11629 TestIfBadThingHitsGoodThing(x, y, move_dir);
11632 void TestIfFriendTouchesBadThing(int x, int y)
11634 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11637 void TestIfBadThingTouchesFriend(int x, int y)
11639 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11642 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11644 int i, kill_x = bad_x, kill_y = bad_y;
11645 static int xy[4][2] =
11653 for (i = 0; i < NUM_DIRECTIONS; i++)
11657 x = bad_x + xy[i][0];
11658 y = bad_y + xy[i][1];
11659 if (!IN_LEV_FIELD(x, y))
11662 element = Feld[x][y];
11663 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11664 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11672 if (kill_x != bad_x || kill_y != bad_y)
11673 Bang(bad_x, bad_y);
11676 void KillPlayer(struct PlayerInfo *player)
11678 int jx = player->jx, jy = player->jy;
11680 if (!player->active)
11683 /* remove accessible field at the player's position */
11684 Feld[jx][jy] = EL_EMPTY;
11686 /* deactivate shield (else Bang()/Explode() would not work right) */
11687 player->shield_normal_time_left = 0;
11688 player->shield_deadly_time_left = 0;
11691 BuryPlayer(player);
11694 static void KillPlayerUnlessEnemyProtected(int x, int y)
11696 if (!PLAYER_ENEMY_PROTECTED(x, y))
11697 KillPlayer(PLAYERINFO(x, y));
11700 static void KillPlayerUnlessExplosionProtected(int x, int y)
11702 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11703 KillPlayer(PLAYERINFO(x, y));
11706 void BuryPlayer(struct PlayerInfo *player)
11708 int jx = player->jx, jy = player->jy;
11710 if (!player->active)
11713 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11714 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11716 player->GameOver = TRUE;
11717 RemovePlayer(player);
11720 void RemovePlayer(struct PlayerInfo *player)
11722 int jx = player->jx, jy = player->jy;
11723 int i, found = FALSE;
11725 player->present = FALSE;
11726 player->active = FALSE;
11728 if (!ExplodeField[jx][jy])
11729 StorePlayer[jx][jy] = 0;
11731 if (player->is_moving)
11732 DrawLevelField(player->last_jx, player->last_jy);
11734 for (i = 0; i < MAX_PLAYERS; i++)
11735 if (stored_player[i].active)
11739 AllPlayersGone = TRUE;
11745 #if USE_NEW_SNAP_DELAY
11746 static void setFieldForSnapping(int x, int y, int element, int direction)
11748 struct ElementInfo *ei = &element_info[element];
11749 int direction_bit = MV_DIR_TO_BIT(direction);
11750 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11751 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11752 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11754 Feld[x][y] = EL_ELEMENT_SNAPPING;
11755 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11757 ResetGfxAnimation(x, y);
11759 GfxElement[x][y] = element;
11760 GfxAction[x][y] = action;
11761 GfxDir[x][y] = direction;
11762 GfxFrame[x][y] = -1;
11767 =============================================================================
11768 checkDiagonalPushing()
11769 -----------------------------------------------------------------------------
11770 check if diagonal input device direction results in pushing of object
11771 (by checking if the alternative direction is walkable, diggable, ...)
11772 =============================================================================
11775 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11776 int x, int y, int real_dx, int real_dy)
11778 int jx, jy, dx, dy, xx, yy;
11780 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11783 /* diagonal direction: check alternative direction */
11788 xx = jx + (dx == 0 ? real_dx : 0);
11789 yy = jy + (dy == 0 ? real_dy : 0);
11791 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11795 =============================================================================
11797 -----------------------------------------------------------------------------
11798 x, y: field next to player (non-diagonal) to try to dig to
11799 real_dx, real_dy: direction as read from input device (can be diagonal)
11800 =============================================================================
11803 int DigField(struct PlayerInfo *player,
11804 int oldx, int oldy, int x, int y,
11805 int real_dx, int real_dy, int mode)
11807 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11808 boolean player_was_pushing = player->is_pushing;
11809 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11810 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11811 int jx = oldx, jy = oldy;
11812 int dx = x - jx, dy = y - jy;
11813 int nextx = x + dx, nexty = y + dy;
11814 int move_direction = (dx == -1 ? MV_LEFT :
11815 dx == +1 ? MV_RIGHT :
11817 dy == +1 ? MV_DOWN : MV_NONE);
11818 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11819 int dig_side = MV_DIR_OPPOSITE(move_direction);
11820 int old_element = Feld[jx][jy];
11821 #if USE_FIXED_DONT_RUN_INTO
11822 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11828 if (is_player) /* function can also be called by EL_PENGUIN */
11830 if (player->MovPos == 0)
11832 player->is_digging = FALSE;
11833 player->is_collecting = FALSE;
11836 if (player->MovPos == 0) /* last pushing move finished */
11837 player->is_pushing = FALSE;
11839 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11841 player->is_switching = FALSE;
11842 player->push_delay = -1;
11844 return MP_NO_ACTION;
11848 #if !USE_FIXED_DONT_RUN_INTO
11849 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11850 return MP_NO_ACTION;
11853 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11854 old_element = Back[jx][jy];
11856 /* in case of element dropped at player position, check background */
11857 else if (Back[jx][jy] != EL_EMPTY &&
11858 game.engine_version >= VERSION_IDENT(2,2,0,0))
11859 old_element = Back[jx][jy];
11861 /* checking here causes player to move into acid even if the current field
11862 cannot be left to that direction */
11864 #if USE_FIXED_DONT_RUN_INTO
11865 if (player_can_move && DONT_RUN_INTO(element))
11867 if (element == EL_ACID && dx == 0 && dy == 1)
11870 Feld[jx][jy] = EL_PLAYER_1;
11871 InitMovingField(jx, jy, MV_DOWN);
11872 Store[jx][jy] = EL_ACID;
11873 ContinueMoving(jx, jy);
11874 BuryPlayer(player);
11877 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11879 return MP_DONT_RUN_INTO;
11884 #if 1 /* ------------------------------ NEW ------------------------------ */
11886 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11887 return MP_NO_ACTION; /* field has no opening in this direction */
11889 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11890 return MP_NO_ACTION; /* field has no opening in this direction */
11892 #if USE_FIXED_DONT_RUN_INTO
11893 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11897 Feld[jx][jy] = player->artwork_element;
11899 Feld[jx][jy] = EL_PLAYER_1;
11901 InitMovingField(jx, jy, MV_DOWN);
11902 Store[jx][jy] = EL_ACID;
11903 ContinueMoving(jx, jy);
11904 BuryPlayer(player);
11906 return MP_DONT_RUN_INTO;
11910 #if USE_FIXED_DONT_RUN_INTO
11911 if (player_can_move && DONT_RUN_INTO(element))
11913 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11915 return MP_DONT_RUN_INTO;
11919 #else /* ------------------------------ OLD ------------------------------ */
11922 #if USE_FIXED_DONT_RUN_INTO
11923 if (player_can_move && DONT_RUN_INTO(element))
11925 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11927 return MP_DONT_RUN_INTO;
11932 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11933 return MP_NO_ACTION; /* field has no opening in this direction */
11935 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11936 return MP_NO_ACTION; /* field has no opening in this direction */
11938 /* checking here causes player to explode when moving into acid */
11940 #if USE_FIXED_DONT_RUN_INTO
11941 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11944 Feld[jx][jy] = EL_PLAYER_1;
11945 InitMovingField(jx, jy, MV_DOWN);
11946 Store[jx][jy] = EL_ACID;
11947 ContinueMoving(jx, jy);
11948 BuryPlayer(player);
11950 return MP_DONT_RUN_INTO;
11955 #endif /* ------------------------------ END ------------------------------ */
11958 #if USE_FIXED_DONT_RUN_INTO
11959 if (player_can_move && DONT_RUN_INTO(element))
11961 if (element == EL_ACID && dx == 0 && dy == 1)
11964 Feld[jx][jy] = EL_PLAYER_1;
11965 InitMovingField(jx, jy, MV_DOWN);
11966 Store[jx][jy] = EL_ACID;
11967 ContinueMoving(jx, jy);
11968 BuryPlayer(player);
11971 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11973 return MP_DONT_RUN_INTO;
11978 #if USE_FIXED_DONT_RUN_INTO
11979 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11980 return MP_NO_ACTION;
11983 #if !USE_FIXED_DONT_RUN_INTO
11984 element = Feld[x][y];
11987 collect_count = element_info[element].collect_count_initial;
11989 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11990 return MP_NO_ACTION;
11992 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11993 player_can_move = player_can_move_or_snap;
11995 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11996 game.engine_version >= VERSION_IDENT(2,2,0,0))
11998 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11999 player->index_bit, dig_side);
12000 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12001 player->index_bit, dig_side);
12003 if (Feld[x][y] != element) /* field changed by snapping */
12006 return MP_NO_ACTION;
12009 #if USE_PLAYER_GRAVITY
12010 if (player->gravity && is_player && !player->is_auto_moving &&
12011 canFallDown(player) && move_direction != MV_DOWN &&
12012 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12013 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12015 if (game.gravity && is_player && !player->is_auto_moving &&
12016 canFallDown(player) && move_direction != MV_DOWN &&
12017 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12018 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12021 if (player_can_move &&
12022 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12024 int sound_element = SND_ELEMENT(element);
12025 int sound_action = ACTION_WALKING;
12027 if (IS_RND_GATE(element))
12029 if (!player->key[RND_GATE_NR(element)])
12030 return MP_NO_ACTION;
12032 else if (IS_RND_GATE_GRAY(element))
12034 if (!player->key[RND_GATE_GRAY_NR(element)])
12035 return MP_NO_ACTION;
12037 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12039 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12040 return MP_NO_ACTION;
12042 else if (element == EL_EXIT_OPEN ||
12043 element == EL_SP_EXIT_OPEN ||
12044 element == EL_SP_EXIT_OPENING)
12046 sound_action = ACTION_PASSING; /* player is passing exit */
12048 else if (element == EL_EMPTY)
12050 sound_action = ACTION_MOVING; /* nothing to walk on */
12053 /* play sound from background or player, whatever is available */
12054 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12055 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12057 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12059 else if (player_can_move &&
12060 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12062 if (!ACCESS_FROM(element, opposite_direction))
12063 return MP_NO_ACTION; /* field not accessible from this direction */
12065 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12066 return MP_NO_ACTION;
12068 if (IS_EM_GATE(element))
12070 if (!player->key[EM_GATE_NR(element)])
12071 return MP_NO_ACTION;
12073 else if (IS_EM_GATE_GRAY(element))
12075 if (!player->key[EM_GATE_GRAY_NR(element)])
12076 return MP_NO_ACTION;
12078 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12080 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12081 return MP_NO_ACTION;
12083 else if (IS_EMC_GATE(element))
12085 if (!player->key[EMC_GATE_NR(element)])
12086 return MP_NO_ACTION;
12088 else if (IS_EMC_GATE_GRAY(element))
12090 if (!player->key[EMC_GATE_GRAY_NR(element)])
12091 return MP_NO_ACTION;
12093 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12095 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12096 return MP_NO_ACTION;
12098 else if (IS_SP_PORT(element))
12100 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12101 element == EL_SP_GRAVITY_PORT_RIGHT ||
12102 element == EL_SP_GRAVITY_PORT_UP ||
12103 element == EL_SP_GRAVITY_PORT_DOWN)
12104 #if USE_PLAYER_GRAVITY
12105 player->gravity = !player->gravity;
12107 game.gravity = !game.gravity;
12109 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12110 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12111 element == EL_SP_GRAVITY_ON_PORT_UP ||
12112 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12113 #if USE_PLAYER_GRAVITY
12114 player->gravity = TRUE;
12116 game.gravity = TRUE;
12118 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12119 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12120 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12121 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12122 #if USE_PLAYER_GRAVITY
12123 player->gravity = FALSE;
12125 game.gravity = FALSE;
12129 /* automatically move to the next field with double speed */
12130 player->programmed_action = move_direction;
12132 if (player->move_delay_reset_counter == 0)
12134 player->move_delay_reset_counter = 2; /* two double speed steps */
12136 DOUBLE_PLAYER_SPEED(player);
12139 PlayLevelSoundAction(x, y, ACTION_PASSING);
12141 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12145 if (mode != DF_SNAP)
12147 GfxElement[x][y] = GFX_ELEMENT(element);
12148 player->is_digging = TRUE;
12151 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12153 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12154 player->index_bit, dig_side);
12156 if (mode == DF_SNAP)
12158 #if USE_NEW_SNAP_DELAY
12159 if (level.block_snap_field)
12160 setFieldForSnapping(x, y, element, move_direction);
12162 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12164 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12167 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12168 player->index_bit, dig_side);
12171 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12175 if (is_player && mode != DF_SNAP)
12177 GfxElement[x][y] = element;
12178 player->is_collecting = TRUE;
12181 if (element == EL_SPEED_PILL)
12183 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12185 else if (element == EL_EXTRA_TIME && level.time > 0)
12187 TimeLeft += level.extra_time;
12188 DrawGameValue_Time(TimeLeft);
12190 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12192 player->shield_normal_time_left += level.shield_normal_time;
12193 if (element == EL_SHIELD_DEADLY)
12194 player->shield_deadly_time_left += level.shield_deadly_time;
12196 else if (element == EL_DYNAMITE ||
12197 element == EL_EM_DYNAMITE ||
12198 element == EL_SP_DISK_RED)
12200 if (player->inventory_size < MAX_INVENTORY_SIZE)
12201 player->inventory_element[player->inventory_size++] = element;
12204 DrawGameDoorValues();
12206 DrawGameValue_Dynamite(local_player->inventory_size);
12209 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12211 player->dynabomb_count++;
12212 player->dynabombs_left++;
12214 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12216 player->dynabomb_size++;
12218 else if (element == EL_DYNABOMB_INCREASE_POWER)
12220 player->dynabomb_xl = TRUE;
12222 else if (IS_KEY(element))
12224 player->key[KEY_NR(element)] = TRUE;
12227 DrawGameDoorValues();
12229 DrawGameValue_Keys(player->key);
12232 redraw_mask |= REDRAW_DOOR_1;
12234 else if (IS_ENVELOPE(element))
12236 player->show_envelope = element;
12238 else if (element == EL_EMC_LENSES)
12240 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12242 RedrawAllInvisibleElementsForLenses();
12244 else if (element == EL_EMC_MAGNIFIER)
12246 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12248 RedrawAllInvisibleElementsForMagnifier();
12250 else if (IS_DROPPABLE(element) ||
12251 IS_THROWABLE(element)) /* can be collected and dropped */
12255 if (collect_count == 0)
12256 player->inventory_infinite_element = element;
12258 for (i = 0; i < collect_count; i++)
12259 if (player->inventory_size < MAX_INVENTORY_SIZE)
12260 player->inventory_element[player->inventory_size++] = element;
12263 DrawGameDoorValues();
12265 DrawGameValue_Dynamite(local_player->inventory_size);
12268 else if (collect_count > 0)
12270 local_player->gems_still_needed -= collect_count;
12271 if (local_player->gems_still_needed < 0)
12272 local_player->gems_still_needed = 0;
12274 DrawGameValue_Emeralds(local_player->gems_still_needed);
12277 RaiseScoreElement(element);
12278 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12281 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12282 player->index_bit, dig_side);
12284 if (mode == DF_SNAP)
12286 #if USE_NEW_SNAP_DELAY
12287 if (level.block_snap_field)
12288 setFieldForSnapping(x, y, element, move_direction);
12290 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12292 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12295 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12296 player->index_bit, dig_side);
12299 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12301 if (mode == DF_SNAP && element != EL_BD_ROCK)
12302 return MP_NO_ACTION;
12304 if (CAN_FALL(element) && dy)
12305 return MP_NO_ACTION;
12307 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12308 !(element == EL_SPRING && level.use_spring_bug))
12309 return MP_NO_ACTION;
12311 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12312 ((move_direction & MV_VERTICAL &&
12313 ((element_info[element].move_pattern & MV_LEFT &&
12314 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12315 (element_info[element].move_pattern & MV_RIGHT &&
12316 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12317 (move_direction & MV_HORIZONTAL &&
12318 ((element_info[element].move_pattern & MV_UP &&
12319 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12320 (element_info[element].move_pattern & MV_DOWN &&
12321 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12322 return MP_NO_ACTION;
12324 /* do not push elements already moving away faster than player */
12325 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12326 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12327 return MP_NO_ACTION;
12329 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12331 if (player->push_delay_value == -1 || !player_was_pushing)
12332 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12334 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12336 if (player->push_delay_value == -1)
12337 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12339 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12341 if (!player->is_pushing)
12342 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12345 player->is_pushing = TRUE;
12346 player->is_active = TRUE;
12348 if (!(IN_LEV_FIELD(nextx, nexty) &&
12349 (IS_FREE(nextx, nexty) ||
12350 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12351 IS_SB_ELEMENT(element)))))
12352 return MP_NO_ACTION;
12354 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12355 return MP_NO_ACTION;
12357 if (player->push_delay == -1) /* new pushing; restart delay */
12358 player->push_delay = 0;
12360 if (player->push_delay < player->push_delay_value &&
12361 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12362 element != EL_SPRING && element != EL_BALLOON)
12364 /* make sure that there is no move delay before next try to push */
12365 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12366 player->move_delay = 0;
12368 return MP_NO_ACTION;
12371 if (IS_SB_ELEMENT(element))
12373 if (element == EL_SOKOBAN_FIELD_FULL)
12375 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12376 local_player->sokobanfields_still_needed++;
12379 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12381 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12382 local_player->sokobanfields_still_needed--;
12385 Feld[x][y] = EL_SOKOBAN_OBJECT;
12387 if (Back[x][y] == Back[nextx][nexty])
12388 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12389 else if (Back[x][y] != 0)
12390 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12393 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12396 if (local_player->sokobanfields_still_needed == 0 &&
12397 game.emulation == EMU_SOKOBAN)
12399 player->LevelSolved = player->GameOver = TRUE;
12400 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12404 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12406 InitMovingField(x, y, move_direction);
12407 GfxAction[x][y] = ACTION_PUSHING;
12409 if (mode == DF_SNAP)
12410 ContinueMoving(x, y);
12412 MovPos[x][y] = (dx != 0 ? dx : dy);
12414 Pushed[x][y] = TRUE;
12415 Pushed[nextx][nexty] = TRUE;
12417 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12418 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12420 player->push_delay_value = -1; /* get new value later */
12422 /* check for element change _after_ element has been pushed */
12423 if (game.use_change_when_pushing_bug)
12425 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12426 player->index_bit, dig_side);
12427 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12428 player->index_bit, dig_side);
12431 else if (IS_SWITCHABLE(element))
12433 if (PLAYER_SWITCHING(player, x, y))
12435 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12436 player->index_bit, dig_side);
12441 player->is_switching = TRUE;
12442 player->switch_x = x;
12443 player->switch_y = y;
12445 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12447 if (element == EL_ROBOT_WHEEL)
12449 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12453 DrawLevelField(x, y);
12455 else if (element == EL_SP_TERMINAL)
12460 SCAN_PLAYFIELD(xx, yy)
12462 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12465 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12467 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12468 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12471 else if (IS_BELT_SWITCH(element))
12473 ToggleBeltSwitch(x, y);
12475 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12476 element == EL_SWITCHGATE_SWITCH_DOWN)
12478 ToggleSwitchgateSwitch(x, y);
12480 else if (element == EL_LIGHT_SWITCH ||
12481 element == EL_LIGHT_SWITCH_ACTIVE)
12483 ToggleLightSwitch(x, y);
12485 else if (element == EL_TIMEGATE_SWITCH)
12487 ActivateTimegateSwitch(x, y);
12489 else if (element == EL_BALLOON_SWITCH_LEFT ||
12490 element == EL_BALLOON_SWITCH_RIGHT ||
12491 element == EL_BALLOON_SWITCH_UP ||
12492 element == EL_BALLOON_SWITCH_DOWN ||
12493 element == EL_BALLOON_SWITCH_NONE ||
12494 element == EL_BALLOON_SWITCH_ANY)
12496 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12497 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12498 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12499 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12500 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12503 else if (element == EL_LAMP)
12505 Feld[x][y] = EL_LAMP_ACTIVE;
12506 local_player->lights_still_needed--;
12508 ResetGfxAnimation(x, y);
12509 DrawLevelField(x, y);
12511 else if (element == EL_TIME_ORB_FULL)
12513 Feld[x][y] = EL_TIME_ORB_EMPTY;
12515 if (level.time > 0 || level.use_time_orb_bug)
12517 TimeLeft += level.time_orb_time;
12518 DrawGameValue_Time(TimeLeft);
12521 ResetGfxAnimation(x, y);
12522 DrawLevelField(x, y);
12524 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12525 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12529 game.ball_state = !game.ball_state;
12532 SCAN_PLAYFIELD(xx, yy)
12534 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12537 int e = Feld[xx][yy];
12539 if (game.ball_state)
12541 if (e == EL_EMC_MAGIC_BALL)
12542 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12543 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12544 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12548 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12549 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12550 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12551 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12556 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12557 player->index_bit, dig_side);
12559 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12560 player->index_bit, dig_side);
12562 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12563 player->index_bit, dig_side);
12569 if (!PLAYER_SWITCHING(player, x, y))
12571 player->is_switching = TRUE;
12572 player->switch_x = x;
12573 player->switch_y = y;
12575 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12576 player->index_bit, dig_side);
12577 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12578 player->index_bit, dig_side);
12580 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12581 player->index_bit, dig_side);
12582 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12583 player->index_bit, dig_side);
12586 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12587 player->index_bit, dig_side);
12588 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12589 player->index_bit, dig_side);
12591 return MP_NO_ACTION;
12594 player->push_delay = -1;
12596 if (is_player) /* function can also be called by EL_PENGUIN */
12598 if (Feld[x][y] != element) /* really digged/collected something */
12600 player->is_collecting = !player->is_digging;
12601 player->is_active = TRUE;
12608 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12610 int jx = player->jx, jy = player->jy;
12611 int x = jx + dx, y = jy + dy;
12612 int snap_direction = (dx == -1 ? MV_LEFT :
12613 dx == +1 ? MV_RIGHT :
12615 dy == +1 ? MV_DOWN : MV_NONE);
12616 boolean can_continue_snapping = (level.continuous_snapping &&
12617 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12619 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12622 if (!player->active || !IN_LEV_FIELD(x, y))
12630 if (player->MovPos == 0)
12631 player->is_pushing = FALSE;
12633 player->is_snapping = FALSE;
12635 if (player->MovPos == 0)
12637 player->is_moving = FALSE;
12638 player->is_digging = FALSE;
12639 player->is_collecting = FALSE;
12645 #if USE_NEW_CONTINUOUS_SNAPPING
12646 /* prevent snapping with already pressed snap key when not allowed */
12647 if (player->is_snapping && !can_continue_snapping)
12650 if (player->is_snapping)
12654 player->MovDir = snap_direction;
12656 if (player->MovPos == 0)
12658 player->is_moving = FALSE;
12659 player->is_digging = FALSE;
12660 player->is_collecting = FALSE;
12663 player->is_dropping = FALSE;
12664 player->is_dropping_pressed = FALSE;
12665 player->drop_pressed_delay = 0;
12667 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12670 player->is_snapping = TRUE;
12671 player->is_active = TRUE;
12673 if (player->MovPos == 0)
12675 player->is_moving = FALSE;
12676 player->is_digging = FALSE;
12677 player->is_collecting = FALSE;
12680 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12681 DrawLevelField(player->last_jx, player->last_jy);
12683 DrawLevelField(x, y);
12688 boolean DropElement(struct PlayerInfo *player)
12690 int old_element, new_element;
12691 int dropx = player->jx, dropy = player->jy;
12692 int drop_direction = player->MovDir;
12693 int drop_side = drop_direction;
12694 int drop_element = (player->inventory_size > 0 ?
12695 player->inventory_element[player->inventory_size - 1] :
12696 player->inventory_infinite_element != EL_UNDEFINED ?
12697 player->inventory_infinite_element :
12698 player->dynabombs_left > 0 ?
12699 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12702 player->is_dropping_pressed = TRUE;
12704 /* do not drop an element on top of another element; when holding drop key
12705 pressed without moving, dropped element must move away before the next
12706 element can be dropped (this is especially important if the next element
12707 is dynamite, which can be placed on background for historical reasons) */
12708 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12711 if (IS_THROWABLE(drop_element))
12713 dropx += GET_DX_FROM_DIR(drop_direction);
12714 dropy += GET_DY_FROM_DIR(drop_direction);
12716 if (!IN_LEV_FIELD(dropx, dropy))
12720 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12721 new_element = drop_element; /* default: no change when dropping */
12723 /* check if player is active, not moving and ready to drop */
12724 if (!player->active || player->MovPos || player->drop_delay > 0)
12727 /* check if player has anything that can be dropped */
12728 if (new_element == EL_UNDEFINED)
12731 /* check if drop key was pressed long enough for EM style dynamite */
12732 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12735 /* check if anything can be dropped at the current position */
12736 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12739 /* collected custom elements can only be dropped on empty fields */
12740 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12743 if (old_element != EL_EMPTY)
12744 Back[dropx][dropy] = old_element; /* store old element on this field */
12746 ResetGfxAnimation(dropx, dropy);
12747 ResetRandomAnimationValue(dropx, dropy);
12749 if (player->inventory_size > 0 ||
12750 player->inventory_infinite_element != EL_UNDEFINED)
12752 if (player->inventory_size > 0)
12754 player->inventory_size--;
12757 DrawGameDoorValues();
12759 DrawGameValue_Dynamite(local_player->inventory_size);
12762 if (new_element == EL_DYNAMITE)
12763 new_element = EL_DYNAMITE_ACTIVE;
12764 else if (new_element == EL_EM_DYNAMITE)
12765 new_element = EL_EM_DYNAMITE_ACTIVE;
12766 else if (new_element == EL_SP_DISK_RED)
12767 new_element = EL_SP_DISK_RED_ACTIVE;
12770 Feld[dropx][dropy] = new_element;
12772 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12773 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12774 el2img(Feld[dropx][dropy]), 0);
12776 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12778 /* needed if previous element just changed to "empty" in the last frame */
12779 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12781 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12782 player->index_bit, drop_side);
12783 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12785 player->index_bit, drop_side);
12787 TestIfElementTouchesCustomElement(dropx, dropy);
12789 else /* player is dropping a dyna bomb */
12791 player->dynabombs_left--;
12793 Feld[dropx][dropy] = new_element;
12795 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12796 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12797 el2img(Feld[dropx][dropy]), 0);
12799 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12802 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12803 InitField_WithBug1(dropx, dropy, FALSE);
12805 new_element = Feld[dropx][dropy]; /* element might have changed */
12807 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12808 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12810 int move_direction, nextx, nexty;
12812 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12813 MovDir[dropx][dropy] = drop_direction;
12815 move_direction = MovDir[dropx][dropy];
12816 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12817 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12819 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12820 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12823 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12824 player->is_dropping = TRUE;
12826 player->drop_pressed_delay = 0;
12827 player->is_dropping_pressed = FALSE;
12829 player->drop_x = dropx;
12830 player->drop_y = dropy;
12835 /* ------------------------------------------------------------------------- */
12836 /* game sound playing functions */
12837 /* ------------------------------------------------------------------------- */
12839 static int *loop_sound_frame = NULL;
12840 static int *loop_sound_volume = NULL;
12842 void InitPlayLevelSound()
12844 int num_sounds = getSoundListSize();
12846 checked_free(loop_sound_frame);
12847 checked_free(loop_sound_volume);
12849 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12850 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12853 static void PlayLevelSound(int x, int y, int nr)
12855 int sx = SCREENX(x), sy = SCREENY(y);
12856 int volume, stereo_position;
12857 int max_distance = 8;
12858 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12860 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12861 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12864 if (!IN_LEV_FIELD(x, y) ||
12865 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12866 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12869 volume = SOUND_MAX_VOLUME;
12871 if (!IN_SCR_FIELD(sx, sy))
12873 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12874 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12876 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12879 stereo_position = (SOUND_MAX_LEFT +
12880 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12881 (SCR_FIELDX + 2 * max_distance));
12883 if (IS_LOOP_SOUND(nr))
12885 /* This assures that quieter loop sounds do not overwrite louder ones,
12886 while restarting sound volume comparison with each new game frame. */
12888 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12891 loop_sound_volume[nr] = volume;
12892 loop_sound_frame[nr] = FrameCounter;
12895 PlaySoundExt(nr, volume, stereo_position, type);
12898 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12900 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12901 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12902 y < LEVELY(BY1) ? LEVELY(BY1) :
12903 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12907 static void PlayLevelSoundAction(int x, int y, int action)
12909 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12912 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12914 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12916 if (sound_effect != SND_UNDEFINED)
12917 PlayLevelSound(x, y, sound_effect);
12920 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12923 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12925 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12926 PlayLevelSound(x, y, sound_effect);
12929 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12931 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12933 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12934 PlayLevelSound(x, y, sound_effect);
12937 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12939 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12941 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12942 StopSound(sound_effect);
12945 static void PlayLevelMusic()
12947 if (levelset.music[level_nr] != MUS_UNDEFINED)
12948 PlayMusic(levelset.music[level_nr]); /* from config file */
12950 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12953 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12955 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12960 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12964 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12968 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12972 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12976 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12980 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12984 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12987 case SAMPLE_android_clone:
12988 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12991 case SAMPLE_android_move:
12992 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12995 case SAMPLE_spring:
12996 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13000 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13004 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13007 case SAMPLE_eater_eat:
13008 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13012 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13015 case SAMPLE_collect:
13016 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13019 case SAMPLE_diamond:
13020 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13023 case SAMPLE_squash:
13024 /* !!! CHECK THIS !!! */
13026 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13028 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13032 case SAMPLE_wonderfall:
13033 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13037 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13041 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13045 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13049 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13053 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13057 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13060 case SAMPLE_wonder:
13061 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13065 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13068 case SAMPLE_exit_open:
13069 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13072 case SAMPLE_exit_leave:
13073 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13076 case SAMPLE_dynamite:
13077 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13081 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13085 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13089 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13093 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13097 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13101 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13105 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13110 void RaiseScore(int value)
13112 local_player->score += value;
13114 DrawGameValue_Score(local_player->score);
13117 void RaiseScoreElement(int element)
13122 case EL_BD_DIAMOND:
13123 case EL_EMERALD_YELLOW:
13124 case EL_EMERALD_RED:
13125 case EL_EMERALD_PURPLE:
13126 case EL_SP_INFOTRON:
13127 RaiseScore(level.score[SC_EMERALD]);
13130 RaiseScore(level.score[SC_DIAMOND]);
13133 RaiseScore(level.score[SC_CRYSTAL]);
13136 RaiseScore(level.score[SC_PEARL]);
13139 case EL_BD_BUTTERFLY:
13140 case EL_SP_ELECTRON:
13141 RaiseScore(level.score[SC_BUG]);
13144 case EL_BD_FIREFLY:
13145 case EL_SP_SNIKSNAK:
13146 RaiseScore(level.score[SC_SPACESHIP]);
13149 case EL_DARK_YAMYAM:
13150 RaiseScore(level.score[SC_YAMYAM]);
13153 RaiseScore(level.score[SC_ROBOT]);
13156 RaiseScore(level.score[SC_PACMAN]);
13159 RaiseScore(level.score[SC_NUT]);
13162 case EL_EM_DYNAMITE:
13163 case EL_SP_DISK_RED:
13164 case EL_DYNABOMB_INCREASE_NUMBER:
13165 case EL_DYNABOMB_INCREASE_SIZE:
13166 case EL_DYNABOMB_INCREASE_POWER:
13167 RaiseScore(level.score[SC_DYNAMITE]);
13169 case EL_SHIELD_NORMAL:
13170 case EL_SHIELD_DEADLY:
13171 RaiseScore(level.score[SC_SHIELD]);
13173 case EL_EXTRA_TIME:
13174 RaiseScore(level.extra_time_score);
13188 RaiseScore(level.score[SC_KEY]);
13191 RaiseScore(element_info[element].collect_score);
13196 void RequestQuitGame(boolean ask_if_really_quit)
13198 if (AllPlayersGone ||
13199 !ask_if_really_quit ||
13200 level_editor_test_game ||
13201 Request("Do you really want to quit the game ?",
13202 REQ_ASK | REQ_STAY_CLOSED))
13204 #if defined(NETWORK_AVALIABLE)
13205 if (options.network)
13206 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13210 game_status = GAME_MODE_MAIN;
13216 if (tape.playing && tape.deactivate_display)
13217 TapeDeactivateDisplayOff(TRUE);
13219 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13221 if (tape.playing && tape.deactivate_display)
13222 TapeDeactivateDisplayOn();
13227 /* ---------- new game button stuff ---------------------------------------- */
13229 /* graphic position values for game buttons */
13230 #define GAME_BUTTON_XSIZE 30
13231 #define GAME_BUTTON_YSIZE 30
13232 #define GAME_BUTTON_XPOS 5
13233 #define GAME_BUTTON_YPOS 215
13234 #define SOUND_BUTTON_XPOS 5
13235 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13237 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13238 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13239 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13240 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13241 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13242 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13249 } gamebutton_info[NUM_GAME_BUTTONS] =
13252 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13257 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13258 GAME_CTRL_ID_PAUSE,
13262 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13267 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13268 SOUND_CTRL_ID_MUSIC,
13269 "background music on/off"
13272 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13273 SOUND_CTRL_ID_LOOPS,
13274 "sound loops on/off"
13277 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13278 SOUND_CTRL_ID_SIMPLE,
13279 "normal sounds on/off"
13283 void CreateGameButtons()
13287 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13289 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13290 struct GadgetInfo *gi;
13293 unsigned long event_mask;
13294 int gd_xoffset, gd_yoffset;
13295 int gd_x1, gd_x2, gd_y1, gd_y2;
13298 gd_xoffset = gamebutton_info[i].x;
13299 gd_yoffset = gamebutton_info[i].y;
13300 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13301 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13303 if (id == GAME_CTRL_ID_STOP ||
13304 id == GAME_CTRL_ID_PAUSE ||
13305 id == GAME_CTRL_ID_PLAY)
13307 button_type = GD_TYPE_NORMAL_BUTTON;
13309 event_mask = GD_EVENT_RELEASED;
13310 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13311 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13315 button_type = GD_TYPE_CHECK_BUTTON;
13317 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13318 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13319 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13320 event_mask = GD_EVENT_PRESSED;
13321 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13322 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13325 gi = CreateGadget(GDI_CUSTOM_ID, id,
13326 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13327 GDI_X, DX + gd_xoffset,
13328 GDI_Y, DY + gd_yoffset,
13329 GDI_WIDTH, GAME_BUTTON_XSIZE,
13330 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13331 GDI_TYPE, button_type,
13332 GDI_STATE, GD_BUTTON_UNPRESSED,
13333 GDI_CHECKED, checked,
13334 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13335 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13336 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13337 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13338 GDI_EVENT_MASK, event_mask,
13339 GDI_CALLBACK_ACTION, HandleGameButtons,
13343 Error(ERR_EXIT, "cannot create gadget");
13345 game_gadget[id] = gi;
13349 void FreeGameButtons()
13353 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13354 FreeGadget(game_gadget[i]);
13357 static void MapGameButtons()
13361 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13362 MapGadget(game_gadget[i]);
13365 void UnmapGameButtons()
13369 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13370 UnmapGadget(game_gadget[i]);
13373 static void HandleGameButtons(struct GadgetInfo *gi)
13375 int id = gi->custom_id;
13377 if (game_status != GAME_MODE_PLAYING)
13382 case GAME_CTRL_ID_STOP:
13386 RequestQuitGame(TRUE);
13389 case GAME_CTRL_ID_PAUSE:
13390 if (options.network)
13392 #if defined(NETWORK_AVALIABLE)
13394 SendToServer_ContinuePlaying();
13396 SendToServer_PausePlaying();
13400 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13403 case GAME_CTRL_ID_PLAY:
13406 #if defined(NETWORK_AVALIABLE)
13407 if (options.network)
13408 SendToServer_ContinuePlaying();
13412 tape.pausing = FALSE;
13413 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13418 case SOUND_CTRL_ID_MUSIC:
13419 if (setup.sound_music)
13421 setup.sound_music = FALSE;
13424 else if (audio.music_available)
13426 setup.sound = setup.sound_music = TRUE;
13428 SetAudioMode(setup.sound);
13434 case SOUND_CTRL_ID_LOOPS:
13435 if (setup.sound_loops)
13436 setup.sound_loops = FALSE;
13437 else if (audio.loops_available)
13439 setup.sound = setup.sound_loops = TRUE;
13440 SetAudioMode(setup.sound);
13444 case SOUND_CTRL_ID_SIMPLE:
13445 if (setup.sound_simple)
13446 setup.sound_simple = FALSE;
13447 else if (audio.sound_available)
13449 setup.sound = setup.sound_simple = TRUE;
13450 SetAudioMode(setup.sound);