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 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF (TRUE * 1)
33 #define USE_NEW_MOVE_STYLE (TRUE * USE_NEW_STUFF * 1)
34 #define USE_NEW_MOVE_DELAY (TRUE * USE_NEW_STUFF * 1)
35 #define USE_NEW_PUSH_DELAY (TRUE * USE_NEW_STUFF * 1)
36 #define USE_NEW_BLOCK_STYLE (TRUE * USE_NEW_STUFF * 1)
37 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
38 #define USE_NEW_RANDOMIZE (TRUE * USE_NEW_STUFF * 1)
40 #define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
41 #define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
43 #define USE_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
45 #define USE_BLOCK_DELAY_BUGFIX (TRUE * USE_NEW_STUFF * 1)
47 #define USE_GRAVITY_BUGFIX_NEW (TRUE * USE_NEW_STUFF * 1)
48 #define USE_GRAVITY_BUGFIX_OLD (TRUE * USE_NEW_STUFF * 0)
50 #define USE_PENGUIN_COLLECT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
52 #define USE_IMPACT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
54 #define USE_HITTING_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
55 #define USE_HIT_BY_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
57 #define USE_DROP_BUGFIX (TRUE * USE_NEW_STUFF * 1)
59 #define USE_CHANGE_TO_TRIGGERED (TRUE * USE_NEW_STUFF * 1)
67 /* for MovePlayer() */
68 #define MF_NO_ACTION 0
72 /* for ScrollPlayer() */
74 #define SCROLL_GO_ON 1
77 #define EX_PHASE_START 0
78 #define EX_TYPE_NONE 0
79 #define EX_TYPE_NORMAL (1 << 0)
80 #define EX_TYPE_CENTER (1 << 1)
81 #define EX_TYPE_BORDER (1 << 2)
82 #define EX_TYPE_CROSS (1 << 3)
83 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
85 /* special positions in the game control window (relative to control window) */
88 #define XX_EMERALDS 29
89 #define YY_EMERALDS 54
90 #define XX_DYNAMITE 29
91 #define YY_DYNAMITE 89
100 /* special positions in the game control window (relative to main window) */
101 #define DX_LEVEL (DX + XX_LEVEL)
102 #define DY_LEVEL (DY + YY_LEVEL)
103 #define DX_EMERALDS (DX + XX_EMERALDS)
104 #define DY_EMERALDS (DY + YY_EMERALDS)
105 #define DX_DYNAMITE (DX + XX_DYNAMITE)
106 #define DY_DYNAMITE (DY + YY_DYNAMITE)
107 #define DX_KEYS (DX + XX_KEYS)
108 #define DY_KEYS (DY + YY_KEYS)
109 #define DX_SCORE (DX + XX_SCORE)
110 #define DY_SCORE (DY + YY_SCORE)
111 #define DX_TIME1 (DX + XX_TIME1)
112 #define DX_TIME2 (DX + XX_TIME2)
113 #define DY_TIME (DY + YY_TIME)
115 /* values for initial player move delay (initial delay counter value) */
116 #define INITIAL_MOVE_DELAY_OFF -1
117 #define INITIAL_MOVE_DELAY_ON 0
119 /* values for player movement speed (which is in fact a delay value) */
120 #define MOVE_DELAY_NORMAL_SPEED 8
121 #define MOVE_DELAY_HIGH_SPEED 4
123 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
124 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
125 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
126 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
128 /* values for other actions */
129 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
131 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
132 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
134 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
136 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
137 RND(element_info[e].push_delay_random))
138 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
139 RND(element_info[e].drop_delay_random))
140 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
141 RND(element_info[e].move_delay_random))
142 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
143 (element_info[e].move_delay_random))
145 #define GET_TARGET_ELEMENT(e, ch) \
146 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
147 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
149 #define GET_VALID_PLAYER_ELEMENT(e) \
150 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
152 #define CAN_GROW_INTO(e) \
153 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
155 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
159 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
160 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
161 (CAN_MOVE_INTO_ACID(e) && \
162 Feld[x][y] == EL_ACID) || \
165 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
166 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
167 (CAN_MOVE_INTO_ACID(e) && \
168 Feld[x][y] == EL_ACID) || \
171 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
172 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
176 (DONT_COLLIDE_WITH(e) && \
178 !PLAYER_ENEMY_PROTECTED(x, y))))
181 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
182 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
184 (DONT_COLLIDE_WITH(e) && \
186 !PLAYER_ENEMY_PROTECTED(x, y))))
189 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
193 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
196 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
201 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
204 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
215 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
223 IS_FOOD_PENGUIN(Feld[x][y])))
224 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
230 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
235 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
236 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
237 (CAN_MOVE_INTO_ACID(e) && \
238 Feld[x][y] == EL_ACID) || \
239 Feld[x][y] == EL_DIAMOND))
241 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
242 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
243 (CAN_MOVE_INTO_ACID(e) && \
244 Feld[x][y] == EL_ACID) || \
245 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
247 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
248 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
249 (CAN_MOVE_INTO_ACID(e) && \
250 Feld[x][y] == EL_ACID) || \
251 IS_AMOEBOID(Feld[x][y])))
253 #define PIG_CAN_ENTER_FIELD(e, x, y) \
254 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
255 (CAN_MOVE_INTO_ACID(e) && \
256 Feld[x][y] == EL_ACID) || \
257 IS_FOOD_PIG(Feld[x][y])))
259 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
260 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
261 (CAN_MOVE_INTO_ACID(e) && \
262 Feld[x][y] == EL_ACID) || \
263 IS_FOOD_PENGUIN(Feld[x][y]) || \
264 Feld[x][y] == EL_EXIT_OPEN))
266 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
267 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
268 (CAN_MOVE_INTO_ACID(e) && \
269 Feld[x][y] == EL_ACID)))
271 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
272 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
273 (CAN_MOVE_INTO_ACID(e) && \
274 Feld[x][y] == EL_ACID) || \
277 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
278 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
279 (CAN_MOVE_INTO_ACID(e) && \
280 Feld[x][y] == EL_ACID)))
284 #define GROUP_NR(e) ((e) - EL_GROUP_START)
285 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
286 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
287 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
289 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
290 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
293 #define CE_ENTER_FIELD_COND(e, x, y) \
294 (!IS_PLAYER(x, y) && \
295 (Feld[x][y] == EL_ACID || \
296 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
298 #define CE_ENTER_FIELD_COND(e, x, y) \
299 (!IS_PLAYER(x, y) && \
300 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
303 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
304 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
306 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
307 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
309 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
310 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
311 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
312 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
314 /* game button identifiers */
315 #define GAME_CTRL_ID_STOP 0
316 #define GAME_CTRL_ID_PAUSE 1
317 #define GAME_CTRL_ID_PLAY 2
318 #define SOUND_CTRL_ID_MUSIC 3
319 #define SOUND_CTRL_ID_LOOPS 4
320 #define SOUND_CTRL_ID_SIMPLE 5
322 #define NUM_GAME_BUTTONS 6
325 /* forward declaration for internal use */
327 static void AdvanceFrameAndPlayerCounters(int);
329 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
330 static boolean MovePlayer(struct PlayerInfo *, int, int);
331 static void ScrollPlayer(struct PlayerInfo *, int);
332 static void ScrollScreen(struct PlayerInfo *, int);
334 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
336 static void InitBeltMovement(void);
337 static void CloseAllOpenTimegates(void);
338 static void CheckGravityMovement(struct PlayerInfo *);
339 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
340 static void KillHeroUnlessEnemyProtected(int, int);
341 static void KillHeroUnlessExplosionProtected(int, int);
343 static void TestIfPlayerTouchesCustomElement(int, int);
344 static void TestIfElementTouchesCustomElement(int, int);
345 static void TestIfElementHitsCustomElement(int, int, int);
347 static void TestIfElementSmashesCustomElement(int, int, int);
350 static void ChangeElement(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, \
356 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
357 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
358 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
359 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
360 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
361 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
364 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
365 #define CheckElementChange(x, y, e, te, ev) \
366 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
367 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
368 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
369 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
370 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
371 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
372 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
374 static void PlayLevelSound(int, int, int);
375 static void PlayLevelSoundNearest(int, int, int);
376 static void PlayLevelSoundAction(int, int, int);
377 static void PlayLevelSoundElementAction(int, int, int, int);
378 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
379 static void PlayLevelSoundActionIfLoop(int, int, int);
380 static void StopLevelSoundActionIfLoop(int, int, int);
381 static void PlayLevelMusic();
383 static void MapGameButtons();
384 static void HandleGameButtons(struct GadgetInfo *);
386 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
389 /* ------------------------------------------------------------------------- */
390 /* definition of elements that automatically change to other elements after */
391 /* a specified time, eventually calling a function when changing */
392 /* ------------------------------------------------------------------------- */
394 /* forward declaration for changer functions */
395 static void InitBuggyBase(int x, int y);
396 static void WarnBuggyBase(int x, int y);
398 static void InitTrap(int x, int y);
399 static void ActivateTrap(int x, int y);
400 static void ChangeActiveTrap(int x, int y);
402 static void InitRobotWheel(int x, int y);
403 static void RunRobotWheel(int x, int y);
404 static void StopRobotWheel(int x, int y);
406 static void InitTimegateWheel(int x, int y);
407 static void RunTimegateWheel(int x, int y);
409 struct ChangingElementInfo
414 void (*pre_change_function)(int x, int y);
415 void (*change_function)(int x, int y);
416 void (*post_change_function)(int x, int y);
419 static struct ChangingElementInfo change_delay_list[] =
470 EL_SWITCHGATE_OPENING,
478 EL_SWITCHGATE_CLOSING,
479 EL_SWITCHGATE_CLOSED,
511 EL_ACID_SPLASH_RIGHT,
520 EL_SP_BUGGY_BASE_ACTIVATING,
527 EL_SP_BUGGY_BASE_ACTIVATING,
528 EL_SP_BUGGY_BASE_ACTIVE,
535 EL_SP_BUGGY_BASE_ACTIVE,
559 EL_ROBOT_WHEEL_ACTIVE,
567 EL_TIMEGATE_SWITCH_ACTIVE,
588 int push_delay_fixed, push_delay_random;
593 { EL_BALLOON, 0, 0 },
595 { EL_SOKOBAN_OBJECT, 2, 0 },
596 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
597 { EL_SATELLITE, 2, 0 },
598 { EL_SP_DISK_YELLOW, 2, 0 },
600 { EL_UNDEFINED, 0, 0 },
608 move_stepsize_list[] =
610 { EL_AMOEBA_DROP, 2 },
611 { EL_AMOEBA_DROPPING, 2 },
612 { EL_QUICKSAND_FILLING, 1 },
613 { EL_QUICKSAND_EMPTYING, 1 },
614 { EL_MAGIC_WALL_FILLING, 2 },
615 { EL_BD_MAGIC_WALL_FILLING, 2 },
616 { EL_MAGIC_WALL_EMPTYING, 2 },
617 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
627 collect_count_list[] =
630 { EL_BD_DIAMOND, 1 },
631 { EL_EMERALD_YELLOW, 1 },
632 { EL_EMERALD_RED, 1 },
633 { EL_EMERALD_PURPLE, 1 },
635 { EL_SP_INFOTRON, 1 },
647 access_direction_list[] =
649 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
650 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
651 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
652 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
653 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
654 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
655 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
656 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
657 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
658 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
659 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
661 { EL_SP_PORT_LEFT, MV_RIGHT },
662 { EL_SP_PORT_RIGHT, MV_LEFT },
663 { EL_SP_PORT_UP, MV_DOWN },
664 { EL_SP_PORT_DOWN, MV_UP },
665 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
666 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
667 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
668 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
669 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
670 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
671 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
672 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
673 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
674 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
675 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
676 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
677 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
678 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
679 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
681 { EL_UNDEFINED, MV_NO_MOVING }
684 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
686 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
687 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
688 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
689 IS_JUST_CHANGING(x, y))
691 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
694 void GetPlayerConfig()
696 if (!audio.sound_available)
697 setup.sound_simple = FALSE;
699 if (!audio.loops_available)
700 setup.sound_loops = FALSE;
702 if (!audio.music_available)
703 setup.sound_music = FALSE;
705 if (!video.fullscreen_available)
706 setup.fullscreen = FALSE;
708 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
710 SetAudioMode(setup.sound);
714 static int getBeltNrFromBeltElement(int element)
716 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
717 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
718 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
721 static int getBeltNrFromBeltActiveElement(int element)
723 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
724 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
725 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
728 static int getBeltNrFromBeltSwitchElement(int element)
730 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
731 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
732 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
735 static int getBeltDirNrFromBeltSwitchElement(int element)
737 static int belt_base_element[4] =
739 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
740 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
741 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
742 EL_CONVEYOR_BELT_4_SWITCH_LEFT
745 int belt_nr = getBeltNrFromBeltSwitchElement(element);
746 int belt_dir_nr = element - belt_base_element[belt_nr];
748 return (belt_dir_nr % 3);
751 static int getBeltDirFromBeltSwitchElement(int element)
753 static int belt_move_dir[3] =
760 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
762 return belt_move_dir[belt_dir_nr];
765 static void InitPlayerField(int x, int y, int element, boolean init_game)
767 if (element == EL_SP_MURPHY)
771 if (stored_player[0].present)
773 Feld[x][y] = EL_SP_MURPHY_CLONE;
779 stored_player[0].use_murphy_graphic = TRUE;
782 Feld[x][y] = EL_PLAYER_1;
788 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
789 int jx = player->jx, jy = player->jy;
791 player->present = TRUE;
793 player->block_last_field = (element == EL_SP_MURPHY ?
794 level.sp_block_last_field :
795 level.block_last_field);
797 #if USE_NEW_BLOCK_STYLE
800 /* ---------- initialize player's last field block delay --------------- */
802 /* always start with reliable default value (no adjustment needed) */
803 player->block_delay_adjustment = 0;
805 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
806 if (player->block_last_field && element == EL_SP_MURPHY)
807 player->block_delay_adjustment = 1;
809 /* special case 2: in game engines before 3.1.1, blocking was different */
810 if (game.use_block_last_field_bug)
811 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
814 /* blocking the last field when moving was corrected in version 3.1.1 */
815 if (game.use_block_last_field_bug)
817 /* even "not blocking" was blocking the last field for one frame */
818 level.block_delay = (level.block_last_field ? 7 : 1);
819 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
821 level.block_last_field = TRUE;
822 level.sp_block_last_field = TRUE;
826 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
827 level.block_delay = 8; /* when blocking, block 8 frames */
828 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
832 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
838 player->block_delay = (player->block_last_field ?
839 (element == EL_SP_MURPHY ?
840 level.sp_block_delay :
841 level.block_delay) : 0);
843 player->block_delay = (element == EL_SP_MURPHY ?
844 (player->block_last_field ? 7 : 1) :
845 (player->block_last_field ? 7 : 1));
851 printf("::: block_last_field == %d, block_delay = %d\n",
852 player->block_last_field, player->block_delay);
856 if (!options.network || player->connected)
858 player->active = TRUE;
860 /* remove potentially duplicate players */
861 if (StorePlayer[jx][jy] == Feld[x][y])
862 StorePlayer[jx][jy] = 0;
864 StorePlayer[x][y] = Feld[x][y];
868 printf("Player %d activated.\n", player->element_nr);
869 printf("[Local player is %d and currently %s.]\n",
870 local_player->element_nr,
871 local_player->active ? "active" : "not active");
875 Feld[x][y] = EL_EMPTY;
877 player->jx = player->last_jx = x;
878 player->jy = player->last_jy = y;
882 static void InitField(int x, int y, boolean init_game)
884 int element = Feld[x][y];
893 InitPlayerField(x, y, element, init_game);
896 case EL_SOKOBAN_FIELD_PLAYER:
897 element = Feld[x][y] = EL_PLAYER_1;
898 InitField(x, y, init_game);
900 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
901 InitField(x, y, init_game);
904 case EL_SOKOBAN_FIELD_EMPTY:
905 local_player->sokobanfields_still_needed++;
909 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
910 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
911 else if (x > 0 && Feld[x-1][y] == EL_ACID)
912 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
913 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
914 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
915 else if (y > 0 && Feld[x][y-1] == EL_ACID)
916 Feld[x][y] = EL_ACID_POOL_BOTTOM;
917 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
918 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
926 case EL_SPACESHIP_RIGHT:
927 case EL_SPACESHIP_UP:
928 case EL_SPACESHIP_LEFT:
929 case EL_SPACESHIP_DOWN:
931 case EL_BD_BUTTERFLY_RIGHT:
932 case EL_BD_BUTTERFLY_UP:
933 case EL_BD_BUTTERFLY_LEFT:
934 case EL_BD_BUTTERFLY_DOWN:
935 case EL_BD_BUTTERFLY:
936 case EL_BD_FIREFLY_RIGHT:
937 case EL_BD_FIREFLY_UP:
938 case EL_BD_FIREFLY_LEFT:
939 case EL_BD_FIREFLY_DOWN:
941 case EL_PACMAN_RIGHT:
965 if (y == lev_fieldy - 1)
967 Feld[x][y] = EL_AMOEBA_GROWING;
968 Store[x][y] = EL_AMOEBA_WET;
972 case EL_DYNAMITE_ACTIVE:
973 case EL_SP_DISK_RED_ACTIVE:
974 case EL_DYNABOMB_PLAYER_1_ACTIVE:
975 case EL_DYNABOMB_PLAYER_2_ACTIVE:
976 case EL_DYNABOMB_PLAYER_3_ACTIVE:
977 case EL_DYNABOMB_PLAYER_4_ACTIVE:
982 local_player->lights_still_needed++;
986 local_player->friends_still_needed++;
991 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
996 Feld[x][y] = EL_EMPTY;
1001 case EL_EM_KEY_1_FILE:
1002 Feld[x][y] = EL_EM_KEY_1;
1004 case EL_EM_KEY_2_FILE:
1005 Feld[x][y] = EL_EM_KEY_2;
1007 case EL_EM_KEY_3_FILE:
1008 Feld[x][y] = EL_EM_KEY_3;
1010 case EL_EM_KEY_4_FILE:
1011 Feld[x][y] = EL_EM_KEY_4;
1015 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1016 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1017 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1018 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1019 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1020 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1021 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1022 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1023 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1024 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1025 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1026 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1029 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1030 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1031 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1033 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1035 game.belt_dir[belt_nr] = belt_dir;
1036 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1038 else /* more than one switch -- set it like the first switch */
1040 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1045 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1047 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1050 case EL_LIGHT_SWITCH_ACTIVE:
1052 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1056 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1058 else if (IS_GROUP_ELEMENT(element))
1060 struct ElementGroupInfo *group = element_info[element].group;
1061 int last_anim_random_frame = gfx.anim_random_frame;
1064 if (group->choice_mode == ANIM_RANDOM)
1065 gfx.anim_random_frame = RND(group->num_elements_resolved);
1067 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1068 group->choice_mode, 0,
1071 if (group->choice_mode == ANIM_RANDOM)
1072 gfx.anim_random_frame = last_anim_random_frame;
1074 group->choice_pos++;
1076 Feld[x][y] = group->element_resolved[element_pos];
1078 InitField(x, y, init_game);
1084 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1086 InitField(x, y, init_game);
1088 /* not needed to call InitMovDir() -- already done by InitField()! */
1089 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1090 CAN_MOVE(Feld[x][y]))
1094 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1096 int old_element = Feld[x][y];
1098 InitField(x, y, init_game);
1100 /* not needed to call InitMovDir() -- already done by InitField()! */
1101 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1102 CAN_MOVE(old_element) &&
1103 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1106 /* this case is in fact a combination of not less than three bugs:
1107 first, it calls InitMovDir() for elements that can move, although this is
1108 already done by InitField(); then, it checks the element that was at this
1109 field _before_ the call to InitField() (which can change it); lastly, it
1110 was not called for "mole with direction" elements, which were treated as
1111 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1115 inline void DrawGameValue_Emeralds(int value)
1117 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1120 inline void DrawGameValue_Dynamite(int value)
1122 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1125 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1129 /* currently only 4 of 8 possible keys are displayed */
1130 for (i = 0; i < STD_NUM_KEYS; i++)
1132 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1133 el2edimg(EL_KEY_1 + i));
1136 inline void DrawGameValue_Score(int value)
1138 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1141 inline void DrawGameValue_Time(int value)
1144 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1146 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1149 inline void DrawGameValue_Level(int value)
1152 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1155 /* misuse area for displaying emeralds to draw bigger level number */
1156 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1157 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1159 /* now copy it to the area for displaying level number */
1160 BlitBitmap(drawto, drawto,
1161 DX_EMERALDS, DY_EMERALDS + 1,
1162 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1163 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1164 DX_LEVEL - 1, DY_LEVEL + 1);
1166 /* restore the area for displaying emeralds */
1167 DrawGameValue_Emeralds(local_player->gems_still_needed);
1169 /* yes, this is all really ugly :-) */
1173 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1176 int key[MAX_NUM_KEYS];
1179 for (i = 0; i < MAX_NUM_KEYS; i++)
1180 key[i] = key_bits & (1 << i);
1182 DrawGameValue_Level(level_nr);
1184 DrawGameValue_Emeralds(emeralds);
1185 DrawGameValue_Dynamite(dynamite);
1186 DrawGameValue_Score(score);
1187 DrawGameValue_Time(time);
1189 DrawGameValue_Keys(key);
1192 void DrawGameDoorValues()
1196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1198 DrawGameDoorValues_EM();
1203 DrawGameValue_Level(level_nr);
1205 DrawGameValue_Emeralds(local_player->gems_still_needed);
1206 DrawGameValue_Dynamite(local_player->inventory_size);
1207 DrawGameValue_Score(local_player->score);
1208 DrawGameValue_Time(TimeLeft);
1210 for (i = 0; i < MAX_PLAYERS; i++)
1211 DrawGameValue_Keys(stored_player[i].key);
1214 static void resolve_group_element(int group_element, int recursion_depth)
1216 static int group_nr;
1217 static struct ElementGroupInfo *group;
1218 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1221 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1223 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1224 group_element - EL_GROUP_START + 1);
1226 /* replace element which caused too deep recursion by question mark */
1227 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1232 if (recursion_depth == 0) /* initialization */
1234 group = element_info[group_element].group;
1235 group_nr = group_element - EL_GROUP_START;
1237 group->num_elements_resolved = 0;
1238 group->choice_pos = 0;
1241 for (i = 0; i < actual_group->num_elements; i++)
1243 int element = actual_group->element[i];
1245 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1248 if (IS_GROUP_ELEMENT(element))
1249 resolve_group_element(element, recursion_depth + 1);
1252 group->element_resolved[group->num_elements_resolved++] = element;
1253 element_info[element].in_group[group_nr] = TRUE;
1258 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1260 printf("::: group %d: %d resolved elements\n",
1261 group_element - EL_GROUP_START, group->num_elements_resolved);
1262 for (i = 0; i < group->num_elements_resolved; i++)
1263 printf("::: - %d ['%s']\n", group->element_resolved[i],
1264 element_info[group->element_resolved[i]].token_name);
1271 =============================================================================
1273 -----------------------------------------------------------------------------
1274 initialize game engine due to level / tape version number
1275 =============================================================================
1278 static void InitGameEngine()
1282 /* set game engine from tape file when re-playing, else from level file */
1283 game.engine_version = (tape.playing ? tape.engine_version :
1284 level.game_version);
1286 /* ---------------------------------------------------------------------- */
1287 /* set flags for bugs and changes according to active game engine version */
1288 /* ---------------------------------------------------------------------- */
1291 Summary of bugfix/change:
1292 Fixed handling for custom elements that change when pushed by the player.
1294 Fixed/changed in version:
1298 Before 3.1.0, custom elements that "change when pushing" changed directly
1299 after the player started pushing them (until then handled in "DigField()").
1300 Since 3.1.0, these custom elements are not changed until the "pushing"
1301 move of the element is finished (now handled in "ContinueMoving()").
1303 Affected levels/tapes:
1304 The first condition is generally needed for all levels/tapes before version
1305 3.1.0, which might use the old behaviour before it was changed; known tapes
1306 that are affected are some tapes from the level set "Walpurgis Gardens" by
1308 The second condition is an exception from the above case and is needed for
1309 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1310 above (including some development versions of 3.1.0), but before it was
1311 known that this change would break tapes like the above and was fixed in
1312 3.1.1, so that the changed behaviour was active although the engine version
1313 while recording maybe was before 3.1.0. There is at least one tape that is
1314 affected by this exception, which is the tape for the one-level set "Bug
1315 Machine" by Juergen Bonhagen.
1318 game.use_change_when_pushing_bug =
1319 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1321 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1322 tape.game_version < VERSION_IDENT(3,1,1,0)));
1325 Summary of bugfix/change:
1326 Fixed handling for blocking the field the player leaves when moving.
1328 Fixed/changed in version:
1332 Before 3.1.1, when "block last field when moving" was enabled, the field
1333 the player is leaving when moving was blocked for the time of the move,
1334 and was directly unblocked afterwards. This resulted in the last field
1335 being blocked for exactly one less than the number of frames of one player
1336 move. Additionally, even when blocking was disabled, the last field was
1337 blocked for exactly one frame.
1338 Since 3.1.1, due to changes in player movement handling, the last field
1339 is not blocked at all when blocking is disabled. When blocking is enabled,
1340 the last field is blocked for exactly the number of frames of one player
1341 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1342 last field is blocked for exactly one more than the number of frames of
1345 Affected levels/tapes:
1346 (!!! yet to be determined -- probably many !!!)
1349 game.use_block_last_field_bug =
1350 (game.engine_version < VERSION_IDENT(3,1,1,0));
1352 /* ---------------------------------------------------------------------- */
1354 /* dynamically adjust element properties according to game engine version */
1355 InitElementPropertiesEngine(game.engine_version);
1358 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1359 printf(" tape version == %06d [%s] [file: %06d]\n",
1360 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1362 printf(" => game.engine_version == %06d\n", game.engine_version);
1365 /* ---------- recursively resolve group elements ------------------------- */
1367 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1368 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1369 element_info[i].in_group[j] = FALSE;
1371 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1372 resolve_group_element(EL_GROUP_START + i, 0);
1374 /* ---------- initialize player's initial move delay --------------------- */
1376 #if USE_NEW_MOVE_DELAY
1377 /* dynamically adjust player properties according to level information */
1378 game.initial_move_delay_value =
1379 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1381 /* dynamically adjust player properties according to game engine version */
1382 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1383 game.initial_move_delay_value : 0);
1385 /* dynamically adjust player properties according to game engine version */
1386 game.initial_move_delay =
1387 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1388 INITIAL_MOVE_DELAY_OFF);
1390 /* dynamically adjust player properties according to level information */
1391 game.initial_move_delay_value =
1392 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1395 /* ---------- initialize player's initial push delay --------------------- */
1397 /* dynamically adjust player properties according to game engine version */
1398 game.initial_push_delay_value =
1399 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1401 /* ---------- initialize changing elements ------------------------------- */
1403 /* initialize changing elements information */
1404 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1406 struct ElementInfo *ei = &element_info[i];
1408 /* this pointer might have been changed in the level editor */
1409 ei->change = &ei->change_page[0];
1411 if (!IS_CUSTOM_ELEMENT(i))
1413 ei->change->target_element = EL_EMPTY_SPACE;
1414 ei->change->delay_fixed = 0;
1415 ei->change->delay_random = 0;
1416 ei->change->delay_frames = 1;
1419 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1421 ei->has_change_event[j] = FALSE;
1423 ei->event_page_nr[j] = 0;
1424 ei->event_page[j] = &ei->change_page[0];
1428 /* add changing elements from pre-defined list */
1429 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1431 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1432 struct ElementInfo *ei = &element_info[ch_delay->element];
1434 ei->change->target_element = ch_delay->target_element;
1435 ei->change->delay_fixed = ch_delay->change_delay;
1437 ei->change->pre_change_function = ch_delay->pre_change_function;
1438 ei->change->change_function = ch_delay->change_function;
1439 ei->change->post_change_function = ch_delay->post_change_function;
1441 ei->has_change_event[CE_DELAY] = TRUE;
1444 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1449 /* add change events from custom element configuration */
1450 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1452 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1454 for (j = 0; j < ei->num_change_pages; j++)
1456 if (!ei->change_page[j].can_change)
1459 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1461 /* only add event page for the first page found with this event */
1462 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1464 ei->has_change_event[k] = TRUE;
1466 ei->event_page_nr[k] = j;
1467 ei->event_page[k] = &ei->change_page[j];
1475 /* add change events from custom element configuration */
1476 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1478 int element = EL_CUSTOM_START + i;
1480 /* only add custom elements that change after fixed/random frame delay */
1481 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1482 element_info[element].has_change_event[CE_DELAY] = TRUE;
1486 /* ---------- initialize run-time trigger player and element ------------- */
1488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1490 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1492 for (j = 0; j < ei->num_change_pages; j++)
1494 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1495 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1499 /* ---------- initialize trigger events ---------------------------------- */
1501 /* initialize trigger events information */
1502 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1503 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1504 trigger_events[i][j] = FALSE;
1507 /* add trigger events from element change event properties */
1508 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1510 struct ElementInfo *ei = &element_info[i];
1512 for (j = 0; j < ei->num_change_pages; j++)
1514 if (!ei->change_page[j].can_change)
1517 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1519 int trigger_element = ei->change_page[j].trigger_element;
1521 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1523 if (ei->change_page[j].has_event[k])
1525 if (IS_GROUP_ELEMENT(trigger_element))
1527 struct ElementGroupInfo *group =
1528 element_info[trigger_element].group;
1530 for (l = 0; l < group->num_elements_resolved; l++)
1531 trigger_events[group->element_resolved[l]][k] = TRUE;
1534 trigger_events[trigger_element][k] = TRUE;
1541 /* add trigger events from element change event properties */
1542 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1543 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1544 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1545 if (element_info[i].change->has_event[j])
1546 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1549 /* ---------- initialize push delay -------------------------------------- */
1551 /* initialize push delay values to default */
1552 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1554 if (!IS_CUSTOM_ELEMENT(i))
1556 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1557 element_info[i].push_delay_random = game.default_push_delay_random;
1561 /* set push delay value for certain elements from pre-defined list */
1562 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1564 int e = push_delay_list[i].element;
1566 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1567 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1570 /* set push delay value for Supaplex elements for newer engine versions */
1571 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1573 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1575 if (IS_SP_ELEMENT(i))
1577 #if USE_NEW_MOVE_STYLE
1578 /* set SP push delay to just enough to push under a falling zonk */
1579 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1581 element_info[i].push_delay_fixed = delay;
1582 element_info[i].push_delay_random = 0;
1584 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1585 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1591 /* ---------- initialize move stepsize ----------------------------------- */
1593 /* initialize move stepsize values to default */
1594 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1595 if (!IS_CUSTOM_ELEMENT(i))
1596 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1598 /* set move stepsize value for certain elements from pre-defined list */
1599 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1601 int e = move_stepsize_list[i].element;
1603 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1607 /* ---------- initialize move dig/leave ---------------------------------- */
1609 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1611 element_info[i].can_leave_element = FALSE;
1612 element_info[i].can_leave_element_last = FALSE;
1616 /* ---------- initialize gem count --------------------------------------- */
1618 /* initialize gem count values for each element */
1619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 if (!IS_CUSTOM_ELEMENT(i))
1621 element_info[i].collect_count = 0;
1623 /* add gem count values for all elements from pre-defined list */
1624 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1625 element_info[collect_count_list[i].element].collect_count =
1626 collect_count_list[i].count;
1628 /* ---------- initialize access direction -------------------------------- */
1630 /* initialize access direction values to default (access from every side) */
1631 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1632 if (!IS_CUSTOM_ELEMENT(i))
1633 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1635 /* set access direction value for certain elements from pre-defined list */
1636 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1637 element_info[access_direction_list[i].element].access_direction =
1638 access_direction_list[i].direction;
1643 =============================================================================
1645 -----------------------------------------------------------------------------
1646 initialize and start new game
1647 =============================================================================
1652 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1653 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1654 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1661 #if USE_NEW_AMOEBA_CODE
1662 printf("Using new amoeba code.\n");
1664 printf("Using old amoeba code.\n");
1669 /* don't play tapes over network */
1670 network_playing = (options.network && !tape.playing);
1672 for (i = 0; i < MAX_PLAYERS; i++)
1674 struct PlayerInfo *player = &stored_player[i];
1676 player->index_nr = i;
1677 player->index_bit = (1 << i);
1678 player->element_nr = EL_PLAYER_1 + i;
1680 player->present = FALSE;
1681 player->active = FALSE;
1684 player->effective_action = 0;
1685 player->programmed_action = 0;
1688 player->gems_still_needed = level.gems_needed;
1689 player->sokobanfields_still_needed = 0;
1690 player->lights_still_needed = 0;
1691 player->friends_still_needed = 0;
1693 for (j = 0; j < MAX_NUM_KEYS; j++)
1694 player->key[j] = FALSE;
1696 player->dynabomb_count = 0;
1697 player->dynabomb_size = 1;
1698 player->dynabombs_left = 0;
1699 player->dynabomb_xl = FALSE;
1701 player->MovDir = MV_NO_MOVING;
1704 player->GfxDir = MV_NO_MOVING;
1705 player->GfxAction = ACTION_DEFAULT;
1707 player->StepFrame = 0;
1709 player->use_murphy_graphic = FALSE;
1711 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1712 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1714 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1716 player->actual_frame_counter = 0;
1718 player->step_counter = 0;
1720 player->last_move_dir = MV_NO_MOVING;
1722 player->is_waiting = FALSE;
1723 player->is_moving = FALSE;
1724 player->is_auto_moving = FALSE;
1725 player->is_digging = FALSE;
1726 player->is_snapping = FALSE;
1727 player->is_collecting = FALSE;
1728 player->is_pushing = FALSE;
1729 player->is_switching = FALSE;
1730 player->is_dropping = FALSE;
1732 player->is_bored = FALSE;
1733 player->is_sleeping = FALSE;
1735 player->frame_counter_bored = -1;
1736 player->frame_counter_sleeping = -1;
1738 player->anim_delay_counter = 0;
1739 player->post_delay_counter = 0;
1741 player->action_waiting = ACTION_DEFAULT;
1742 player->last_action_waiting = ACTION_DEFAULT;
1743 player->special_action_bored = ACTION_DEFAULT;
1744 player->special_action_sleeping = ACTION_DEFAULT;
1746 player->num_special_action_bored = 0;
1747 player->num_special_action_sleeping = 0;
1749 /* determine number of special actions for bored and sleeping animation */
1750 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1752 boolean found = FALSE;
1754 for (k = 0; k < NUM_DIRECTIONS; k++)
1755 if (el_act_dir2img(player->element_nr, j, k) !=
1756 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1760 player->num_special_action_bored++;
1764 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1766 boolean found = FALSE;
1768 for (k = 0; k < NUM_DIRECTIONS; k++)
1769 if (el_act_dir2img(player->element_nr, j, k) !=
1770 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1774 player->num_special_action_sleeping++;
1779 player->switch_x = -1;
1780 player->switch_y = -1;
1783 player->drop_x = -1;
1784 player->drop_y = -1;
1787 player->show_envelope = 0;
1789 player->move_delay = game.initial_move_delay;
1790 player->move_delay_value = game.initial_move_delay_value;
1792 player->move_delay_reset_counter = 0;
1794 #if USE_NEW_PUSH_DELAY
1795 player->push_delay = -1; /* initialized when pushing starts */
1796 player->push_delay_value = game.initial_push_delay_value;
1798 player->push_delay = 0;
1799 player->push_delay_value = game.initial_push_delay_value;
1802 player->drop_delay = 0;
1804 player->last_jx = player->last_jy = 0;
1805 player->jx = player->jy = 0;
1807 player->shield_normal_time_left = 0;
1808 player->shield_deadly_time_left = 0;
1810 player->inventory_infinite_element = EL_UNDEFINED;
1811 player->inventory_size = 0;
1813 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1814 SnapField(player, 0, 0);
1816 player->LevelSolved = FALSE;
1817 player->GameOver = FALSE;
1820 network_player_action_received = FALSE;
1822 #if defined(NETWORK_AVALIABLE)
1823 /* initial null action */
1824 if (network_playing)
1825 SendToServer_MovePlayer(MV_NO_MOVING);
1834 TimeLeft = level.time;
1837 ScreenMovDir = MV_NO_MOVING;
1841 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1843 AllPlayersGone = FALSE;
1845 game.yamyam_content_nr = 0;
1846 game.magic_wall_active = FALSE;
1847 game.magic_wall_time_left = 0;
1848 game.light_time_left = 0;
1849 game.timegate_time_left = 0;
1850 game.switchgate_pos = 0;
1851 game.balloon_dir = MV_NO_MOVING;
1852 game.gravity = level.initial_gravity;
1853 game.explosions_delayed = TRUE;
1855 game.envelope_active = FALSE;
1857 for (i = 0; i < NUM_BELTS; i++)
1859 game.belt_dir[i] = MV_NO_MOVING;
1860 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1863 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1864 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1866 for (x = 0; x < lev_fieldx; x++)
1868 for (y = 0; y < lev_fieldy; y++)
1870 Feld[x][y] = level.field[x][y];
1871 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1872 ChangeDelay[x][y] = 0;
1873 ChangePage[x][y] = -1;
1874 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1876 WasJustMoving[x][y] = 0;
1877 WasJustFalling[x][y] = 0;
1878 CheckCollision[x][y] = 0;
1880 Pushed[x][y] = FALSE;
1882 Changed[x][y] = FALSE;
1883 ChangeEvent[x][y] = -1;
1885 ExplodePhase[x][y] = 0;
1886 ExplodeDelay[x][y] = 0;
1887 ExplodeField[x][y] = EX_TYPE_NONE;
1889 RunnerVisit[x][y] = 0;
1890 PlayerVisit[x][y] = 0;
1893 GfxRandom[x][y] = INIT_GFX_RANDOM();
1894 GfxElement[x][y] = EL_UNDEFINED;
1895 GfxAction[x][y] = ACTION_DEFAULT;
1896 GfxDir[x][y] = MV_NO_MOVING;
1900 for (y = 0; y < lev_fieldy; y++)
1902 for (x = 0; x < lev_fieldx; x++)
1904 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1906 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1908 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1911 InitField(x, y, TRUE);
1917 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1918 emulate_sb ? EMU_SOKOBAN :
1919 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1921 /* initialize explosion and ignition delay */
1922 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1924 if (!IS_CUSTOM_ELEMENT(i))
1927 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1928 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1929 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1930 int last_phase = (num_phase + 1) * delay;
1931 int half_phase = (num_phase / 2) * delay;
1933 element_info[i].explosion_delay = last_phase - 1;
1934 element_info[i].ignition_delay = half_phase;
1937 if (i == EL_BLACK_ORB)
1938 element_info[i].ignition_delay = 0;
1940 if (i == EL_BLACK_ORB)
1941 element_info[i].ignition_delay = 1;
1946 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1947 element_info[i].explosion_delay = 1;
1949 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1950 element_info[i].ignition_delay = 1;
1954 /* correct non-moving belts to start moving left */
1955 for (i = 0; i < NUM_BELTS; i++)
1956 if (game.belt_dir[i] == MV_NO_MOVING)
1957 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1959 /* check if any connected player was not found in playfield */
1960 for (i = 0; i < MAX_PLAYERS; i++)
1962 struct PlayerInfo *player = &stored_player[i];
1964 if (player->connected && !player->present)
1966 for (j = 0; j < MAX_PLAYERS; j++)
1968 struct PlayerInfo *some_player = &stored_player[j];
1969 int jx = some_player->jx, jy = some_player->jy;
1971 /* assign first free player found that is present in the playfield */
1972 if (some_player->present && !some_player->connected)
1974 player->present = TRUE;
1975 player->active = TRUE;
1977 some_player->present = FALSE;
1978 some_player->active = FALSE;
1981 player->element_nr = some_player->element_nr;
1984 #if USE_NEW_BLOCK_STYLE
1985 player->block_last_field = some_player->block_last_field;
1986 player->block_delay_adjustment = some_player->block_delay_adjustment;
1989 StorePlayer[jx][jy] = player->element_nr;
1990 player->jx = player->last_jx = jx;
1991 player->jy = player->last_jy = jy;
2001 /* when playing a tape, eliminate all players which do not participate */
2003 for (i = 0; i < MAX_PLAYERS; i++)
2005 if (stored_player[i].active && !tape.player_participates[i])
2007 struct PlayerInfo *player = &stored_player[i];
2008 int jx = player->jx, jy = player->jy;
2010 player->active = FALSE;
2011 StorePlayer[jx][jy] = 0;
2012 Feld[jx][jy] = EL_EMPTY;
2016 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2018 /* when in single player mode, eliminate all but the first active player */
2020 for (i = 0; i < MAX_PLAYERS; i++)
2022 if (stored_player[i].active)
2024 for (j = i + 1; j < MAX_PLAYERS; j++)
2026 if (stored_player[j].active)
2028 struct PlayerInfo *player = &stored_player[j];
2029 int jx = player->jx, jy = player->jy;
2031 player->active = FALSE;
2032 player->present = FALSE;
2034 StorePlayer[jx][jy] = 0;
2035 Feld[jx][jy] = EL_EMPTY;
2042 /* when recording the game, store which players take part in the game */
2045 for (i = 0; i < MAX_PLAYERS; i++)
2046 if (stored_player[i].active)
2047 tape.player_participates[i] = TRUE;
2052 for (i = 0; i < MAX_PLAYERS; i++)
2054 struct PlayerInfo *player = &stored_player[i];
2056 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2061 if (local_player == player)
2062 printf("Player %d is local player.\n", i+1);
2066 if (BorderElement == EL_EMPTY)
2069 SBX_Right = lev_fieldx - SCR_FIELDX;
2071 SBY_Lower = lev_fieldy - SCR_FIELDY;
2076 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2078 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2081 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2082 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2084 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2085 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2087 /* if local player not found, look for custom element that might create
2088 the player (make some assumptions about the right custom element) */
2089 if (!local_player->present)
2091 int start_x = 0, start_y = 0;
2092 int found_rating = 0;
2093 int found_element = EL_UNDEFINED;
2095 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2097 int element = Feld[x][y];
2102 if (!IS_CUSTOM_ELEMENT(element))
2105 if (CAN_CHANGE(element))
2107 for (i = 0; i < element_info[element].num_change_pages; i++)
2109 content = element_info[element].change_page[i].target_element;
2110 is_player = ELEM_IS_PLAYER(content);
2112 if (is_player && (found_rating < 3 || element < found_element))
2118 found_element = element;
2123 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2125 content = element_info[element].content[xx][yy];
2126 is_player = ELEM_IS_PLAYER(content);
2128 if (is_player && (found_rating < 2 || element < found_element))
2130 start_x = x + xx - 1;
2131 start_y = y + yy - 1;
2134 found_element = element;
2137 if (!CAN_CHANGE(element))
2140 for (i = 0; i < element_info[element].num_change_pages; i++)
2142 content= element_info[element].change_page[i].target_content[xx][yy];
2143 is_player = ELEM_IS_PLAYER(content);
2145 if (is_player && (found_rating < 1 || element < found_element))
2147 start_x = x + xx - 1;
2148 start_y = y + yy - 1;
2151 found_element = element;
2157 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2158 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2161 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2162 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2168 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2169 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2170 local_player->jx - MIDPOSX);
2172 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2173 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2174 local_player->jy - MIDPOSY);
2176 scroll_x = SBX_Left;
2177 scroll_y = SBY_Upper;
2178 if (local_player->jx >= SBX_Left + MIDPOSX)
2179 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2180 local_player->jx - MIDPOSX :
2182 if (local_player->jy >= SBY_Upper + MIDPOSY)
2183 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2184 local_player->jy - MIDPOSY :
2189 CloseDoor(DOOR_CLOSE_1);
2191 /* !!! FIX THIS (START) !!! */
2192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2194 InitGameEngine_EM();
2201 /* after drawing the level, correct some elements */
2202 if (game.timegate_time_left == 0)
2203 CloseAllOpenTimegates();
2205 if (setup.soft_scrolling)
2206 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2208 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2211 /* !!! FIX THIS (END) !!! */
2213 /* copy default game door content to main double buffer */
2214 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2215 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2217 DrawGameDoorValues();
2221 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2222 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2223 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2227 /* copy actual game door content to door double buffer for OpenDoor() */
2228 BlitBitmap(drawto, bitmap_db_door,
2229 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2231 OpenDoor(DOOR_OPEN_ALL);
2233 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2235 if (setup.sound_music)
2238 KeyboardAutoRepeatOffUnlessAutoplay();
2242 for (i = 0; i < MAX_PLAYERS; i++)
2243 printf("Player %d %sactive.\n",
2244 i + 1, (stored_player[i].active ? "" : "not "));
2248 printf("::: starting game [%d]\n", FrameCounter);
2252 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2254 /* this is used for non-R'n'D game engines to update certain engine values */
2256 /* needed to determine if sounds are played within the visible screen area */
2257 scroll_x = actual_scroll_x;
2258 scroll_y = actual_scroll_y;
2261 void InitMovDir(int x, int y)
2263 int i, element = Feld[x][y];
2264 static int xy[4][2] =
2271 static int direction[3][4] =
2273 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2274 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2275 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2284 Feld[x][y] = EL_BUG;
2285 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2288 case EL_SPACESHIP_RIGHT:
2289 case EL_SPACESHIP_UP:
2290 case EL_SPACESHIP_LEFT:
2291 case EL_SPACESHIP_DOWN:
2292 Feld[x][y] = EL_SPACESHIP;
2293 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2296 case EL_BD_BUTTERFLY_RIGHT:
2297 case EL_BD_BUTTERFLY_UP:
2298 case EL_BD_BUTTERFLY_LEFT:
2299 case EL_BD_BUTTERFLY_DOWN:
2300 Feld[x][y] = EL_BD_BUTTERFLY;
2301 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2304 case EL_BD_FIREFLY_RIGHT:
2305 case EL_BD_FIREFLY_UP:
2306 case EL_BD_FIREFLY_LEFT:
2307 case EL_BD_FIREFLY_DOWN:
2308 Feld[x][y] = EL_BD_FIREFLY;
2309 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2312 case EL_PACMAN_RIGHT:
2314 case EL_PACMAN_LEFT:
2315 case EL_PACMAN_DOWN:
2316 Feld[x][y] = EL_PACMAN;
2317 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2320 case EL_SP_SNIKSNAK:
2321 MovDir[x][y] = MV_UP;
2324 case EL_SP_ELECTRON:
2325 MovDir[x][y] = MV_LEFT;
2332 Feld[x][y] = EL_MOLE;
2333 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2337 if (IS_CUSTOM_ELEMENT(element))
2339 struct ElementInfo *ei = &element_info[element];
2340 int move_direction_initial = ei->move_direction_initial;
2341 int move_pattern = ei->move_pattern;
2343 if (move_direction_initial == MV_START_PREVIOUS)
2345 if (MovDir[x][y] != MV_NO_MOVING)
2348 move_direction_initial = MV_START_AUTOMATIC;
2351 if (move_direction_initial == MV_START_RANDOM)
2352 MovDir[x][y] = 1 << RND(4);
2353 else if (move_direction_initial & MV_ANY_DIRECTION)
2354 MovDir[x][y] = move_direction_initial;
2355 else if (move_pattern == MV_ALL_DIRECTIONS ||
2356 move_pattern == MV_TURNING_LEFT ||
2357 move_pattern == MV_TURNING_RIGHT ||
2358 move_pattern == MV_TURNING_LEFT_RIGHT ||
2359 move_pattern == MV_TURNING_RIGHT_LEFT ||
2360 move_pattern == MV_TURNING_RANDOM)
2361 MovDir[x][y] = 1 << RND(4);
2362 else if (move_pattern == MV_HORIZONTAL)
2363 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2364 else if (move_pattern == MV_VERTICAL)
2365 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2366 else if (move_pattern & MV_ANY_DIRECTION)
2367 MovDir[x][y] = element_info[element].move_pattern;
2368 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2369 move_pattern == MV_ALONG_RIGHT_SIDE)
2372 /* use random direction as default start direction */
2373 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2374 MovDir[x][y] = 1 << RND(4);
2377 for (i = 0; i < NUM_DIRECTIONS; i++)
2379 int x1 = x + xy[i][0];
2380 int y1 = y + xy[i][1];
2382 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2384 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2385 MovDir[x][y] = direction[0][i];
2387 MovDir[x][y] = direction[1][i];
2396 MovDir[x][y] = 1 << RND(4);
2398 if (element != EL_BUG &&
2399 element != EL_SPACESHIP &&
2400 element != EL_BD_BUTTERFLY &&
2401 element != EL_BD_FIREFLY)
2404 for (i = 0; i < NUM_DIRECTIONS; i++)
2406 int x1 = x + xy[i][0];
2407 int y1 = y + xy[i][1];
2409 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2411 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2413 MovDir[x][y] = direction[0][i];
2416 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2417 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2419 MovDir[x][y] = direction[1][i];
2428 GfxDir[x][y] = MovDir[x][y];
2431 void InitAmoebaNr(int x, int y)
2434 int group_nr = AmoebeNachbarNr(x, y);
2438 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2440 if (AmoebaCnt[i] == 0)
2448 AmoebaNr[x][y] = group_nr;
2449 AmoebaCnt[group_nr]++;
2450 AmoebaCnt2[group_nr]++;
2456 boolean raise_level = FALSE;
2458 if (local_player->MovPos)
2462 if (tape.auto_play) /* tape might already be stopped here */
2463 tape.auto_play_level_solved = TRUE;
2465 if (tape.playing && tape.auto_play)
2466 tape.auto_play_level_solved = TRUE;
2469 local_player->LevelSolved = FALSE;
2471 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2475 if (!tape.playing && setup.sound_loops)
2476 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2477 SND_CTRL_PLAY_LOOP);
2479 while (TimeLeft > 0)
2481 if (!tape.playing && !setup.sound_loops)
2482 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2483 if (TimeLeft > 0 && !(TimeLeft % 10))
2484 RaiseScore(level.score[SC_TIME_BONUS]);
2485 if (TimeLeft > 100 && !(TimeLeft % 10))
2490 DrawGameValue_Time(TimeLeft);
2498 if (!tape.playing && setup.sound_loops)
2499 StopSound(SND_GAME_LEVELTIME_BONUS);
2501 else if (level.time == 0) /* level without time limit */
2503 if (!tape.playing && setup.sound_loops)
2504 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2505 SND_CTRL_PLAY_LOOP);
2507 while (TimePlayed < 999)
2509 if (!tape.playing && !setup.sound_loops)
2510 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2511 if (TimePlayed < 999 && !(TimePlayed % 10))
2512 RaiseScore(level.score[SC_TIME_BONUS]);
2513 if (TimePlayed < 900 && !(TimePlayed % 10))
2518 DrawGameValue_Time(TimePlayed);
2526 if (!tape.playing && setup.sound_loops)
2527 StopSound(SND_GAME_LEVELTIME_BONUS);
2530 /* close exit door after last player */
2531 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2532 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2533 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2535 int element = Feld[ExitX][ExitY];
2537 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2538 EL_SP_EXIT_CLOSING);
2540 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2543 /* Hero disappears */
2544 if (ExitX >= 0 && ExitY >= 0)
2545 DrawLevelField(ExitX, ExitY);
2552 CloseDoor(DOOR_CLOSE_1);
2557 SaveTape(tape.level_nr); /* Ask to save tape */
2560 if (level_nr == leveldir_current->handicap_level)
2562 leveldir_current->handicap_level++;
2563 SaveLevelSetup_SeriesInfo();
2566 if (level_editor_test_game)
2567 local_player->score = -1; /* no highscore when playing from editor */
2568 else if (level_nr < leveldir_current->last_level)
2569 raise_level = TRUE; /* advance to next level */
2571 if ((hi_pos = NewHiScore()) >= 0)
2573 game_status = GAME_MODE_SCORES;
2574 DrawHallOfFame(hi_pos);
2583 game_status = GAME_MODE_MAIN;
2600 LoadScore(level_nr);
2602 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2603 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2606 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2608 if (local_player->score > highscore[k].Score)
2610 /* player has made it to the hall of fame */
2612 if (k < MAX_SCORE_ENTRIES - 1)
2614 int m = MAX_SCORE_ENTRIES - 1;
2617 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2618 if (!strcmp(setup.player_name, highscore[l].Name))
2620 if (m == k) /* player's new highscore overwrites his old one */
2624 for (l = m; l > k; l--)
2626 strcpy(highscore[l].Name, highscore[l - 1].Name);
2627 highscore[l].Score = highscore[l - 1].Score;
2634 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2635 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2636 highscore[k].Score = local_player->score;
2642 else if (!strncmp(setup.player_name, highscore[k].Name,
2643 MAX_PLAYER_NAME_LEN))
2644 break; /* player already there with a higher score */
2650 SaveScore(level_nr);
2655 inline static int getElementMoveStepsize(int x, int y)
2657 int element = Feld[x][y];
2658 int direction = MovDir[x][y];
2659 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2660 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2661 int horiz_move = (dx != 0);
2662 int sign = (horiz_move ? dx : dy);
2663 int step = sign * element_info[element].move_stepsize;
2665 /* special values for move stepsize for spring and things on conveyor belt */
2669 if (element == EL_SPRING)
2670 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2671 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2672 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2673 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2675 if (CAN_FALL(element) &&
2676 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2677 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2678 else if (element == EL_SPRING)
2679 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2686 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2688 if (player->GfxAction != action || player->GfxDir != dir)
2691 printf("Player frame reset! (%d => %d, %d => %d)\n",
2692 player->GfxAction, action, player->GfxDir, dir);
2695 player->GfxAction = action;
2696 player->GfxDir = dir;
2698 player->StepFrame = 0;
2702 static void ResetRandomAnimationValue(int x, int y)
2704 GfxRandom[x][y] = INIT_GFX_RANDOM();
2707 static void ResetGfxAnimation(int x, int y)
2710 GfxAction[x][y] = ACTION_DEFAULT;
2711 GfxDir[x][y] = MovDir[x][y];
2714 void InitMovingField(int x, int y, int direction)
2716 int element = Feld[x][y];
2717 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2718 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2722 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2723 ResetGfxAnimation(x, y);
2725 #if USE_CAN_MOVE_NOT_MOVING
2727 MovDir[x][y] = direction;
2728 GfxDir[x][y] = direction;
2729 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2730 ACTION_FALLING : ACTION_MOVING);
2732 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2734 if (Feld[newx][newy] == EL_EMPTY)
2735 Feld[newx][newy] = EL_BLOCKED;
2737 MovDir[newx][newy] = MovDir[x][y];
2738 GfxFrame[newx][newy] = GfxFrame[x][y];
2739 GfxRandom[newx][newy] = GfxRandom[x][y];
2740 GfxAction[newx][newy] = GfxAction[x][y];
2741 GfxDir[newx][newy] = GfxDir[x][y];
2746 MovDir[newx][newy] = MovDir[x][y] = direction;
2747 GfxDir[x][y] = direction;
2749 if (Feld[newx][newy] == EL_EMPTY)
2750 Feld[newx][newy] = EL_BLOCKED;
2752 if (direction == MV_DOWN && CAN_FALL(element))
2753 GfxAction[x][y] = ACTION_FALLING;
2755 GfxAction[x][y] = ACTION_MOVING;
2757 GfxFrame[newx][newy] = GfxFrame[x][y];
2758 GfxRandom[newx][newy] = GfxRandom[x][y];
2759 GfxAction[newx][newy] = GfxAction[x][y];
2760 GfxDir[newx][newy] = GfxDir[x][y];
2764 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2766 int direction = MovDir[x][y];
2767 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2768 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2774 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2776 int oldx = x, oldy = y;
2777 int direction = MovDir[x][y];
2779 if (direction == MV_LEFT)
2781 else if (direction == MV_RIGHT)
2783 else if (direction == MV_UP)
2785 else if (direction == MV_DOWN)
2788 *comes_from_x = oldx;
2789 *comes_from_y = oldy;
2792 int MovingOrBlocked2Element(int x, int y)
2794 int element = Feld[x][y];
2796 if (element == EL_BLOCKED)
2800 Blocked2Moving(x, y, &oldx, &oldy);
2801 return Feld[oldx][oldy];
2807 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2809 /* like MovingOrBlocked2Element(), but if element is moving
2810 and (x,y) is the field the moving element is just leaving,
2811 return EL_BLOCKED instead of the element value */
2812 int element = Feld[x][y];
2814 if (IS_MOVING(x, y))
2816 if (element == EL_BLOCKED)
2820 Blocked2Moving(x, y, &oldx, &oldy);
2821 return Feld[oldx][oldy];
2830 static void RemoveField(int x, int y)
2832 Feld[x][y] = EL_EMPTY;
2839 ChangeDelay[x][y] = 0;
2840 ChangePage[x][y] = -1;
2841 Pushed[x][y] = FALSE;
2844 ExplodeField[x][y] = EX_TYPE_NONE;
2847 GfxElement[x][y] = EL_UNDEFINED;
2848 GfxAction[x][y] = ACTION_DEFAULT;
2849 GfxDir[x][y] = MV_NO_MOVING;
2852 void RemoveMovingField(int x, int y)
2854 int oldx = x, oldy = y, newx = x, newy = y;
2855 int element = Feld[x][y];
2856 int next_element = EL_UNDEFINED;
2858 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2861 if (IS_MOVING(x, y))
2863 Moving2Blocked(x, y, &newx, &newy);
2865 if (Feld[newx][newy] != EL_BLOCKED)
2868 if (Feld[newx][newy] != EL_BLOCKED)
2870 /* element is moving, but target field is not free (blocked), but
2871 already occupied by something different (example: acid pool);
2872 in this case, only remove the moving field, but not the target */
2874 RemoveField(oldx, oldy);
2876 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2878 DrawLevelField(oldx, oldy);
2884 else if (element == EL_BLOCKED)
2886 Blocked2Moving(x, y, &oldx, &oldy);
2887 if (!IS_MOVING(oldx, oldy))
2891 if (element == EL_BLOCKED &&
2892 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2893 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2894 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2895 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2896 next_element = get_next_element(Feld[oldx][oldy]);
2898 RemoveField(oldx, oldy);
2899 RemoveField(newx, newy);
2901 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2903 if (next_element != EL_UNDEFINED)
2904 Feld[oldx][oldy] = next_element;
2906 DrawLevelField(oldx, oldy);
2907 DrawLevelField(newx, newy);
2910 void DrawDynamite(int x, int y)
2912 int sx = SCREENX(x), sy = SCREENY(y);
2913 int graphic = el2img(Feld[x][y]);
2916 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2919 if (IS_WALKABLE_INSIDE(Back[x][y]))
2923 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2924 else if (Store[x][y])
2925 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2927 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2930 if (Back[x][y] || Store[x][y])
2931 DrawGraphicThruMask(sx, sy, graphic, frame);
2933 DrawGraphic(sx, sy, graphic, frame);
2935 if (game.emulation == EMU_SUPAPLEX)
2936 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2937 else if (Store[x][y])
2938 DrawGraphicThruMask(sx, sy, graphic, frame);
2940 DrawGraphic(sx, sy, graphic, frame);
2944 void CheckDynamite(int x, int y)
2946 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2950 if (MovDelay[x][y] != 0)
2953 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2960 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2962 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2963 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2964 StopSound(SND_DYNAMITE_ACTIVE);
2966 StopSound(SND_DYNABOMB_ACTIVE);
2972 void DrawRelocatePlayer(struct PlayerInfo *player)
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2977 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2978 int jx = player->jx;
2979 int jy = player->jy;
2981 if (level.instant_relocation)
2984 int offset = (setup.scroll_delay ? 3 : 0);
2986 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2988 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2989 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2990 local_player->jx - MIDPOSX);
2992 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2993 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2994 local_player->jy - MIDPOSY);
2998 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2999 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3000 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3002 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3003 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3004 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3006 /* don't scroll over playfield boundaries */
3007 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3008 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3010 /* don't scroll over playfield boundaries */
3011 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3012 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3015 scroll_x += (local_player->jx - old_jx);
3016 scroll_y += (local_player->jy - old_jy);
3018 /* don't scroll over playfield boundaries */
3019 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3020 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3022 /* don't scroll over playfield boundaries */
3023 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3024 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3027 RedrawPlayfield(TRUE, 0,0,0,0);
3033 int offset = (setup.scroll_delay ? 3 : 0);
3035 int scroll_xx = -999, scroll_yy = -999;
3037 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3039 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3042 int fx = FX, fy = FY;
3044 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3045 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3046 local_player->jx - MIDPOSX);
3048 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3049 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3050 local_player->jy - MIDPOSY);
3052 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3053 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3056 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3059 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3066 fx += dx * TILEX / 2;
3067 fy += dy * TILEY / 2;
3069 ScrollLevel(dx, dy);
3072 /* scroll in two steps of half tile size to make things smoother */
3073 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3075 Delay(wait_delay_value);
3077 /* scroll second step to align at full tile size */
3079 Delay(wait_delay_value);
3082 int scroll_xx = -999, scroll_yy = -999;
3084 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3086 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3089 int fx = FX, fy = FY;
3091 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3092 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3093 local_player->jx - MIDPOSX);
3095 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3096 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3097 local_player->jy - MIDPOSY);
3099 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3100 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3103 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3106 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3113 fx += dx * TILEX / 2;
3114 fy += dy * TILEY / 2;
3116 ScrollLevel(dx, dy);
3119 /* scroll in two steps of half tile size to make things smoother */
3120 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3122 Delay(wait_delay_value);
3124 /* scroll second step to align at full tile size */
3126 Delay(wait_delay_value);
3132 Delay(wait_delay_value);
3136 void RelocatePlayer(int jx, int jy, int el_player_raw)
3139 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3141 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3143 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3144 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3145 boolean no_delay = (tape.warp_forward);
3146 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3147 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3148 int old_jx = player->jx;
3149 int old_jy = player->jy;
3150 int old_element = Feld[old_jx][old_jy];
3151 int element = Feld[jx][jy];
3152 boolean player_relocated = (old_jx != jx || old_jy != jy);
3154 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3155 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3157 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3158 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3159 int leave_side_horiz = move_dir_horiz;
3160 int leave_side_vert = move_dir_vert;
3162 static int trigger_sides[4][2] =
3164 /* enter side leave side */
3165 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3166 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3167 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3168 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3170 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3171 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3172 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3173 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3175 int enter_side = enter_side_horiz | enter_side_vert;
3176 int leave_side = leave_side_horiz | leave_side_vert;
3178 if (player->GameOver) /* do not reanimate dead player */
3181 if (!player_relocated) /* no need to relocate the player */
3184 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3186 RemoveField(jx, jy); /* temporarily remove newly placed player */
3187 DrawLevelField(jx, jy);
3190 if (player->present)
3192 while (player->MovPos)
3194 ScrollPlayer(player, SCROLL_GO_ON);
3195 ScrollScreen(NULL, SCROLL_GO_ON);
3197 #if USE_NEW_MOVE_DELAY
3198 AdvanceFrameAndPlayerCounters(player->index_nr);
3206 Delay(wait_delay_value);
3209 DrawPlayer(player); /* needed here only to cleanup last field */
3210 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3212 player->is_moving = FALSE;
3216 if (IS_CUSTOM_ELEMENT(old_element))
3217 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3219 player->index_bit, leave_side);
3221 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3223 player->index_bit, leave_side);
3226 Feld[jx][jy] = el_player;
3227 InitPlayerField(jx, jy, el_player, TRUE);
3229 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3231 Feld[jx][jy] = element;
3232 InitField(jx, jy, FALSE);
3236 if (player == local_player) /* only visually relocate local player */
3237 DrawRelocatePlayer(player);
3241 TestIfHeroTouchesBadThing(jx, jy);
3242 TestIfPlayerTouchesCustomElement(jx, jy);
3246 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3251 /* needed to allow change of walkable custom element by entering player */
3252 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3253 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3255 /* needed to allow change of walkable custom element by entering player */
3256 Changed[jx][jy] = 0; /* allow another change */
3261 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3262 enter_side == MV_LEFT ? "left" :
3263 enter_side == MV_RIGHT ? "right" :
3264 enter_side == MV_UP ? "top" :
3265 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3269 if (IS_CUSTOM_ELEMENT(element))
3270 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3271 player->index_bit, enter_side);
3273 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3275 player->index_bit, enter_side);
3279 void Explode(int ex, int ey, int phase, int mode)
3286 /* !!! eliminate this variable !!! */
3287 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3292 int last_phase = num_phase * delay;
3293 int half_phase = (num_phase / 2) * delay;
3294 int first_phase_after_start = EX_PHASE_START + 1;
3298 if (game.explosions_delayed)
3300 ExplodeField[ex][ey] = mode;
3304 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3306 int center_element = Feld[ex][ey];
3309 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3313 /* --- This is only really needed (and now handled) in "Impact()". --- */
3314 /* do not explode moving elements that left the explode field in time */
3315 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3316 center_element == EL_EMPTY &&
3317 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3322 if (mode == EX_TYPE_NORMAL ||
3323 mode == EX_TYPE_CENTER ||
3324 mode == EX_TYPE_CROSS)
3325 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3327 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3328 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3331 /* remove things displayed in background while burning dynamite */
3332 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3335 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3337 /* put moving element to center field (and let it explode there) */
3338 center_element = MovingOrBlocked2Element(ex, ey);
3339 RemoveMovingField(ex, ey);
3340 Feld[ex][ey] = center_element;
3346 last_phase = element_info[center_element].explosion_delay + 1;
3348 last_phase = element_info[center_element].explosion_delay;
3352 printf("::: %d -> %d\n", center_element, last_phase);
3356 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3358 int xx = x - ex + 1;
3359 int yy = y - ey + 1;
3364 if (!IN_LEV_FIELD(x, y) ||
3365 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3366 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3369 if (!IN_LEV_FIELD(x, y) ||
3370 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3374 if (!IN_LEV_FIELD(x, y) ||
3375 ((mode != EX_TYPE_NORMAL ||
3376 center_element == EL_AMOEBA_TO_DIAMOND) &&
3377 (x != ex || y != ey)))
3381 element = Feld[x][y];
3383 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3385 element = MovingOrBlocked2Element(x, y);
3387 if (!IS_EXPLOSION_PROOF(element))
3388 RemoveMovingField(x, y);
3394 if (IS_EXPLOSION_PROOF(element))
3397 /* indestructible elements can only explode in center (but not flames) */
3399 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3400 mode == EX_TYPE_BORDER)) ||
3401 element == EL_FLAMES)
3404 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3405 element == EL_FLAMES)
3411 if ((IS_INDESTRUCTIBLE(element) &&
3412 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3413 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3414 element == EL_FLAMES)
3418 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3419 behaviour, for example when touching a yamyam that explodes to rocks
3420 with active deadly shield, a rock is created under the player !!! */
3421 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3423 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3424 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3425 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3427 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3430 if (IS_ACTIVE_BOMB(element))
3432 /* re-activate things under the bomb like gate or penguin */
3434 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3437 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3442 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3443 element_info[Feld[x][y]].token_name,
3444 Store[x][y], Store2[x][y]);
3451 /* save walkable background elements while explosion on same tile */
3453 if (IS_INDESTRUCTIBLE(element))
3454 Back[x][y] = element;
3458 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3459 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3460 Back[x][y] = element;
3462 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3463 (x != ex || y != ey))
3464 Back[x][y] = element;
3467 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3468 Back[x][y] = element;
3472 /* ignite explodable elements reached by other explosion */
3473 if (element == EL_EXPLOSION)
3474 element = Store2[x][y];
3477 if (AmoebaNr[x][y] &&
3478 (element == EL_AMOEBA_FULL ||
3479 element == EL_BD_AMOEBA ||
3480 element == EL_AMOEBA_GROWING))
3482 AmoebaCnt[AmoebaNr[x][y]]--;
3483 AmoebaCnt2[AmoebaNr[x][y]]--;
3489 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3491 switch(StorePlayer[ex][ey])
3494 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3497 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3500 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3504 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3509 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3510 Store[x][y] = EL_EMPTY;
3512 if (game.emulation == EMU_SUPAPLEX)
3513 Store[x][y] = EL_EMPTY;
3516 else if (center_element == EL_MOLE)
3517 Store[x][y] = EL_EMERALD_RED;
3518 else if (center_element == EL_PENGUIN)
3519 Store[x][y] = EL_EMERALD_PURPLE;
3520 else if (center_element == EL_BUG)
3521 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3522 else if (center_element == EL_BD_BUTTERFLY)
3523 Store[x][y] = EL_BD_DIAMOND;
3524 else if (center_element == EL_SP_ELECTRON)
3525 Store[x][y] = EL_SP_INFOTRON;
3526 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3527 Store[x][y] = level.amoeba_content;
3528 else if (center_element == EL_YAMYAM)
3529 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3530 else if (IS_CUSTOM_ELEMENT(center_element) &&
3531 element_info[center_element].content[xx][yy] != EL_EMPTY)
3532 Store[x][y] = element_info[center_element].content[xx][yy];
3533 else if (element == EL_WALL_EMERALD)
3534 Store[x][y] = EL_EMERALD;
3535 else if (element == EL_WALL_DIAMOND)
3536 Store[x][y] = EL_DIAMOND;
3537 else if (element == EL_WALL_BD_DIAMOND)
3538 Store[x][y] = EL_BD_DIAMOND;
3539 else if (element == EL_WALL_EMERALD_YELLOW)
3540 Store[x][y] = EL_EMERALD_YELLOW;
3541 else if (element == EL_WALL_EMERALD_RED)
3542 Store[x][y] = EL_EMERALD_RED;
3543 else if (element == EL_WALL_EMERALD_PURPLE)
3544 Store[x][y] = EL_EMERALD_PURPLE;
3545 else if (element == EL_WALL_PEARL)
3546 Store[x][y] = EL_PEARL;
3547 else if (element == EL_WALL_CRYSTAL)
3548 Store[x][y] = EL_CRYSTAL;
3549 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3550 Store[x][y] = element_info[element].content[1][1];
3552 Store[x][y] = EL_EMPTY;
3554 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3555 center_element == EL_AMOEBA_TO_DIAMOND)
3556 Store2[x][y] = element;
3559 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3560 element_info[Store2[x][y]].token_name);
3564 if (AmoebaNr[x][y] &&
3565 (element == EL_AMOEBA_FULL ||
3566 element == EL_BD_AMOEBA ||
3567 element == EL_AMOEBA_GROWING))
3569 AmoebaCnt[AmoebaNr[x][y]]--;
3570 AmoebaCnt2[AmoebaNr[x][y]]--;
3576 MovDir[x][y] = MovPos[x][y] = 0;
3577 GfxDir[x][y] = MovDir[x][y];
3582 Feld[x][y] = EL_EXPLOSION;
3584 GfxElement[x][y] = center_element;
3586 GfxElement[x][y] = EL_UNDEFINED;
3589 ExplodePhase[x][y] = 1;
3591 ExplodeDelay[x][y] = last_phase;
3596 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3598 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3605 if (center_element == EL_YAMYAM)
3606 game.yamyam_content_nr =
3607 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3610 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3611 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3625 GfxFrame[x][y] = 0; /* restart explosion animation */
3629 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3633 last_phase = ExplodeDelay[x][y];
3636 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3640 /* activate this even in non-DEBUG version until cause for crash in
3641 getGraphicAnimationFrame() (see below) is found and eliminated */
3645 if (GfxElement[x][y] == EL_UNDEFINED)
3648 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3649 printf("Explode(): This should never happen!\n");
3652 GfxElement[x][y] = EL_EMPTY;
3658 border_element = Store2[x][y];
3660 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3661 border_element = StorePlayer[x][y];
3663 if (IS_PLAYER(x, y))
3664 border_element = StorePlayer[x][y];
3668 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3669 element_info[border_element].token_name, Store2[x][y]);
3673 printf("::: phase == %d\n", phase);
3676 if (phase == element_info[border_element].ignition_delay ||
3677 phase == last_phase)
3679 boolean border_explosion = FALSE;
3683 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3684 !PLAYER_EXPLOSION_PROTECTED(x, y))
3686 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3689 if (IS_PLAYER(x, y))
3692 KillHeroUnlessExplosionProtected(x, y);
3693 border_explosion = TRUE;
3696 if (phase == last_phase)
3697 printf("::: IS_PLAYER\n");
3700 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3703 printf("::: %d,%d: %d %s\n", x, y, border_element,
3704 element_info[border_element].token_name);
3707 Feld[x][y] = Store2[x][y];
3710 border_explosion = TRUE;
3713 if (phase == last_phase)
3714 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3717 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3719 AmoebeUmwandeln(x, y);
3721 border_explosion = TRUE;
3724 if (phase == last_phase)
3725 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3726 element_info[border_element].explosion_delay,
3727 element_info[border_element].ignition_delay,
3733 /* if an element just explodes due to another explosion (chain-reaction),
3734 do not immediately end the new explosion when it was the last frame of
3735 the explosion (as it would be done in the following "if"-statement!) */
3736 if (border_explosion && phase == last_phase)
3743 if (phase == first_phase_after_start)
3745 int element = Store2[x][y];
3747 if (element == EL_BLACK_ORB)
3749 Feld[x][y] = Store2[x][y];
3754 else if (phase == half_phase)
3756 int element = Store2[x][y];
3758 if (IS_PLAYER(x, y))
3759 KillHeroUnlessExplosionProtected(x, y);
3760 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3762 Feld[x][y] = Store2[x][y];
3766 else if (element == EL_AMOEBA_TO_DIAMOND)
3767 AmoebeUmwandeln(x, y);
3771 if (phase == last_phase)
3776 printf("::: done: phase == %d\n", phase);
3780 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3783 element = Feld[x][y] = Store[x][y];
3784 Store[x][y] = Store2[x][y] = 0;
3785 GfxElement[x][y] = EL_UNDEFINED;
3787 /* player can escape from explosions and might therefore be still alive */
3788 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3789 element <= EL_PLAYER_IS_EXPLODING_4)
3790 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3792 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3793 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3794 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3797 /* restore probably existing indestructible background element */
3798 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3799 element = Feld[x][y] = Back[x][y];
3802 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3803 GfxDir[x][y] = MV_NO_MOVING;
3804 ChangeDelay[x][y] = 0;
3805 ChangePage[x][y] = -1;
3808 InitField_WithBug2(x, y, FALSE);
3810 InitField(x, y, FALSE);
3812 /* !!! not needed !!! */
3814 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3815 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3818 if (CAN_MOVE(element))
3823 DrawLevelField(x, y);
3825 TestIfElementTouchesCustomElement(x, y);
3827 if (GFX_CRUMBLED(element))
3828 DrawLevelFieldCrumbledSandNeighbours(x, y);
3830 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3831 StorePlayer[x][y] = 0;
3833 if (ELEM_IS_PLAYER(element))
3834 RelocatePlayer(x, y, element);
3837 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3839 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3843 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3845 int stored = Store[x][y];
3846 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3847 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3851 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3853 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3857 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3861 printf("::: %d / %d [%d - %d]\n",
3862 GfxFrame[x][y], phase - delay, phase, delay);
3866 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3867 element_info[GfxElement[x][y]].token_name,
3872 DrawLevelFieldCrumbledSand(x, y);
3874 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3876 DrawLevelElement(x, y, Back[x][y]);
3877 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3879 else if (IS_WALKABLE_UNDER(Back[x][y]))
3881 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3882 DrawLevelElementThruMask(x, y, Back[x][y]);
3884 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3885 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3889 void DynaExplode(int ex, int ey)
3892 int dynabomb_element = Feld[ex][ey];
3893 int dynabomb_size = 1;
3894 boolean dynabomb_xl = FALSE;
3895 struct PlayerInfo *player;
3896 static int xy[4][2] =
3904 if (IS_ACTIVE_BOMB(dynabomb_element))
3906 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3907 dynabomb_size = player->dynabomb_size;
3908 dynabomb_xl = player->dynabomb_xl;
3909 player->dynabombs_left++;
3912 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3914 for (i = 0; i < NUM_DIRECTIONS; i++)
3916 for (j = 1; j <= dynabomb_size; j++)
3918 int x = ex + j * xy[i][0];
3919 int y = ey + j * xy[i][1];
3922 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3925 element = Feld[x][y];
3927 /* do not restart explosions of fields with active bombs */
3928 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3931 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3935 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3936 !IS_DIGGABLE(element) && !dynabomb_xl)
3939 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3940 !CAN_GROW_INTO(element) && !dynabomb_xl)
3944 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3945 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3946 element != EL_SAND && !dynabomb_xl)
3953 void Bang(int x, int y)
3956 int element = MovingOrBlocked2Element(x, y);
3958 int element = Feld[x][y];
3962 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3964 if (IS_PLAYER(x, y))
3967 struct PlayerInfo *player = PLAYERINFO(x, y);
3969 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3970 player->element_nr);
3975 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3977 if (game.emulation == EMU_SUPAPLEX)
3978 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3980 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3985 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3993 case EL_BD_BUTTERFLY:
3996 case EL_DARK_YAMYAM:
4000 RaiseScoreElement(element);
4001 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4003 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4004 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4005 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4006 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4007 case EL_DYNABOMB_INCREASE_NUMBER:
4008 case EL_DYNABOMB_INCREASE_SIZE:
4009 case EL_DYNABOMB_INCREASE_POWER:
4014 case EL_LAMP_ACTIVE:
4016 case EL_AMOEBA_TO_DIAMOND:
4018 if (IS_PLAYER(x, y))
4019 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4021 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4025 if (element_info[element].explosion_type == EXPLODES_CROSS)
4027 if (CAN_EXPLODE_CROSS(element))
4030 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4035 else if (element_info[element].explosion_type == EXPLODES_1X1)
4037 else if (CAN_EXPLODE_1X1(element))
4039 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4041 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4045 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4048 void SplashAcid(int x, int y)
4051 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4052 (!IN_LEV_FIELD(x - 1, y - 2) ||
4053 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4054 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4056 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4057 (!IN_LEV_FIELD(x + 1, y - 2) ||
4058 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4059 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4061 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4063 /* input: position of element entering acid (obsolete) */
4065 int element = Feld[x][y];
4067 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4070 if (element != EL_ACID_SPLASH_LEFT &&
4071 element != EL_ACID_SPLASH_RIGHT)
4073 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4075 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4076 (!IN_LEV_FIELD(x - 1, y - 1) ||
4077 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4078 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4080 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4081 (!IN_LEV_FIELD(x + 1, y - 1) ||
4082 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4083 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4088 static void InitBeltMovement()
4090 static int belt_base_element[4] =
4092 EL_CONVEYOR_BELT_1_LEFT,
4093 EL_CONVEYOR_BELT_2_LEFT,
4094 EL_CONVEYOR_BELT_3_LEFT,
4095 EL_CONVEYOR_BELT_4_LEFT
4097 static int belt_base_active_element[4] =
4099 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4100 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4101 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4102 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4107 /* set frame order for belt animation graphic according to belt direction */
4108 for (i = 0; i < NUM_BELTS; i++)
4112 for (j = 0; j < NUM_BELT_PARTS; j++)
4114 int element = belt_base_active_element[belt_nr] + j;
4115 int graphic = el2img(element);
4117 if (game.belt_dir[i] == MV_LEFT)
4118 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4120 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4124 for (y = 0; y < lev_fieldy; y++)
4126 for (x = 0; x < lev_fieldx; x++)
4128 int element = Feld[x][y];
4130 for (i = 0; i < NUM_BELTS; i++)
4132 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4134 int e_belt_nr = getBeltNrFromBeltElement(element);
4137 if (e_belt_nr == belt_nr)
4139 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4141 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4149 static void ToggleBeltSwitch(int x, int y)
4151 static int belt_base_element[4] =
4153 EL_CONVEYOR_BELT_1_LEFT,
4154 EL_CONVEYOR_BELT_2_LEFT,
4155 EL_CONVEYOR_BELT_3_LEFT,
4156 EL_CONVEYOR_BELT_4_LEFT
4158 static int belt_base_active_element[4] =
4160 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4161 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4162 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4163 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4165 static int belt_base_switch_element[4] =
4167 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4168 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4169 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4170 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4172 static int belt_move_dir[4] =
4180 int element = Feld[x][y];
4181 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4182 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4183 int belt_dir = belt_move_dir[belt_dir_nr];
4186 if (!IS_BELT_SWITCH(element))
4189 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4190 game.belt_dir[belt_nr] = belt_dir;
4192 if (belt_dir_nr == 3)
4195 /* set frame order for belt animation graphic according to belt direction */
4196 for (i = 0; i < NUM_BELT_PARTS; i++)
4198 int element = belt_base_active_element[belt_nr] + i;
4199 int graphic = el2img(element);
4201 if (belt_dir == MV_LEFT)
4202 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4204 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4207 for (yy = 0; yy < lev_fieldy; yy++)
4209 for (xx = 0; xx < lev_fieldx; xx++)
4211 int element = Feld[xx][yy];
4213 if (IS_BELT_SWITCH(element))
4215 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4217 if (e_belt_nr == belt_nr)
4219 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4220 DrawLevelField(xx, yy);
4223 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4225 int e_belt_nr = getBeltNrFromBeltElement(element);
4227 if (e_belt_nr == belt_nr)
4229 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4231 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4232 DrawLevelField(xx, yy);
4235 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4237 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4239 if (e_belt_nr == belt_nr)
4241 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4243 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4244 DrawLevelField(xx, yy);
4251 static void ToggleSwitchgateSwitch(int x, int y)
4255 game.switchgate_pos = !game.switchgate_pos;
4257 for (yy = 0; yy < lev_fieldy; yy++)
4259 for (xx = 0; xx < lev_fieldx; xx++)
4261 int element = Feld[xx][yy];
4263 if (element == EL_SWITCHGATE_SWITCH_UP ||
4264 element == EL_SWITCHGATE_SWITCH_DOWN)
4266 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4267 DrawLevelField(xx, yy);
4269 else if (element == EL_SWITCHGATE_OPEN ||
4270 element == EL_SWITCHGATE_OPENING)
4272 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4274 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4276 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4279 else if (element == EL_SWITCHGATE_CLOSED ||
4280 element == EL_SWITCHGATE_CLOSING)
4282 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4284 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4286 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4293 static int getInvisibleActiveFromInvisibleElement(int element)
4295 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4296 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4297 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4301 static int getInvisibleFromInvisibleActiveElement(int element)
4303 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4304 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4305 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4309 static void RedrawAllLightSwitchesAndInvisibleElements()
4313 for (y = 0; y < lev_fieldy; y++)
4315 for (x = 0; x < lev_fieldx; x++)
4317 int element = Feld[x][y];
4319 if (element == EL_LIGHT_SWITCH &&
4320 game.light_time_left > 0)
4322 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4323 DrawLevelField(x, y);
4325 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4326 game.light_time_left == 0)
4328 Feld[x][y] = EL_LIGHT_SWITCH;
4329 DrawLevelField(x, y);
4331 else if (element == EL_INVISIBLE_STEELWALL ||
4332 element == EL_INVISIBLE_WALL ||
4333 element == EL_INVISIBLE_SAND)
4335 if (game.light_time_left > 0)
4336 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4338 DrawLevelField(x, y);
4340 /* uncrumble neighbour fields, if needed */
4341 if (element == EL_INVISIBLE_SAND)
4342 DrawLevelFieldCrumbledSandNeighbours(x, y);
4344 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4345 element == EL_INVISIBLE_WALL_ACTIVE ||
4346 element == EL_INVISIBLE_SAND_ACTIVE)
4348 if (game.light_time_left == 0)
4349 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4351 DrawLevelField(x, y);
4353 /* re-crumble neighbour fields, if needed */
4354 if (element == EL_INVISIBLE_SAND)
4355 DrawLevelFieldCrumbledSandNeighbours(x, y);
4361 static void ToggleLightSwitch(int x, int y)
4363 int element = Feld[x][y];
4365 game.light_time_left =
4366 (element == EL_LIGHT_SWITCH ?
4367 level.time_light * FRAMES_PER_SECOND : 0);
4369 RedrawAllLightSwitchesAndInvisibleElements();
4372 static void ActivateTimegateSwitch(int x, int y)
4376 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4378 for (yy = 0; yy < lev_fieldy; yy++)
4380 for (xx = 0; xx < lev_fieldx; xx++)
4382 int element = Feld[xx][yy];
4384 if (element == EL_TIMEGATE_CLOSED ||
4385 element == EL_TIMEGATE_CLOSING)
4387 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4388 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4392 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4394 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4395 DrawLevelField(xx, yy);
4402 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4405 void Impact(int x, int y)
4407 boolean last_line = (y == lev_fieldy - 1);
4408 boolean object_hit = FALSE;
4409 boolean impact = (last_line || object_hit);
4410 int element = Feld[x][y];
4411 int smashed = EL_STEELWALL;
4414 printf("IMPACT!\n");
4417 if (!last_line) /* check if element below was hit */
4419 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4422 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4423 MovDir[x][y + 1] != MV_DOWN ||
4424 MovPos[x][y + 1] <= TILEY / 2));
4427 object_hit = !IS_FREE(x, y + 1);
4430 /* do not smash moving elements that left the smashed field in time */
4431 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4432 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4436 smashed = MovingOrBlocked2Element(x, y + 1);
4438 impact = (last_line || object_hit);
4441 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4443 SplashAcid(x, y + 1);
4447 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4448 /* only reset graphic animation if graphic really changes after impact */
4450 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4452 ResetGfxAnimation(x, y);
4453 DrawLevelField(x, y);
4456 if (impact && CAN_EXPLODE_IMPACT(element))
4461 else if (impact && element == EL_PEARL)
4463 ResetGfxAnimation(x, y);
4465 Feld[x][y] = EL_PEARL_BREAKING;
4466 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4469 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4471 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4476 if (impact && element == EL_AMOEBA_DROP)
4478 if (object_hit && IS_PLAYER(x, y + 1))
4479 KillHeroUnlessEnemyProtected(x, y + 1);
4480 else if (object_hit && smashed == EL_PENGUIN)
4484 Feld[x][y] = EL_AMOEBA_GROWING;
4485 Store[x][y] = EL_AMOEBA_WET;
4487 ResetRandomAnimationValue(x, y);
4492 if (object_hit) /* check which object was hit */
4494 if (CAN_PASS_MAGIC_WALL(element) &&
4495 (smashed == EL_MAGIC_WALL ||
4496 smashed == EL_BD_MAGIC_WALL))
4499 int activated_magic_wall =
4500 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4501 EL_BD_MAGIC_WALL_ACTIVE);
4503 /* activate magic wall / mill */
4504 for (yy = 0; yy < lev_fieldy; yy++)
4505 for (xx = 0; xx < lev_fieldx; xx++)
4506 if (Feld[xx][yy] == smashed)
4507 Feld[xx][yy] = activated_magic_wall;
4509 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4510 game.magic_wall_active = TRUE;
4512 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4513 SND_MAGIC_WALL_ACTIVATING :
4514 SND_BD_MAGIC_WALL_ACTIVATING));
4517 if (IS_PLAYER(x, y + 1))
4519 if (CAN_SMASH_PLAYER(element))
4521 KillHeroUnlessEnemyProtected(x, y + 1);
4525 else if (smashed == EL_PENGUIN)
4527 if (CAN_SMASH_PLAYER(element))
4533 else if (element == EL_BD_DIAMOND)
4535 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4541 else if (((element == EL_SP_INFOTRON ||
4542 element == EL_SP_ZONK) &&
4543 (smashed == EL_SP_SNIKSNAK ||
4544 smashed == EL_SP_ELECTRON ||
4545 smashed == EL_SP_DISK_ORANGE)) ||
4546 (element == EL_SP_INFOTRON &&
4547 smashed == EL_SP_DISK_YELLOW))
4553 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4559 else if (CAN_SMASH_EVERYTHING(element))
4561 if (IS_CLASSIC_ENEMY(smashed) ||
4562 CAN_EXPLODE_SMASHED(smashed))
4567 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4569 if (smashed == EL_LAMP ||
4570 smashed == EL_LAMP_ACTIVE)
4575 else if (smashed == EL_NUT)
4577 Feld[x][y + 1] = EL_NUT_BREAKING;
4578 PlayLevelSound(x, y, SND_NUT_BREAKING);
4579 RaiseScoreElement(EL_NUT);
4582 else if (smashed == EL_PEARL)
4584 ResetGfxAnimation(x, y);
4586 Feld[x][y + 1] = EL_PEARL_BREAKING;
4587 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4590 else if (smashed == EL_DIAMOND)
4592 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4593 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4596 else if (IS_BELT_SWITCH(smashed))
4598 ToggleBeltSwitch(x, y + 1);
4600 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4601 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4603 ToggleSwitchgateSwitch(x, y + 1);
4605 else if (smashed == EL_LIGHT_SWITCH ||
4606 smashed == EL_LIGHT_SWITCH_ACTIVE)
4608 ToggleLightSwitch(x, y + 1);
4613 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4616 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4619 /* !!! TEST ONLY !!! */
4620 CheckElementChangeBySide(x, y + 1, smashed, element,
4621 CE_SWITCHED, CH_SIDE_TOP);
4622 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4623 CE_SWITCH_OF_X, CH_SIDE_TOP);
4625 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4626 CE_SWITCH_OF_X, CH_SIDE_TOP);
4627 CheckElementChangeBySide(x, y + 1, smashed, element,
4628 CE_SWITCHED, CH_SIDE_TOP);
4634 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4639 /* play sound of magic wall / mill */
4641 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4642 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4644 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4645 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4646 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4647 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4652 /* play sound of object that hits the ground */
4653 if (last_line || object_hit)
4654 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4657 inline static void TurnRoundExt(int x, int y)
4669 { 0, 0 }, { 0, 0 }, { 0, 0 },
4674 int left, right, back;
4678 { MV_DOWN, MV_UP, MV_RIGHT },
4679 { MV_UP, MV_DOWN, MV_LEFT },
4681 { MV_LEFT, MV_RIGHT, MV_DOWN },
4685 { MV_RIGHT, MV_LEFT, MV_UP }
4688 int element = Feld[x][y];
4689 int move_pattern = element_info[element].move_pattern;
4691 int old_move_dir = MovDir[x][y];
4692 int left_dir = turn[old_move_dir].left;
4693 int right_dir = turn[old_move_dir].right;
4694 int back_dir = turn[old_move_dir].back;
4696 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4697 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4698 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4699 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4701 int left_x = x + left_dx, left_y = y + left_dy;
4702 int right_x = x + right_dx, right_y = y + right_dy;
4703 int move_x = x + move_dx, move_y = y + move_dy;
4707 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4709 TestIfBadThingTouchesOtherBadThing(x, y);
4711 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4712 MovDir[x][y] = right_dir;
4713 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4714 MovDir[x][y] = left_dir;
4716 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4718 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4722 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4723 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4725 TestIfBadThingTouchesOtherBadThing(x, y);
4727 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4728 MovDir[x][y] = left_dir;
4729 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4730 MovDir[x][y] = right_dir;
4732 if ((element == EL_SPACESHIP ||
4733 element == EL_SP_SNIKSNAK ||
4734 element == EL_SP_ELECTRON)
4735 && MovDir[x][y] != old_move_dir)
4737 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4741 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4743 TestIfBadThingTouchesOtherBadThing(x, y);
4745 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4746 MovDir[x][y] = left_dir;
4747 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4748 MovDir[x][y] = right_dir;
4750 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4752 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4755 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4757 TestIfBadThingTouchesOtherBadThing(x, y);
4759 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4760 MovDir[x][y] = left_dir;
4761 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4762 MovDir[x][y] = right_dir;
4764 if (MovDir[x][y] != old_move_dir)
4768 else if (element == EL_YAMYAM)
4770 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4771 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4773 if (can_turn_left && can_turn_right)
4774 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4775 else if (can_turn_left)
4776 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4777 else if (can_turn_right)
4778 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4780 MovDir[x][y] = back_dir;
4782 MovDelay[x][y] = 16 + 16 * RND(3);
4784 else if (element == EL_DARK_YAMYAM)
4786 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4788 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4791 if (can_turn_left && can_turn_right)
4792 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4793 else if (can_turn_left)
4794 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4795 else if (can_turn_right)
4796 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4798 MovDir[x][y] = back_dir;
4800 MovDelay[x][y] = 16 + 16 * RND(3);
4802 else if (element == EL_PACMAN)
4804 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4805 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4807 if (can_turn_left && can_turn_right)
4808 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4809 else if (can_turn_left)
4810 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4811 else if (can_turn_right)
4812 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4814 MovDir[x][y] = back_dir;
4816 MovDelay[x][y] = 6 + RND(40);
4818 else if (element == EL_PIG)
4820 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4821 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4822 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4823 boolean should_turn_left, should_turn_right, should_move_on;
4825 int rnd = RND(rnd_value);
4827 should_turn_left = (can_turn_left &&
4829 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4830 y + back_dy + left_dy)));
4831 should_turn_right = (can_turn_right &&
4833 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4834 y + back_dy + right_dy)));
4835 should_move_on = (can_move_on &&
4838 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4839 y + move_dy + left_dy) ||
4840 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4841 y + move_dy + right_dy)));
4843 if (should_turn_left || should_turn_right || should_move_on)
4845 if (should_turn_left && should_turn_right && should_move_on)
4846 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4847 rnd < 2 * rnd_value / 3 ? right_dir :
4849 else if (should_turn_left && should_turn_right)
4850 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4851 else if (should_turn_left && should_move_on)
4852 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4853 else if (should_turn_right && should_move_on)
4854 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4855 else if (should_turn_left)
4856 MovDir[x][y] = left_dir;
4857 else if (should_turn_right)
4858 MovDir[x][y] = right_dir;
4859 else if (should_move_on)
4860 MovDir[x][y] = old_move_dir;
4862 else if (can_move_on && rnd > rnd_value / 8)
4863 MovDir[x][y] = old_move_dir;
4864 else if (can_turn_left && can_turn_right)
4865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4866 else if (can_turn_left && rnd > rnd_value / 8)
4867 MovDir[x][y] = left_dir;
4868 else if (can_turn_right && rnd > rnd_value/8)
4869 MovDir[x][y] = right_dir;
4871 MovDir[x][y] = back_dir;
4873 xx = x + move_xy[MovDir[x][y]].x;
4874 yy = y + move_xy[MovDir[x][y]].y;
4877 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4878 if (!IN_LEV_FIELD(xx, yy) ||
4879 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4880 MovDir[x][y] = old_move_dir;
4882 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4883 MovDir[x][y] = old_move_dir;
4888 else if (element == EL_DRAGON)
4890 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4891 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4892 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4894 int rnd = RND(rnd_value);
4897 if (FrameCounter < 1 && x == 0 && y == 29)
4898 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4901 if (can_move_on && rnd > rnd_value / 8)
4902 MovDir[x][y] = old_move_dir;
4903 else if (can_turn_left && can_turn_right)
4904 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4905 else if (can_turn_left && rnd > rnd_value / 8)
4906 MovDir[x][y] = left_dir;
4907 else if (can_turn_right && rnd > rnd_value / 8)
4908 MovDir[x][y] = right_dir;
4910 MovDir[x][y] = back_dir;
4912 xx = x + move_xy[MovDir[x][y]].x;
4913 yy = y + move_xy[MovDir[x][y]].y;
4916 if (FrameCounter < 1 && x == 0 && y == 29)
4917 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4918 xx, yy, Feld[xx][yy],
4923 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4924 MovDir[x][y] = old_move_dir;
4926 if (!IS_FREE(xx, yy))
4927 MovDir[x][y] = old_move_dir;
4931 if (FrameCounter < 1 && x == 0 && y == 29)
4932 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4937 else if (element == EL_MOLE)
4939 boolean can_move_on =
4940 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4941 IS_AMOEBOID(Feld[move_x][move_y]) ||
4942 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4945 boolean can_turn_left =
4946 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4947 IS_AMOEBOID(Feld[left_x][left_y])));
4949 boolean can_turn_right =
4950 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4951 IS_AMOEBOID(Feld[right_x][right_y])));
4953 if (can_turn_left && can_turn_right)
4954 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4955 else if (can_turn_left)
4956 MovDir[x][y] = left_dir;
4958 MovDir[x][y] = right_dir;
4961 if (MovDir[x][y] != old_move_dir)
4964 else if (element == EL_BALLOON)
4966 MovDir[x][y] = game.balloon_dir;
4969 else if (element == EL_SPRING)
4972 if (MovDir[x][y] & MV_HORIZONTAL &&
4973 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4974 MovDir[x][y] = MV_NO_MOVING;
4976 if (MovDir[x][y] & MV_HORIZONTAL &&
4977 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4978 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4979 MovDir[x][y] = MV_NO_MOVING;
4984 else if (element == EL_ROBOT ||
4985 element == EL_SATELLITE ||
4986 element == EL_PENGUIN)
4988 int attr_x = -1, attr_y = -1;
4999 for (i = 0; i < MAX_PLAYERS; i++)
5001 struct PlayerInfo *player = &stored_player[i];
5002 int jx = player->jx, jy = player->jy;
5004 if (!player->active)
5008 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5017 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5018 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5019 game.engine_version < VERSION_IDENT(3,1,0,0)))
5021 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5028 if (element == EL_PENGUIN)
5031 static int xy[4][2] =
5039 for (i = 0; i < NUM_DIRECTIONS; i++)
5041 int ex = x + xy[i][0];
5042 int ey = y + xy[i][1];
5044 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5053 MovDir[x][y] = MV_NO_MOVING;
5055 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5056 else if (attr_x > x)
5057 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5059 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5060 else if (attr_y > y)
5061 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5063 if (element == EL_ROBOT)
5067 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5068 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5069 Moving2Blocked(x, y, &newx, &newy);
5071 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5072 MovDelay[x][y] = 8 + 8 * !RND(3);
5074 MovDelay[x][y] = 16;
5076 else if (element == EL_PENGUIN)
5082 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5084 boolean first_horiz = RND(2);
5085 int new_move_dir = MovDir[x][y];
5088 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5089 Moving2Blocked(x, y, &newx, &newy);
5091 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5095 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5096 Moving2Blocked(x, y, &newx, &newy);
5098 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5101 MovDir[x][y] = old_move_dir;
5105 else /* (element == EL_SATELLITE) */
5111 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5113 boolean first_horiz = RND(2);
5114 int new_move_dir = MovDir[x][y];
5117 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5118 Moving2Blocked(x, y, &newx, &newy);
5120 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5124 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5125 Moving2Blocked(x, y, &newx, &newy);
5127 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5130 MovDir[x][y] = old_move_dir;
5135 else if (move_pattern == MV_TURNING_LEFT ||
5136 move_pattern == MV_TURNING_RIGHT ||
5137 move_pattern == MV_TURNING_LEFT_RIGHT ||
5138 move_pattern == MV_TURNING_RIGHT_LEFT ||
5139 move_pattern == MV_TURNING_RANDOM ||
5140 move_pattern == MV_ALL_DIRECTIONS)
5142 boolean can_turn_left =
5143 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5144 boolean can_turn_right =
5145 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5147 #if USE_CAN_MOVE_NOT_MOVING
5148 if (element_info[element].move_stepsize == 0) /* not moving */
5152 if (move_pattern == MV_TURNING_LEFT)
5153 MovDir[x][y] = left_dir;
5154 else if (move_pattern == MV_TURNING_RIGHT)
5155 MovDir[x][y] = right_dir;
5156 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5157 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5158 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5159 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5160 else if (move_pattern == MV_TURNING_RANDOM)
5161 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5162 can_turn_right && !can_turn_left ? right_dir :
5163 RND(2) ? left_dir : right_dir);
5164 else if (can_turn_left && can_turn_right)
5165 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5166 else if (can_turn_left)
5167 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5168 else if (can_turn_right)
5169 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5171 MovDir[x][y] = back_dir;
5173 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5175 else if (move_pattern == MV_HORIZONTAL ||
5176 move_pattern == MV_VERTICAL)
5178 if (move_pattern & old_move_dir)
5179 MovDir[x][y] = back_dir;
5180 else if (move_pattern == MV_HORIZONTAL)
5181 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5182 else if (move_pattern == MV_VERTICAL)
5183 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5185 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5187 else if (move_pattern & MV_ANY_DIRECTION)
5189 MovDir[x][y] = move_pattern;
5190 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5192 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5194 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5195 MovDir[x][y] = left_dir;
5196 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5197 MovDir[x][y] = right_dir;
5199 if (MovDir[x][y] != old_move_dir)
5200 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5202 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5204 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5205 MovDir[x][y] = right_dir;
5206 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5207 MovDir[x][y] = left_dir;
5209 if (MovDir[x][y] != old_move_dir)
5210 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5212 else if (move_pattern == MV_TOWARDS_PLAYER ||
5213 move_pattern == MV_AWAY_FROM_PLAYER)
5215 int attr_x = -1, attr_y = -1;
5217 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5228 for (i = 0; i < MAX_PLAYERS; i++)
5230 struct PlayerInfo *player = &stored_player[i];
5231 int jx = player->jx, jy = player->jy;
5233 if (!player->active)
5237 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5245 MovDir[x][y] = MV_NO_MOVING;
5247 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5248 else if (attr_x > x)
5249 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5251 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5252 else if (attr_y > y)
5253 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5255 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5257 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5259 boolean first_horiz = RND(2);
5260 int new_move_dir = MovDir[x][y];
5262 #if USE_CAN_MOVE_NOT_MOVING
5263 if (element_info[element].move_stepsize == 0) /* not moving */
5265 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5266 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5273 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5274 Moving2Blocked(x, y, &newx, &newy);
5276 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5280 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5281 Moving2Blocked(x, y, &newx, &newy);
5283 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5286 MovDir[x][y] = old_move_dir;
5289 else if (move_pattern == MV_WHEN_PUSHED ||
5290 move_pattern == MV_WHEN_DROPPED)
5292 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5293 MovDir[x][y] = MV_NO_MOVING;
5297 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5299 static int test_xy[7][2] =
5309 static int test_dir[7] =
5319 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5320 int move_preference = -1000000; /* start with very low preference */
5321 int new_move_dir = MV_NO_MOVING;
5322 int start_test = RND(4);
5325 for (i = 0; i < NUM_DIRECTIONS; i++)
5327 int move_dir = test_dir[start_test + i];
5328 int move_dir_preference;
5330 xx = x + test_xy[start_test + i][0];
5331 yy = y + test_xy[start_test + i][1];
5333 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5334 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5336 new_move_dir = move_dir;
5341 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5344 move_dir_preference = -1 * RunnerVisit[xx][yy];
5345 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5346 move_dir_preference = PlayerVisit[xx][yy];
5348 if (move_dir_preference > move_preference)
5350 /* prefer field that has not been visited for the longest time */
5351 move_preference = move_dir_preference;
5352 new_move_dir = move_dir;
5354 else if (move_dir_preference == move_preference &&
5355 move_dir == old_move_dir)
5357 /* prefer last direction when all directions are preferred equally */
5358 move_preference = move_dir_preference;
5359 new_move_dir = move_dir;
5363 MovDir[x][y] = new_move_dir;
5364 if (old_move_dir != new_move_dir)
5367 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5375 static void TurnRound(int x, int y)
5377 int direction = MovDir[x][y];
5380 GfxDir[x][y] = MovDir[x][y];
5386 GfxDir[x][y] = MovDir[x][y];
5389 if (direction != MovDir[x][y])
5394 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5397 GfxAction[x][y] = ACTION_WAITING;
5401 static boolean JustBeingPushed(int x, int y)
5405 for (i = 0; i < MAX_PLAYERS; i++)
5407 struct PlayerInfo *player = &stored_player[i];
5409 if (player->active && player->is_pushing && player->MovPos)
5411 int next_jx = player->jx + (player->jx - player->last_jx);
5412 int next_jy = player->jy + (player->jy - player->last_jy);
5414 if (x == next_jx && y == next_jy)
5422 void StartMoving(int x, int y)
5425 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5427 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5428 int element = Feld[x][y];
5434 if (MovDelay[x][y] == 0)
5435 GfxAction[x][y] = ACTION_DEFAULT;
5437 /* !!! this should be handled more generic (not only for mole) !!! */
5438 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5439 GfxAction[x][y] = ACTION_DEFAULT;
5442 if (CAN_FALL(element) && y < lev_fieldy - 1)
5444 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5445 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5446 if (JustBeingPushed(x, y))
5449 if (element == EL_QUICKSAND_FULL)
5451 if (IS_FREE(x, y + 1))
5453 InitMovingField(x, y, MV_DOWN);
5454 started_moving = TRUE;
5456 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5457 Store[x][y] = EL_ROCK;
5459 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5461 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5464 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5466 if (!MovDelay[x][y])
5467 MovDelay[x][y] = TILEY + 1;
5476 Feld[x][y] = EL_QUICKSAND_EMPTY;
5477 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5478 Store[x][y + 1] = Store[x][y];
5481 PlayLevelSoundAction(x, y, ACTION_FILLING);
5483 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5487 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5488 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5490 InitMovingField(x, y, MV_DOWN);
5491 started_moving = TRUE;
5493 Feld[x][y] = EL_QUICKSAND_FILLING;
5494 Store[x][y] = element;
5496 PlayLevelSoundAction(x, y, ACTION_FILLING);
5498 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5501 else if (element == EL_MAGIC_WALL_FULL)
5503 if (IS_FREE(x, y + 1))
5505 InitMovingField(x, y, MV_DOWN);
5506 started_moving = TRUE;
5508 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5509 Store[x][y] = EL_CHANGED(Store[x][y]);
5511 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5513 if (!MovDelay[x][y])
5514 MovDelay[x][y] = TILEY/4 + 1;
5523 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5524 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5525 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5529 else if (element == EL_BD_MAGIC_WALL_FULL)
5531 if (IS_FREE(x, y + 1))
5533 InitMovingField(x, y, MV_DOWN);
5534 started_moving = TRUE;
5536 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5537 Store[x][y] = EL_CHANGED2(Store[x][y]);
5539 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5541 if (!MovDelay[x][y])
5542 MovDelay[x][y] = TILEY/4 + 1;
5551 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5552 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5553 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5557 else if (CAN_PASS_MAGIC_WALL(element) &&
5558 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5559 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5561 InitMovingField(x, y, MV_DOWN);
5562 started_moving = TRUE;
5565 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5566 EL_BD_MAGIC_WALL_FILLING);
5567 Store[x][y] = element;
5570 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5572 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5575 SplashAcid(x, y + 1);
5577 InitMovingField(x, y, MV_DOWN);
5578 started_moving = TRUE;
5580 Store[x][y] = EL_ACID;
5582 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5583 GfxAction[x][y + 1] = ACTION_ACTIVE;
5587 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5588 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5590 #if USE_IMPACT_BUGFIX
5591 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5592 CAN_FALL(element) && WasJustFalling[x][y] &&
5593 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5595 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5596 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5597 (Feld[x][y + 1] == EL_BLOCKED)))
5599 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5600 CAN_SMASH(element) && WasJustFalling[x][y] &&
5601 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5603 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5604 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5605 (Feld[x][y + 1] == EL_BLOCKED)))
5610 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5611 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5612 WasJustMoving[x][y] && !Pushed[x][y + 1])
5614 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5615 WasJustMoving[x][y])
5620 /* this is needed for a special case not covered by calling "Impact()"
5621 from "ContinueMoving()": if an element moves to a tile directly below
5622 another element which was just falling on that tile (which was empty
5623 in the previous frame), the falling element above would just stop
5624 instead of smashing the element below (in previous version, the above
5625 element was just checked for "moving" instead of "falling", resulting
5626 in incorrect smashes caused by horizontal movement of the above
5627 element; also, the case of the player being the element to smash was
5628 simply not covered here... :-/ ) */
5631 WasJustMoving[x][y] = 0;
5632 WasJustFalling[x][y] = 0;
5635 CheckCollision[x][y] = 0;
5638 if (IS_PLAYER(x, y + 1))
5639 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5644 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5646 if (MovDir[x][y] == MV_NO_MOVING)
5648 InitMovingField(x, y, MV_DOWN);
5649 started_moving = TRUE;
5652 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5654 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5655 MovDir[x][y] = MV_DOWN;
5657 InitMovingField(x, y, MV_DOWN);
5658 started_moving = TRUE;
5660 else if (element == EL_AMOEBA_DROP)
5662 Feld[x][y] = EL_AMOEBA_GROWING;
5663 Store[x][y] = EL_AMOEBA_WET;
5665 /* Store[x][y + 1] must be zero, because:
5666 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5669 #if OLD_GAME_BEHAVIOUR
5670 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5672 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5673 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5674 element != EL_DX_SUPABOMB)
5677 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5678 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5679 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5680 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5683 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5684 (IS_FREE(x - 1, y + 1) ||
5685 Feld[x - 1][y + 1] == EL_ACID));
5686 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5687 (IS_FREE(x + 1, y + 1) ||
5688 Feld[x + 1][y + 1] == EL_ACID));
5689 boolean can_fall_any = (can_fall_left || can_fall_right);
5690 boolean can_fall_both = (can_fall_left && can_fall_right);
5692 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5694 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5696 if (slippery_type == SLIPPERY_ONLY_LEFT)
5697 can_fall_right = FALSE;
5698 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5699 can_fall_left = FALSE;
5700 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5701 can_fall_right = FALSE;
5702 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5703 can_fall_left = FALSE;
5705 can_fall_any = (can_fall_left || can_fall_right);
5706 can_fall_both = (can_fall_left && can_fall_right);
5709 #if USE_NEW_SP_SLIPPERY
5710 /* !!! better use the same properties as for custom elements here !!! */
5711 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5712 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5714 can_fall_right = FALSE; /* slip down on left side */
5715 can_fall_both = FALSE;
5722 if (game.emulation == EMU_BOULDERDASH ||
5723 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5724 can_fall_right = FALSE; /* slip down on left side */
5726 can_fall_left = !(can_fall_right = RND(2));
5728 can_fall_both = FALSE;
5735 if (can_fall_both &&
5736 (game.emulation != EMU_BOULDERDASH &&
5737 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5738 can_fall_left = !(can_fall_right = RND(2));
5741 /* if not determined otherwise, prefer left side for slipping down */
5742 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5743 started_moving = TRUE;
5747 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5749 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5752 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5753 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5754 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5755 int belt_dir = game.belt_dir[belt_nr];
5757 if ((belt_dir == MV_LEFT && left_is_free) ||
5758 (belt_dir == MV_RIGHT && right_is_free))
5761 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5764 InitMovingField(x, y, belt_dir);
5765 started_moving = TRUE;
5768 Pushed[x][y] = TRUE;
5769 Pushed[nextx][y] = TRUE;
5772 GfxAction[x][y] = ACTION_DEFAULT;
5776 MovDir[x][y] = 0; /* if element was moving, stop it */
5781 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5783 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5785 if (CAN_MOVE(element) && !started_moving)
5788 int move_pattern = element_info[element].move_pattern;
5793 if (MovDir[x][y] == MV_NO_MOVING)
5795 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5796 x, y, element, element_info[element].token_name);
5797 printf("StartMoving(): This should never happen!\n");
5802 Moving2Blocked(x, y, &newx, &newy);
5805 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5808 if ((element == EL_SATELLITE ||
5809 element == EL_BALLOON ||
5810 element == EL_SPRING)
5811 && JustBeingPushed(x, y))
5818 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5819 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5821 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5822 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5823 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5827 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5828 element, element_info[element].token_name,
5829 WasJustMoving[x][y],
5830 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5831 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5832 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5833 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5837 WasJustMoving[x][y] = 0;
5840 CheckCollision[x][y] = 0;
5842 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5845 if (Feld[x][y] != element) /* element has changed */
5847 element = Feld[x][y];
5848 move_pattern = element_info[element].move_pattern;
5850 if (!CAN_MOVE(element))
5854 if (Feld[x][y] != element) /* element has changed */
5862 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5863 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5865 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5867 Moving2Blocked(x, y, &newx, &newy);
5868 if (Feld[newx][newy] == EL_BLOCKED)
5869 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5875 if (FrameCounter < 1 && x == 0 && y == 29)
5876 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5879 if (!MovDelay[x][y]) /* start new movement phase */
5881 /* all objects that can change their move direction after each step
5882 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5884 if (element != EL_YAMYAM &&
5885 element != EL_DARK_YAMYAM &&
5886 element != EL_PACMAN &&
5887 !(move_pattern & MV_ANY_DIRECTION) &&
5888 move_pattern != MV_TURNING_LEFT &&
5889 move_pattern != MV_TURNING_RIGHT &&
5890 move_pattern != MV_TURNING_LEFT_RIGHT &&
5891 move_pattern != MV_TURNING_RIGHT_LEFT &&
5892 move_pattern != MV_TURNING_RANDOM)
5897 if (FrameCounter < 1 && x == 0 && y == 29)
5898 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5901 if (MovDelay[x][y] && (element == EL_BUG ||
5902 element == EL_SPACESHIP ||
5903 element == EL_SP_SNIKSNAK ||
5904 element == EL_SP_ELECTRON ||
5905 element == EL_MOLE))
5906 DrawLevelField(x, y);
5910 if (MovDelay[x][y]) /* wait some time before next movement */
5915 if (element == EL_YAMYAM)
5918 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5919 DrawLevelElementAnimation(x, y, element);
5923 if (MovDelay[x][y]) /* element still has to wait some time */
5926 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5927 ResetGfxAnimation(x, y);
5931 if (GfxAction[x][y] != ACTION_WAITING)
5932 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5934 GfxAction[x][y] = ACTION_WAITING;
5938 if (element == EL_ROBOT ||
5940 element == EL_PACMAN ||
5942 element == EL_YAMYAM ||
5943 element == EL_DARK_YAMYAM)
5946 DrawLevelElementAnimation(x, y, element);
5948 DrawLevelElementAnimationIfNeeded(x, y, element);
5950 PlayLevelSoundAction(x, y, ACTION_WAITING);
5952 else if (element == EL_SP_ELECTRON)
5953 DrawLevelElementAnimationIfNeeded(x, y, element);
5954 else if (element == EL_DRAGON)
5957 int dir = MovDir[x][y];
5958 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5959 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5960 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5961 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5962 dir == MV_UP ? IMG_FLAMES_1_UP :
5963 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5964 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5967 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5970 GfxAction[x][y] = ACTION_ATTACKING;
5972 if (IS_PLAYER(x, y))
5973 DrawPlayerField(x, y);
5975 DrawLevelField(x, y);
5977 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5979 for (i = 1; i <= 3; i++)
5981 int xx = x + i * dx;
5982 int yy = y + i * dy;
5983 int sx = SCREENX(xx);
5984 int sy = SCREENY(yy);
5985 int flame_graphic = graphic + (i - 1);
5987 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5992 int flamed = MovingOrBlocked2Element(xx, yy);
5996 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5998 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5999 RemoveMovingField(xx, yy);
6001 RemoveField(xx, yy);
6003 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6006 RemoveMovingField(xx, yy);
6010 if (ChangeDelay[xx][yy])
6011 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6012 Feld[xx][yy] == EL_BLOCKED));
6016 ChangeDelay[xx][yy] = 0;
6018 Feld[xx][yy] = EL_FLAMES;
6019 if (IN_SCR_FIELD(sx, sy))
6021 DrawLevelFieldCrumbledSand(xx, yy);
6022 DrawGraphic(sx, sy, flame_graphic, frame);
6027 if (Feld[xx][yy] == EL_FLAMES)
6028 Feld[xx][yy] = EL_EMPTY;
6029 DrawLevelField(xx, yy);
6034 if (MovDelay[x][y]) /* element still has to wait some time */
6036 PlayLevelSoundAction(x, y, ACTION_WAITING);
6042 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6043 for all other elements GfxAction will be set by InitMovingField() */
6044 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6045 GfxAction[x][y] = ACTION_MOVING;
6049 /* now make next step */
6051 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6053 if (DONT_COLLIDE_WITH(element) &&
6054 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6055 !PLAYER_ENEMY_PROTECTED(newx, newy))
6058 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6062 /* player killed by element which is deadly when colliding with */
6064 KillHero(PLAYERINFO(newx, newy));
6071 else if (CAN_MOVE_INTO_ACID(element) &&
6072 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6073 (MovDir[x][y] == MV_DOWN ||
6074 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6076 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6077 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6081 else if ((element == EL_PENGUIN ||
6082 element == EL_ROBOT ||
6083 element == EL_SATELLITE ||
6084 element == EL_BALLOON ||
6085 IS_CUSTOM_ELEMENT(element)) &&
6086 IN_LEV_FIELD(newx, newy) &&
6087 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6090 SplashAcid(newx, newy);
6091 Store[x][y] = EL_ACID;
6093 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6095 if (Feld[newx][newy] == EL_EXIT_OPEN)
6099 DrawLevelField(x, y);
6101 Feld[x][y] = EL_EMPTY;
6102 DrawLevelField(x, y);
6105 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6106 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6107 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6109 local_player->friends_still_needed--;
6110 if (!local_player->friends_still_needed &&
6111 !local_player->GameOver && AllPlayersGone)
6112 local_player->LevelSolved = local_player->GameOver = TRUE;
6116 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6118 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6119 DrawLevelField(newx, newy);
6121 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6123 else if (!IS_FREE(newx, newy))
6125 GfxAction[x][y] = ACTION_WAITING;
6127 if (IS_PLAYER(x, y))
6128 DrawPlayerField(x, y);
6130 DrawLevelField(x, y);
6135 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6137 if (IS_FOOD_PIG(Feld[newx][newy]))
6139 if (IS_MOVING(newx, newy))
6140 RemoveMovingField(newx, newy);
6143 Feld[newx][newy] = EL_EMPTY;
6144 DrawLevelField(newx, newy);
6147 PlayLevelSound(x, y, SND_PIG_DIGGING);
6149 else if (!IS_FREE(newx, newy))
6151 if (IS_PLAYER(x, y))
6152 DrawPlayerField(x, y);
6154 DrawLevelField(x, y);
6163 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6166 else if (IS_CUSTOM_ELEMENT(element) &&
6167 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6171 !IS_FREE(newx, newy)
6176 int new_element = Feld[newx][newy];
6179 printf("::: '%s' digs '%s' [%d]\n",
6180 element_info[element].token_name,
6181 element_info[Feld[newx][newy]].token_name,
6182 StorePlayer[newx][newy]);
6185 if (!IS_FREE(newx, newy))
6187 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6188 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6191 /* no element can dig solid indestructible elements */
6192 if (IS_INDESTRUCTIBLE(new_element) &&
6193 !IS_DIGGABLE(new_element) &&
6194 !IS_COLLECTIBLE(new_element))
6197 if (AmoebaNr[newx][newy] &&
6198 (new_element == EL_AMOEBA_FULL ||
6199 new_element == EL_BD_AMOEBA ||
6200 new_element == EL_AMOEBA_GROWING))
6202 AmoebaCnt[AmoebaNr[newx][newy]]--;
6203 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6206 if (IS_MOVING(newx, newy))
6207 RemoveMovingField(newx, newy);
6210 RemoveField(newx, newy);
6211 DrawLevelField(newx, newy);
6214 /* if digged element was about to explode, prevent the explosion */
6215 ExplodeField[newx][newy] = EX_TYPE_NONE;
6217 PlayLevelSoundAction(x, y, action);
6222 Store[newx][newy] = EL_EMPTY;
6223 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6225 #if USE_CHANGE_TO_TRIGGERED
6226 int move_leave_element = element_info[element].move_leave_element;
6228 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6229 new_element : move_leave_element);
6231 Store[newx][newy] = element_info[element].move_leave_element;
6235 Store[newx][newy] = EL_EMPTY;
6236 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6237 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6238 Store[newx][newy] = element_info[element].move_leave_element;
6241 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6242 element_info[element].can_leave_element = TRUE;
6245 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6247 RunnerVisit[x][y] = FrameCounter;
6248 PlayerVisit[x][y] /= 8; /* expire player visit path */
6254 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6256 if (!IS_FREE(newx, newy))
6258 if (IS_PLAYER(x, y))
6259 DrawPlayerField(x, y);
6261 DrawLevelField(x, y);
6267 boolean wanna_flame = !RND(10);
6268 int dx = newx - x, dy = newy - y;
6269 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6270 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6271 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6272 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6273 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6274 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6277 IS_CLASSIC_ENEMY(element1) ||
6278 IS_CLASSIC_ENEMY(element2)) &&
6279 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6280 element1 != EL_FLAMES && element2 != EL_FLAMES)
6283 ResetGfxAnimation(x, y);
6284 GfxAction[x][y] = ACTION_ATTACKING;
6287 if (IS_PLAYER(x, y))
6288 DrawPlayerField(x, y);
6290 DrawLevelField(x, y);
6292 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6294 MovDelay[x][y] = 50;
6298 RemoveField(newx, newy);
6300 Feld[newx][newy] = EL_FLAMES;
6301 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6304 RemoveField(newx1, newy1);
6306 Feld[newx1][newy1] = EL_FLAMES;
6308 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6311 RemoveField(newx2, newy2);
6313 Feld[newx2][newy2] = EL_FLAMES;
6320 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6321 Feld[newx][newy] == EL_DIAMOND)
6323 if (IS_MOVING(newx, newy))
6324 RemoveMovingField(newx, newy);
6327 Feld[newx][newy] = EL_EMPTY;
6328 DrawLevelField(newx, newy);
6331 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6333 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6334 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6336 if (AmoebaNr[newx][newy])
6338 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6339 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6340 Feld[newx][newy] == EL_BD_AMOEBA)
6341 AmoebaCnt[AmoebaNr[newx][newy]]--;
6346 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6348 if (IS_MOVING(newx, newy))
6351 RemoveMovingField(newx, newy);
6355 Feld[newx][newy] = EL_EMPTY;
6356 DrawLevelField(newx, newy);
6359 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6361 else if ((element == EL_PACMAN || element == EL_MOLE)
6362 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6364 if (AmoebaNr[newx][newy])
6366 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6367 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6368 Feld[newx][newy] == EL_BD_AMOEBA)
6369 AmoebaCnt[AmoebaNr[newx][newy]]--;
6372 if (element == EL_MOLE)
6374 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6375 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6377 ResetGfxAnimation(x, y);
6378 GfxAction[x][y] = ACTION_DIGGING;
6379 DrawLevelField(x, y);
6381 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6383 return; /* wait for shrinking amoeba */
6385 else /* element == EL_PACMAN */
6387 Feld[newx][newy] = EL_EMPTY;
6388 DrawLevelField(newx, newy);
6389 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6392 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6393 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6394 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6396 /* wait for shrinking amoeba to completely disappear */
6399 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6401 /* object was running against a wall */
6406 if (move_pattern & MV_ANY_DIRECTION &&
6407 move_pattern == MovDir[x][y])
6409 int blocking_element =
6410 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6413 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6414 element_info[element].token_name,
6415 element_info[blocking_element].token_name,
6419 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6422 element = Feld[x][y]; /* element might have changed */
6427 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6428 DrawLevelElementAnimation(x, y, element);
6430 if (element == EL_BUG ||
6431 element == EL_SPACESHIP ||
6432 element == EL_SP_SNIKSNAK)
6433 DrawLevelField(x, y);
6434 else if (element == EL_MOLE)
6435 DrawLevelField(x, y);
6436 else if (element == EL_BD_BUTTERFLY ||
6437 element == EL_BD_FIREFLY)
6438 DrawLevelElementAnimationIfNeeded(x, y, element);
6439 else if (element == EL_SATELLITE)
6440 DrawLevelElementAnimationIfNeeded(x, y, element);
6441 else if (element == EL_SP_ELECTRON)
6442 DrawLevelElementAnimationIfNeeded(x, y, element);
6445 if (DONT_TOUCH(element))
6446 TestIfBadThingTouchesHero(x, y);
6449 PlayLevelSoundAction(x, y, ACTION_WAITING);
6455 InitMovingField(x, y, MovDir[x][y]);
6457 PlayLevelSoundAction(x, y, ACTION_MOVING);
6461 ContinueMoving(x, y);
6468 void ContinueMoving(int x, int y)
6470 int element = Feld[x][y];
6471 int stored = Store[x][y];
6472 struct ElementInfo *ei = &element_info[element];
6473 int direction = MovDir[x][y];
6474 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6475 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6476 int newx = x + dx, newy = y + dy;
6478 int nextx = newx + dx, nexty = newy + dy;
6481 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6482 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6484 boolean pushed_by_player = Pushed[x][y];
6486 boolean last_line = (newy == lev_fieldy - 1);
6488 MovPos[x][y] += getElementMoveStepsize(x, y);
6491 if (pushed_by_player && IS_PLAYER(x, y))
6493 /* special case: moving object pushed by player */
6494 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6497 if (pushed_by_player) /* special case: moving object pushed by player */
6498 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6501 if (ABS(MovPos[x][y]) < TILEX)
6503 DrawLevelField(x, y);
6505 return; /* element is still moving */
6508 /* element reached destination field */
6510 Feld[x][y] = EL_EMPTY;
6511 Feld[newx][newy] = element;
6512 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6515 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6517 element = Feld[newx][newy] = EL_ACID;
6520 else if (element == EL_MOLE)
6522 Feld[x][y] = EL_SAND;
6524 DrawLevelFieldCrumbledSandNeighbours(x, y);
6526 else if (element == EL_QUICKSAND_FILLING)
6528 element = Feld[newx][newy] = get_next_element(element);
6529 Store[newx][newy] = Store[x][y];
6531 else if (element == EL_QUICKSAND_EMPTYING)
6533 Feld[x][y] = get_next_element(element);
6534 element = Feld[newx][newy] = Store[x][y];
6536 else if (element == EL_MAGIC_WALL_FILLING)
6538 element = Feld[newx][newy] = get_next_element(element);
6539 if (!game.magic_wall_active)
6540 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6541 Store[newx][newy] = Store[x][y];
6543 else if (element == EL_MAGIC_WALL_EMPTYING)
6545 Feld[x][y] = get_next_element(element);
6546 if (!game.magic_wall_active)
6547 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6548 element = Feld[newx][newy] = Store[x][y];
6550 else if (element == EL_BD_MAGIC_WALL_FILLING)
6552 element = Feld[newx][newy] = get_next_element(element);
6553 if (!game.magic_wall_active)
6554 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6555 Store[newx][newy] = Store[x][y];
6557 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6559 Feld[x][y] = get_next_element(element);
6560 if (!game.magic_wall_active)
6561 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6562 element = Feld[newx][newy] = Store[x][y];
6564 else if (element == EL_AMOEBA_DROPPING)
6566 Feld[x][y] = get_next_element(element);
6567 element = Feld[newx][newy] = Store[x][y];
6569 else if (element == EL_SOKOBAN_OBJECT)
6572 Feld[x][y] = Back[x][y];
6574 if (Back[newx][newy])
6575 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6577 Back[x][y] = Back[newx][newy] = 0;
6580 else if (Store[x][y] == EL_ACID)
6582 element = Feld[newx][newy] = EL_ACID;
6586 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6587 ei->move_leave_element != EL_EMPTY &&
6588 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6589 Store[x][y] != EL_EMPTY))
6591 /* some elements can leave other elements behind after moving */
6593 Feld[x][y] = ei->move_leave_element;
6594 InitField(x, y, FALSE);
6596 if (GFX_CRUMBLED(Feld[x][y]))
6597 DrawLevelFieldCrumbledSandNeighbours(x, y);
6601 Store[x][y] = EL_EMPTY;
6605 MovDelay[newx][newy] = 0;
6607 if (CAN_CHANGE(element))
6609 /* copy element change control values to new field */
6610 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6611 ChangePage[newx][newy] = ChangePage[x][y];
6612 Changed[newx][newy] = Changed[x][y];
6613 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6616 ChangeDelay[x][y] = 0;
6617 ChangePage[x][y] = -1;
6618 Changed[x][y] = FALSE;
6619 ChangeEvent[x][y] = -1;
6621 /* copy animation control values to new field */
6622 GfxFrame[newx][newy] = GfxFrame[x][y];
6623 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6624 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6625 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6627 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6630 /* do this after checking for left-behind element */
6631 ResetGfxAnimation(x, y); /* reset animation values for old field */
6635 /* some elements can leave other elements behind after moving */
6637 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6638 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6639 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6641 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6642 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6646 int move_leave_element = ei->move_leave_element;
6648 #if USE_CHANGE_TO_TRIGGERED
6649 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6650 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6651 move_leave_element = stored;
6654 Feld[x][y] = move_leave_element;
6656 #if USE_PREVIOUS_MOVE_DIR
6657 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6658 MovDir[x][y] = direction;
6661 InitField(x, y, FALSE);
6663 if (GFX_CRUMBLED(Feld[x][y]))
6664 DrawLevelFieldCrumbledSandNeighbours(x, y);
6666 if (ELEM_IS_PLAYER(move_leave_element))
6667 RelocatePlayer(x, y, move_leave_element);
6672 /* some elements can leave other elements behind after moving */
6673 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6674 ei->move_leave_element != EL_EMPTY &&
6675 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6676 ei->can_leave_element_last))
6678 Feld[x][y] = ei->move_leave_element;
6679 InitField(x, y, FALSE);
6681 if (GFX_CRUMBLED(Feld[x][y]))
6682 DrawLevelFieldCrumbledSandNeighbours(x, y);
6685 ei->can_leave_element_last = ei->can_leave_element;
6686 ei->can_leave_element = FALSE;
6690 /* do this after checking for left-behind element */
6691 ResetGfxAnimation(x, y); /* reset animation values for old field */
6695 /* 2.1.1 (does not work correctly for spring) */
6696 if (!CAN_MOVE(element))
6697 MovDir[newx][newy] = 0;
6701 /* (does not work for falling objects that slide horizontally) */
6702 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6703 MovDir[newx][newy] = 0;
6706 if (!CAN_MOVE(element) ||
6707 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6708 MovDir[newx][newy] = 0;
6712 if (!CAN_MOVE(element) ||
6713 (CAN_FALL(element) && direction == MV_DOWN))
6714 GfxDir[x][y] = MovDir[newx][newy] = 0;
6716 if (!CAN_MOVE(element) ||
6717 (CAN_FALL(element) && direction == MV_DOWN &&
6718 (element == EL_SPRING ||
6719 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6720 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6721 GfxDir[x][y] = MovDir[newx][newy] = 0;
6727 DrawLevelField(x, y);
6728 DrawLevelField(newx, newy);
6730 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6732 /* prevent pushed element from moving on in pushed direction */
6733 if (pushed_by_player && CAN_MOVE(element) &&
6734 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6735 !(element_info[element].move_pattern & direction))
6736 TurnRound(newx, newy);
6739 /* prevent elements on conveyor belt from moving on in last direction */
6740 if (pushed_by_conveyor && CAN_FALL(element) &&
6741 direction & MV_HORIZONTAL)
6744 if (CAN_MOVE(element))
6745 InitMovDir(newx, newy);
6747 MovDir[newx][newy] = 0;
6749 MovDir[newx][newy] = 0;
6754 if (!pushed_by_player)
6756 int nextx = newx + dx, nexty = newy + dy;
6757 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6759 WasJustMoving[newx][newy] = 3;
6761 if (CAN_FALL(element) && direction == MV_DOWN)
6762 WasJustFalling[newx][newy] = 3;
6764 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6765 CheckCollision[newx][newy] = 2;
6768 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6770 TestIfBadThingTouchesHero(newx, newy);
6771 TestIfBadThingTouchesFriend(newx, newy);
6773 if (!IS_CUSTOM_ELEMENT(element))
6774 TestIfBadThingTouchesOtherBadThing(newx, newy);
6776 else if (element == EL_PENGUIN)
6777 TestIfFriendTouchesBadThing(newx, newy);
6779 #if USE_NEW_MOVE_STYLE
6781 if (CAN_FALL(element) && direction == MV_DOWN &&
6782 !last_line && IS_PLAYER(x, newy + 1))
6783 printf("::: we would now kill the player [%d]\n", FrameCounter);
6786 /* give the player one last chance (one more frame) to move away */
6787 if (CAN_FALL(element) && direction == MV_DOWN &&
6788 (last_line || (!IS_FREE(x, newy + 1) &&
6789 (!IS_PLAYER(x, newy + 1) ||
6790 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6793 if (CAN_FALL(element) && direction == MV_DOWN &&
6794 (last_line || !IS_FREE(x, newy + 1)))
6802 if (pushed_by_player && !game.use_change_when_pushing_bug)
6804 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6807 if (pushed_by_player)
6812 int dig_side = MV_DIR_OPPOSITE(direction);
6814 static int trigger_sides[4] =
6816 CH_SIDE_RIGHT, /* moving left */
6817 CH_SIDE_LEFT, /* moving right */
6818 CH_SIDE_BOTTOM, /* moving up */
6819 CH_SIDE_TOP, /* moving down */
6821 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6823 struct PlayerInfo *player = PLAYERINFO(x, y);
6825 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6826 player->index_bit, dig_side);
6827 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6828 player->index_bit, dig_side);
6833 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6837 if (ChangePage[newx][newy] != -1) /* delayed change */
6838 ChangeElement(newx, newy, ChangePage[newx][newy]);
6843 TestIfElementHitsCustomElement(newx, newy, direction);
6847 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6849 int hitting_element = Feld[newx][newy];
6851 /* !!! fix side (direction) orientation here and elsewhere !!! */
6852 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6856 if (IN_LEV_FIELD(nextx, nexty))
6858 int opposite_direction = MV_DIR_OPPOSITE(direction);
6859 int hitting_side = direction;
6860 int touched_side = opposite_direction;
6861 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6862 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6863 MovDir[nextx][nexty] != direction ||
6864 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6870 CheckElementChangeBySide(nextx, nexty, touched_element,
6871 CE_HIT_BY_SOMETHING, opposite_direction);
6873 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6874 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6876 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6878 struct ElementChangeInfo *change =
6879 &element_info[hitting_element].change_page[i];
6881 if (change->can_change &&
6882 change->has_event[CE_HITTING_X] &&
6883 change->trigger_side & touched_side &&
6884 change->trigger_element == touched_element)
6886 CheckElementChangeByPage(newx, newy, hitting_element,
6887 touched_element, CE_HITTING_X, i);
6893 if (IS_CUSTOM_ELEMENT(touched_element) &&
6894 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6896 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6898 struct ElementChangeInfo *change =
6899 &element_info[touched_element].change_page[i];
6901 if (change->can_change &&
6902 change->has_event[CE_HIT_BY_X] &&
6903 change->trigger_side & hitting_side &&
6904 change->trigger_element == hitting_element)
6906 CheckElementChangeByPage(nextx, nexty, touched_element,
6907 hitting_element, CE_HIT_BY_X,i);
6918 TestIfPlayerTouchesCustomElement(newx, newy);
6919 TestIfElementTouchesCustomElement(newx, newy);
6922 int AmoebeNachbarNr(int ax, int ay)
6925 int element = Feld[ax][ay];
6927 static int xy[4][2] =
6935 for (i = 0; i < NUM_DIRECTIONS; i++)
6937 int x = ax + xy[i][0];
6938 int y = ay + xy[i][1];
6940 if (!IN_LEV_FIELD(x, y))
6943 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6944 group_nr = AmoebaNr[x][y];
6950 void AmoebenVereinigen(int ax, int ay)
6952 int i, x, y, xx, yy;
6953 int new_group_nr = AmoebaNr[ax][ay];
6954 static int xy[4][2] =
6962 if (new_group_nr == 0)
6965 for (i = 0; i < NUM_DIRECTIONS; i++)
6970 if (!IN_LEV_FIELD(x, y))
6973 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6974 Feld[x][y] == EL_BD_AMOEBA ||
6975 Feld[x][y] == EL_AMOEBA_DEAD) &&
6976 AmoebaNr[x][y] != new_group_nr)
6978 int old_group_nr = AmoebaNr[x][y];
6980 if (old_group_nr == 0)
6983 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6984 AmoebaCnt[old_group_nr] = 0;
6985 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6986 AmoebaCnt2[old_group_nr] = 0;
6988 for (yy = 0; yy < lev_fieldy; yy++)
6990 for (xx = 0; xx < lev_fieldx; xx++)
6992 if (AmoebaNr[xx][yy] == old_group_nr)
6993 AmoebaNr[xx][yy] = new_group_nr;
7000 void AmoebeUmwandeln(int ax, int ay)
7004 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7006 int group_nr = AmoebaNr[ax][ay];
7011 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7012 printf("AmoebeUmwandeln(): This should never happen!\n");
7017 for (y = 0; y < lev_fieldy; y++)
7019 for (x = 0; x < lev_fieldx; x++)
7021 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7024 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7028 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7029 SND_AMOEBA_TURNING_TO_GEM :
7030 SND_AMOEBA_TURNING_TO_ROCK));
7035 static int xy[4][2] =
7043 for (i = 0; i < NUM_DIRECTIONS; i++)
7048 if (!IN_LEV_FIELD(x, y))
7051 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7053 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7054 SND_AMOEBA_TURNING_TO_GEM :
7055 SND_AMOEBA_TURNING_TO_ROCK));
7062 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7065 int group_nr = AmoebaNr[ax][ay];
7066 boolean done = FALSE;
7071 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7072 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7077 for (y = 0; y < lev_fieldy; y++)
7079 for (x = 0; x < lev_fieldx; x++)
7081 if (AmoebaNr[x][y] == group_nr &&
7082 (Feld[x][y] == EL_AMOEBA_DEAD ||
7083 Feld[x][y] == EL_BD_AMOEBA ||
7084 Feld[x][y] == EL_AMOEBA_GROWING))
7087 Feld[x][y] = new_element;
7088 InitField(x, y, FALSE);
7089 DrawLevelField(x, y);
7096 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7097 SND_BD_AMOEBA_TURNING_TO_ROCK :
7098 SND_BD_AMOEBA_TURNING_TO_GEM));
7101 void AmoebeWaechst(int x, int y)
7103 static unsigned long sound_delay = 0;
7104 static unsigned long sound_delay_value = 0;
7106 if (!MovDelay[x][y]) /* start new growing cycle */
7110 if (DelayReached(&sound_delay, sound_delay_value))
7113 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7115 if (Store[x][y] == EL_BD_AMOEBA)
7116 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7118 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7120 sound_delay_value = 30;
7124 if (MovDelay[x][y]) /* wait some time before growing bigger */
7127 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7129 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7130 6 - MovDelay[x][y]);
7132 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7135 if (!MovDelay[x][y])
7137 Feld[x][y] = Store[x][y];
7139 DrawLevelField(x, y);
7144 void AmoebaDisappearing(int x, int y)
7146 static unsigned long sound_delay = 0;
7147 static unsigned long sound_delay_value = 0;
7149 if (!MovDelay[x][y]) /* start new shrinking cycle */
7153 if (DelayReached(&sound_delay, sound_delay_value))
7154 sound_delay_value = 30;
7157 if (MovDelay[x][y]) /* wait some time before shrinking */
7160 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7162 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7163 6 - MovDelay[x][y]);
7165 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7168 if (!MovDelay[x][y])
7170 Feld[x][y] = EL_EMPTY;
7171 DrawLevelField(x, y);
7173 /* don't let mole enter this field in this cycle;
7174 (give priority to objects falling to this field from above) */
7180 void AmoebeAbleger(int ax, int ay)
7183 int element = Feld[ax][ay];
7184 int graphic = el2img(element);
7185 int newax = ax, neway = ay;
7186 static int xy[4][2] =
7194 if (!level.amoeba_speed)
7196 Feld[ax][ay] = EL_AMOEBA_DEAD;
7197 DrawLevelField(ax, ay);
7201 if (IS_ANIMATED(graphic))
7202 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7204 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7205 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7207 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7210 if (MovDelay[ax][ay])
7214 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7217 int x = ax + xy[start][0];
7218 int y = ay + xy[start][1];
7220 if (!IN_LEV_FIELD(x, y))
7224 if (IS_FREE(x, y) ||
7225 CAN_GROW_INTO(Feld[x][y]) ||
7226 Feld[x][y] == EL_QUICKSAND_EMPTY)
7232 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7233 if (IS_FREE(x, y) ||
7234 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7241 if (newax == ax && neway == ay)
7244 else /* normal or "filled" (BD style) amoeba */
7247 boolean waiting_for_player = FALSE;
7249 for (i = 0; i < NUM_DIRECTIONS; i++)
7251 int j = (start + i) % 4;
7252 int x = ax + xy[j][0];
7253 int y = ay + xy[j][1];
7255 if (!IN_LEV_FIELD(x, y))
7259 if (IS_FREE(x, y) ||
7260 CAN_GROW_INTO(Feld[x][y]) ||
7261 Feld[x][y] == EL_QUICKSAND_EMPTY)
7268 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7269 if (IS_FREE(x, y) ||
7270 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7277 else if (IS_PLAYER(x, y))
7278 waiting_for_player = TRUE;
7281 if (newax == ax && neway == ay) /* amoeba cannot grow */
7284 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7286 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7289 Feld[ax][ay] = EL_AMOEBA_DEAD;
7290 DrawLevelField(ax, ay);
7291 AmoebaCnt[AmoebaNr[ax][ay]]--;
7293 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7295 if (element == EL_AMOEBA_FULL)
7296 AmoebeUmwandeln(ax, ay);
7297 else if (element == EL_BD_AMOEBA)
7298 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7303 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7305 /* amoeba gets larger by growing in some direction */
7307 int new_group_nr = AmoebaNr[ax][ay];
7310 if (new_group_nr == 0)
7312 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7313 printf("AmoebeAbleger(): This should never happen!\n");
7318 AmoebaNr[newax][neway] = new_group_nr;
7319 AmoebaCnt[new_group_nr]++;
7320 AmoebaCnt2[new_group_nr]++;
7322 /* if amoeba touches other amoeba(s) after growing, unify them */
7323 AmoebenVereinigen(newax, neway);
7325 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7327 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7333 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7334 (neway == lev_fieldy - 1 && newax != ax))
7336 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7337 Store[newax][neway] = element;
7339 else if (neway == ay)
7341 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7343 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7345 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7350 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7351 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7352 Store[ax][ay] = EL_AMOEBA_DROP;
7353 ContinueMoving(ax, ay);
7357 DrawLevelField(newax, neway);
7360 void Life(int ax, int ay)
7363 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7365 int element = Feld[ax][ay];
7366 int graphic = el2img(element);
7367 boolean changed = FALSE;
7369 if (IS_ANIMATED(graphic))
7370 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7375 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7376 MovDelay[ax][ay] = life_time;
7378 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7381 if (MovDelay[ax][ay])
7385 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7387 int xx = ax+x1, yy = ay+y1;
7390 if (!IN_LEV_FIELD(xx, yy))
7393 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7395 int x = xx+x2, y = yy+y2;
7397 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7400 if (((Feld[x][y] == element ||
7401 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7403 (IS_FREE(x, y) && Stop[x][y]))
7407 if (xx == ax && yy == ay) /* field in the middle */
7409 if (nachbarn < life[0] || nachbarn > life[1])
7411 Feld[xx][yy] = EL_EMPTY;
7413 DrawLevelField(xx, yy);
7414 Stop[xx][yy] = TRUE;
7419 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7420 { /* free border field */
7421 if (nachbarn >= life[2] && nachbarn <= life[3])
7423 Feld[xx][yy] = element;
7424 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7426 DrawLevelField(xx, yy);
7427 Stop[xx][yy] = TRUE;
7432 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7433 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7434 { /* free border field */
7435 if (nachbarn >= life[2] && nachbarn <= life[3])
7437 Feld[xx][yy] = element;
7438 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7440 DrawLevelField(xx, yy);
7441 Stop[xx][yy] = TRUE;
7449 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7450 SND_GAME_OF_LIFE_GROWING);
7453 static void InitRobotWheel(int x, int y)
7455 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7458 static void RunRobotWheel(int x, int y)
7460 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7463 static void StopRobotWheel(int x, int y)
7465 if (ZX == x && ZY == y)
7469 static void InitTimegateWheel(int x, int y)
7472 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7474 /* another brainless, "type style" bug ... :-( */
7475 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7479 static void RunTimegateWheel(int x, int y)
7481 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7484 void CheckExit(int x, int y)
7486 if (local_player->gems_still_needed > 0 ||
7487 local_player->sokobanfields_still_needed > 0 ||
7488 local_player->lights_still_needed > 0)
7490 int element = Feld[x][y];
7491 int graphic = el2img(element);
7493 if (IS_ANIMATED(graphic))
7494 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7499 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7502 Feld[x][y] = EL_EXIT_OPENING;
7504 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7507 void CheckExitSP(int x, int y)
7509 if (local_player->gems_still_needed > 0)
7511 int element = Feld[x][y];
7512 int graphic = el2img(element);
7514 if (IS_ANIMATED(graphic))
7515 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7520 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7523 Feld[x][y] = EL_SP_EXIT_OPENING;
7525 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7528 static void CloseAllOpenTimegates()
7532 for (y = 0; y < lev_fieldy; y++)
7534 for (x = 0; x < lev_fieldx; x++)
7536 int element = Feld[x][y];
7538 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7540 Feld[x][y] = EL_TIMEGATE_CLOSING;
7542 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7544 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7551 void EdelsteinFunkeln(int x, int y)
7553 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7556 if (Feld[x][y] == EL_BD_DIAMOND)
7559 if (MovDelay[x][y] == 0) /* next animation frame */
7560 MovDelay[x][y] = 11 * !SimpleRND(500);
7562 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7566 if (setup.direct_draw && MovDelay[x][y])
7567 SetDrawtoField(DRAW_BUFFERED);
7569 DrawLevelElementAnimation(x, y, Feld[x][y]);
7571 if (MovDelay[x][y] != 0)
7573 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7574 10 - MovDelay[x][y]);
7576 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7578 if (setup.direct_draw)
7582 dest_x = FX + SCREENX(x) * TILEX;
7583 dest_y = FY + SCREENY(y) * TILEY;
7585 BlitBitmap(drawto_field, window,
7586 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7587 SetDrawtoField(DRAW_DIRECT);
7593 void MauerWaechst(int x, int y)
7597 if (!MovDelay[x][y]) /* next animation frame */
7598 MovDelay[x][y] = 3 * delay;
7600 if (MovDelay[x][y]) /* wait some time before next frame */
7604 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7606 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7607 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7609 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7612 if (!MovDelay[x][y])
7614 if (MovDir[x][y] == MV_LEFT)
7616 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7617 DrawLevelField(x - 1, y);
7619 else if (MovDir[x][y] == MV_RIGHT)
7621 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7622 DrawLevelField(x + 1, y);
7624 else if (MovDir[x][y] == MV_UP)
7626 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7627 DrawLevelField(x, y - 1);
7631 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7632 DrawLevelField(x, y + 1);
7635 Feld[x][y] = Store[x][y];
7637 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7638 DrawLevelField(x, y);
7643 void MauerAbleger(int ax, int ay)
7645 int element = Feld[ax][ay];
7646 int graphic = el2img(element);
7647 boolean oben_frei = FALSE, unten_frei = FALSE;
7648 boolean links_frei = FALSE, rechts_frei = FALSE;
7649 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7650 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7651 boolean new_wall = FALSE;
7653 if (IS_ANIMATED(graphic))
7654 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7656 if (!MovDelay[ax][ay]) /* start building new wall */
7657 MovDelay[ax][ay] = 6;
7659 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7662 if (MovDelay[ax][ay])
7666 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7668 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7670 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7672 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7675 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7676 element == EL_EXPANDABLE_WALL_ANY)
7680 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7681 Store[ax][ay-1] = element;
7682 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7683 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7684 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7685 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7690 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7691 Store[ax][ay+1] = element;
7692 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7693 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7694 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7695 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7700 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7701 element == EL_EXPANDABLE_WALL_ANY ||
7702 element == EL_EXPANDABLE_WALL)
7706 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7707 Store[ax-1][ay] = element;
7708 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7709 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7710 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7711 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7717 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7718 Store[ax+1][ay] = element;
7719 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7720 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7721 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7722 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7727 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7728 DrawLevelField(ax, ay);
7730 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7732 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7733 unten_massiv = TRUE;
7734 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7735 links_massiv = TRUE;
7736 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7737 rechts_massiv = TRUE;
7739 if (((oben_massiv && unten_massiv) ||
7740 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7741 element == EL_EXPANDABLE_WALL) &&
7742 ((links_massiv && rechts_massiv) ||
7743 element == EL_EXPANDABLE_WALL_VERTICAL))
7744 Feld[ax][ay] = EL_WALL;
7748 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7750 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7754 void CheckForDragon(int x, int y)
7757 boolean dragon_found = FALSE;
7758 static int xy[4][2] =
7766 for (i = 0; i < NUM_DIRECTIONS; i++)
7768 for (j = 0; j < 4; j++)
7770 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7772 if (IN_LEV_FIELD(xx, yy) &&
7773 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7775 if (Feld[xx][yy] == EL_DRAGON)
7776 dragon_found = TRUE;
7785 for (i = 0; i < NUM_DIRECTIONS; i++)
7787 for (j = 0; j < 3; j++)
7789 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7791 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7793 Feld[xx][yy] = EL_EMPTY;
7794 DrawLevelField(xx, yy);
7803 static void InitBuggyBase(int x, int y)
7805 int element = Feld[x][y];
7806 int activating_delay = FRAMES_PER_SECOND / 4;
7809 (element == EL_SP_BUGGY_BASE ?
7810 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7811 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7813 element == EL_SP_BUGGY_BASE_ACTIVE ?
7814 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7817 static void WarnBuggyBase(int x, int y)
7820 static int xy[4][2] =
7828 for (i = 0; i < NUM_DIRECTIONS; i++)
7830 int xx = x + xy[i][0], yy = y + xy[i][1];
7832 if (IS_PLAYER(xx, yy))
7834 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7841 static void InitTrap(int x, int y)
7843 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7846 static void ActivateTrap(int x, int y)
7848 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7851 static void ChangeActiveTrap(int x, int y)
7853 int graphic = IMG_TRAP_ACTIVE;
7855 /* if new animation frame was drawn, correct crumbled sand border */
7856 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7857 DrawLevelFieldCrumbledSand(x, y);
7860 static void ChangeElementNowExt(int x, int y, int target_element)
7862 int previous_move_direction = MovDir[x][y];
7864 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7865 IS_WALKABLE(Feld[x][y]));
7867 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7868 IS_WALKABLE(Feld[x][y]) &&
7872 /* check if element under player changes from accessible to unaccessible
7873 (needed for special case of dropping element which then changes) */
7874 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7875 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7878 printf("::: BOOOM! [%d, '%s']\n", target_element,
7879 element_info[target_element].token_name);
7891 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7892 RemoveMovingField(x, y);
7896 Feld[x][y] = target_element;
7899 Feld[x][y] = target_element;
7902 ResetGfxAnimation(x, y);
7903 ResetRandomAnimationValue(x, y);
7905 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7906 MovDir[x][y] = previous_move_direction;
7909 InitField_WithBug1(x, y, FALSE);
7911 InitField(x, y, FALSE);
7912 if (CAN_MOVE(Feld[x][y]))
7916 DrawLevelField(x, y);
7918 if (GFX_CRUMBLED(Feld[x][y]))
7919 DrawLevelFieldCrumbledSandNeighbours(x, y);
7923 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7927 TestIfBadThingTouchesHero(x, y);
7928 TestIfPlayerTouchesCustomElement(x, y);
7929 TestIfElementTouchesCustomElement(x, y);
7932 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7933 if (ELEM_IS_PLAYER(target_element))
7934 RelocatePlayer(x, y, target_element);
7937 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7939 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7943 TestIfBadThingTouchesHero(x, y);
7944 TestIfPlayerTouchesCustomElement(x, y);
7945 TestIfElementTouchesCustomElement(x, y);
7949 static boolean ChangeElementNow(int x, int y, int element, int page)
7951 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7953 int old_element = Feld[x][y];
7955 /* always use default change event to prevent running into a loop */
7956 if (ChangeEvent[x][y] == -1)
7957 ChangeEvent[x][y] = CE_DELAY;
7959 if (ChangeEvent[x][y] == CE_DELAY)
7961 /* reset actual trigger element and player */
7962 change->actual_trigger_element = EL_EMPTY;
7963 change->actual_trigger_player = EL_PLAYER_1;
7967 /* do not change any elements that have already changed in this frame */
7971 /* do not change already changed elements with same change event */
7972 if (Changed[x][y] & ChangeEvent[x][y])
7977 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7979 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7983 /* !!! indirect change before direct change !!! */
7984 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
7987 if (change->explode)
7994 if (change->use_target_content)
7996 boolean complete_replace = TRUE;
7997 boolean can_replace[3][3];
8000 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8003 boolean is_walkable;
8004 boolean is_diggable;
8005 boolean is_collectible;
8006 boolean is_removable;
8007 boolean is_destructible;
8008 int ex = x + xx - 1;
8009 int ey = y + yy - 1;
8010 int content_element = change->target_content[xx][yy];
8013 can_replace[xx][yy] = TRUE;
8015 if (ex == x && ey == y) /* do not check changing element itself */
8018 if (content_element == EL_EMPTY_SPACE)
8020 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8025 if (!IN_LEV_FIELD(ex, ey))
8027 can_replace[xx][yy] = FALSE;
8028 complete_replace = FALSE;
8034 if (Changed[ex][ey]) /* do not change already changed elements */
8036 can_replace[xx][yy] = FALSE;
8037 complete_replace = FALSE;
8045 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8046 e = MovingOrBlocked2Element(ex, ey);
8051 is_empty = (IS_FREE(ex, ey) ||
8052 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8053 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8054 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8058 is_empty = (IS_FREE(ex, ey) ||
8059 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8061 is_empty = (IS_FREE(ex, ey) ||
8062 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8067 is_walkable = (is_empty || IS_WALKABLE(e));
8068 is_diggable = (is_empty || IS_DIGGABLE(e));
8069 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8070 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8071 is_removable = (is_diggable || is_collectible);
8073 can_replace[xx][yy] =
8074 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8075 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8076 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8077 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8078 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8079 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8080 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8082 if (!can_replace[xx][yy])
8083 complete_replace = FALSE;
8085 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8086 IS_WALKABLE(content_element)));
8088 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8090 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8093 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8094 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8095 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8097 can_replace[xx][yy] = FALSE;
8098 complete_replace = FALSE;
8103 if (!change->only_if_complete || complete_replace)
8105 boolean something_has_changed = FALSE;
8107 if (change->only_if_complete && change->use_random_replace &&
8108 RND(100) < change->random_percentage)
8111 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8113 int ex = x + xx - 1;
8114 int ey = y + yy - 1;
8115 int content_element;
8117 if (can_replace[xx][yy] && (!change->use_random_replace ||
8118 RND(100) < change->random_percentage))
8120 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8121 RemoveMovingField(ex, ey);
8123 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8125 content_element = change->target_content[xx][yy];
8126 target_element = GET_TARGET_ELEMENT(content_element, change);
8128 ChangeElementNowExt(ex, ey, target_element);
8130 something_has_changed = TRUE;
8132 /* for symmetry reasons, freeze newly created border elements */
8133 if (ex != x || ey != y)
8134 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8138 if (something_has_changed)
8139 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8144 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8146 ChangeElementNowExt(x, y, target_element);
8148 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8152 /* this uses direct change before indirect change */
8153 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8159 static void ChangeElement(int x, int y, int page)
8161 int element = MovingOrBlocked2Element(x, y);
8162 struct ElementInfo *ei = &element_info[element];
8163 struct ElementChangeInfo *change = &ei->change_page[page];
8166 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8169 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8170 x, y, element, element_info[element].token_name);
8171 printf("ChangeElement(): This should never happen!\n");
8176 /* this can happen with classic bombs on walkable, changing elements */
8177 if (!CAN_CHANGE(element))
8180 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8181 ChangeDelay[x][y] = 0;
8187 if (ChangeDelay[x][y] == 0) /* initialize element change */
8189 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8190 RND(change->delay_random * change->delay_frames)) + 1;
8192 ResetGfxAnimation(x, y);
8193 ResetRandomAnimationValue(x, y);
8195 if (change->pre_change_function)
8196 change->pre_change_function(x, y);
8199 ChangeDelay[x][y]--;
8201 if (ChangeDelay[x][y] != 0) /* continue element change */
8203 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8205 if (IS_ANIMATED(graphic))
8206 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8208 if (change->change_function)
8209 change->change_function(x, y);
8211 else /* finish element change */
8213 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8215 page = ChangePage[x][y];
8216 ChangePage[x][y] = -1;
8218 change = &ei->change_page[page];
8222 if (IS_MOVING(x, y) && !change->explode)
8224 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8227 ChangeDelay[x][y] = 1; /* try change after next move step */
8228 ChangePage[x][y] = page; /* remember page to use for change */
8233 if (ChangeElementNow(x, y, element, page))
8235 if (change->post_change_function)
8236 change->post_change_function(x, y);
8241 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8242 int trigger_element,
8249 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8251 if (!(trigger_events[trigger_element][trigger_event]))
8254 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8256 int element = EL_CUSTOM_START + i;
8258 boolean change_element = FALSE;
8261 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8264 for (j = 0; j < element_info[element].num_change_pages; j++)
8266 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8268 if (change->can_change &&
8269 change->has_event[trigger_event] &&
8270 change->trigger_side & trigger_side &&
8271 change->trigger_player & trigger_player &&
8272 change->trigger_page & trigger_page_bits &&
8273 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8276 if (!(change->has_event[trigger_event]))
8277 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8278 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8281 change_element = TRUE;
8284 change->actual_trigger_element = trigger_element;
8285 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8291 if (!change_element)
8294 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8297 if (x == lx && y == ly) /* do not change trigger element itself */
8301 if (Feld[x][y] == element)
8303 ChangeDelay[x][y] = 1;
8304 ChangeEvent[x][y] = trigger_event;
8305 ChangeElement(x, y, page);
8313 static boolean CheckElementChangeExt(int x, int y,
8315 int trigger_element,
8321 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8324 if (Feld[x][y] == EL_BLOCKED)
8326 Blocked2Moving(x, y, &x, &y);
8327 element = Feld[x][y];
8331 if (Feld[x][y] != element) /* check if element has already changed */
8334 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8335 Feld[x][y], element_info[Feld[x][y]].token_name,
8336 element, element_info[element].token_name,
8345 if (trigger_page < 0)
8347 boolean change_element = FALSE;
8350 for (i = 0; i < element_info[element].num_change_pages; i++)
8352 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8354 if (change->can_change &&
8355 change->has_event[trigger_event] &&
8356 change->trigger_side & trigger_side &&
8357 change->trigger_player & trigger_player)
8359 change_element = TRUE;
8362 change->actual_trigger_element = trigger_element;
8363 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8369 if (!change_element)
8374 struct ElementInfo *ei = &element_info[element];
8375 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8377 change->actual_trigger_element = trigger_element;
8378 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8383 /* !!! this check misses pages with same event, but different side !!! */
8385 if (trigger_page < 0)
8386 trigger_page = element_info[element].event_page_nr[trigger_event];
8388 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8392 ChangeDelay[x][y] = 1;
8393 ChangeEvent[x][y] = trigger_event;
8394 ChangeElement(x, y, trigger_page);
8399 static void PlayPlayerSound(struct PlayerInfo *player)
8401 int jx = player->jx, jy = player->jy;
8402 int element = player->element_nr;
8403 int last_action = player->last_action_waiting;
8404 int action = player->action_waiting;
8406 if (player->is_waiting)
8408 if (action != last_action)
8409 PlayLevelSoundElementAction(jx, jy, element, action);
8411 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8415 if (action != last_action)
8416 StopSound(element_info[element].sound[last_action]);
8418 if (last_action == ACTION_SLEEPING)
8419 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8423 static void PlayAllPlayersSound()
8427 for (i = 0; i < MAX_PLAYERS; i++)
8428 if (stored_player[i].active)
8429 PlayPlayerSound(&stored_player[i]);
8432 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8434 boolean last_waiting = player->is_waiting;
8435 int move_dir = player->MovDir;
8437 player->last_action_waiting = player->action_waiting;
8441 if (!last_waiting) /* not waiting -> waiting */
8443 player->is_waiting = TRUE;
8445 player->frame_counter_bored =
8447 game.player_boring_delay_fixed +
8448 SimpleRND(game.player_boring_delay_random);
8449 player->frame_counter_sleeping =
8451 game.player_sleeping_delay_fixed +
8452 SimpleRND(game.player_sleeping_delay_random);
8454 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8457 if (game.player_sleeping_delay_fixed +
8458 game.player_sleeping_delay_random > 0 &&
8459 player->anim_delay_counter == 0 &&
8460 player->post_delay_counter == 0 &&
8461 FrameCounter >= player->frame_counter_sleeping)
8462 player->is_sleeping = TRUE;
8463 else if (game.player_boring_delay_fixed +
8464 game.player_boring_delay_random > 0 &&
8465 FrameCounter >= player->frame_counter_bored)
8466 player->is_bored = TRUE;
8468 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8469 player->is_bored ? ACTION_BORING :
8472 if (player->is_sleeping)
8474 if (player->num_special_action_sleeping > 0)
8476 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8478 int last_special_action = player->special_action_sleeping;
8479 int num_special_action = player->num_special_action_sleeping;
8480 int special_action =
8481 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8482 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8483 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8484 last_special_action + 1 : ACTION_SLEEPING);
8485 int special_graphic =
8486 el_act_dir2img(player->element_nr, special_action, move_dir);
8488 player->anim_delay_counter =
8489 graphic_info[special_graphic].anim_delay_fixed +
8490 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8491 player->post_delay_counter =
8492 graphic_info[special_graphic].post_delay_fixed +
8493 SimpleRND(graphic_info[special_graphic].post_delay_random);
8495 player->special_action_sleeping = special_action;
8498 if (player->anim_delay_counter > 0)
8500 player->action_waiting = player->special_action_sleeping;
8501 player->anim_delay_counter--;
8503 else if (player->post_delay_counter > 0)
8505 player->post_delay_counter--;
8509 else if (player->is_bored)
8511 if (player->num_special_action_bored > 0)
8513 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8515 int special_action =
8516 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8517 int special_graphic =
8518 el_act_dir2img(player->element_nr, special_action, move_dir);
8520 player->anim_delay_counter =
8521 graphic_info[special_graphic].anim_delay_fixed +
8522 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8523 player->post_delay_counter =
8524 graphic_info[special_graphic].post_delay_fixed +
8525 SimpleRND(graphic_info[special_graphic].post_delay_random);
8527 player->special_action_bored = special_action;
8530 if (player->anim_delay_counter > 0)
8532 player->action_waiting = player->special_action_bored;
8533 player->anim_delay_counter--;
8535 else if (player->post_delay_counter > 0)
8537 player->post_delay_counter--;
8542 else if (last_waiting) /* waiting -> not waiting */
8544 player->is_waiting = FALSE;
8545 player->is_bored = FALSE;
8546 player->is_sleeping = FALSE;
8548 player->frame_counter_bored = -1;
8549 player->frame_counter_sleeping = -1;
8551 player->anim_delay_counter = 0;
8552 player->post_delay_counter = 0;
8554 player->action_waiting = ACTION_DEFAULT;
8556 player->special_action_bored = ACTION_DEFAULT;
8557 player->special_action_sleeping = ACTION_DEFAULT;
8562 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8565 static byte stored_player_action[MAX_PLAYERS];
8566 static int num_stored_actions = 0;
8568 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8569 int left = player_action & JOY_LEFT;
8570 int right = player_action & JOY_RIGHT;
8571 int up = player_action & JOY_UP;
8572 int down = player_action & JOY_DOWN;
8573 int button1 = player_action & JOY_BUTTON_1;
8574 int button2 = player_action & JOY_BUTTON_2;
8575 int dx = (left ? -1 : right ? 1 : 0);
8576 int dy = (up ? -1 : down ? 1 : 0);
8579 stored_player_action[player->index_nr] = 0;
8580 num_stored_actions++;
8584 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8587 if (!player->active || tape.pausing)
8591 printf("::: [%d %d %d %d] [%d %d]\n",
8592 left, right, up, down, button1, button2);
8598 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8603 if (player->MovPos == 0)
8604 CheckGravityMovement(player);
8607 snapped = SnapField(player, dx, dy);
8611 dropped = DropElement(player);
8613 moved = MovePlayer(player, dx, dy);
8616 if (tape.single_step && tape.recording && !tape.pausing)
8618 if (button1 || (dropped && !moved))
8620 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8621 SnapField(player, 0, 0); /* stop snapping */
8625 SetPlayerWaiting(player, FALSE);
8628 return player_action;
8630 stored_player_action[player->index_nr] = player_action;
8636 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8639 /* no actions for this player (no input at player's configured device) */
8641 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8642 SnapField(player, 0, 0);
8643 CheckGravityMovementWhenNotMoving(player);
8645 if (player->MovPos == 0)
8646 SetPlayerWaiting(player, TRUE);
8648 if (player->MovPos == 0) /* needed for tape.playing */
8649 player->is_moving = FALSE;
8651 player->is_dropping = FALSE;
8657 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8659 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8661 TapeRecordAction(stored_player_action);
8662 num_stored_actions = 0;
8669 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8671 static byte stored_player_action[MAX_PLAYERS];
8672 static int num_stored_actions = 0;
8673 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8674 int left = player_action & JOY_LEFT;
8675 int right = player_action & JOY_RIGHT;
8676 int up = player_action & JOY_UP;
8677 int down = player_action & JOY_DOWN;
8678 int button1 = player_action & JOY_BUTTON_1;
8679 int button2 = player_action & JOY_BUTTON_2;
8680 int dx = (left ? -1 : right ? 1 : 0);
8681 int dy = (up ? -1 : down ? 1 : 0);
8683 stored_player_action[player->index_nr] = 0;
8684 num_stored_actions++;
8686 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8688 if (!player->active || tape.pausing)
8693 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8696 snapped = SnapField(player, dx, dy);
8700 dropped = DropElement(player);
8702 moved = MovePlayer(player, dx, dy);
8705 if (tape.single_step && tape.recording && !tape.pausing)
8707 if (button1 || (dropped && !moved))
8709 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8710 SnapField(player, 0, 0); /* stop snapping */
8714 stored_player_action[player->index_nr] = player_action;
8718 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8720 /* no actions for this player (no input at player's configured device) */
8722 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8723 SnapField(player, 0, 0);
8724 CheckGravityMovementWhenNotMoving(player);
8726 if (player->MovPos == 0)
8727 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8729 if (player->MovPos == 0) /* needed for tape.playing */
8730 player->is_moving = FALSE;
8733 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8735 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8737 TapeRecordAction(stored_player_action);
8738 num_stored_actions = 0;
8743 void AdvanceFrameAndPlayerCounters(int player_nr)
8747 /* advance frame counters (global frame counter and time frame counter) */
8751 /* advance player counters (counters for move delay, move animation etc.) */
8752 for (i = 0; i < MAX_PLAYERS; i++)
8754 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8756 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8758 if (!advance_player_counters) /* not all players may be affected */
8761 stored_player[i].Frame += move_frames;
8763 if (stored_player[i].MovPos != 0)
8764 stored_player[i].StepFrame += move_frames;
8766 #if USE_NEW_MOVE_DELAY
8767 if (stored_player[i].move_delay > 0)
8768 stored_player[i].move_delay--;
8771 #if USE_NEW_PUSH_DELAY
8772 /* due to bugs in previous versions, counter must count up, not down */
8773 if (stored_player[i].push_delay != -1)
8774 stored_player[i].push_delay++;
8777 if (stored_player[i].drop_delay > 0)
8778 stored_player[i].drop_delay--;
8784 static unsigned long game_frame_delay = 0;
8785 unsigned long game_frame_delay_value;
8786 int magic_wall_x = 0, magic_wall_y = 0;
8787 int i, x, y, element, graphic;
8788 byte *recorded_player_action;
8789 byte summarized_player_action = 0;
8791 byte tape_action[MAX_PLAYERS];
8794 if (game_status != GAME_MODE_PLAYING)
8797 game_frame_delay_value =
8798 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8800 if (tape.playing && tape.warp_forward && !tape.pausing)
8801 game_frame_delay_value = 0;
8803 /* ---------- main game synchronization point ---------- */
8805 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8807 if (network_playing && !network_player_action_received)
8811 printf("DEBUG: try to get network player actions in time\n");
8815 #if defined(NETWORK_AVALIABLE)
8816 /* last chance to get network player actions without main loop delay */
8820 if (game_status != GAME_MODE_PLAYING)
8823 if (!network_player_action_received)
8827 printf("DEBUG: failed to get network player actions in time\n");
8838 printf("::: getting new tape action [%d]\n", FrameCounter);
8841 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8844 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8845 if (recorded_player_action == NULL && tape.pausing)
8850 printf("::: %d\n", stored_player[0].action);
8854 if (recorded_player_action != NULL)
8855 for (i = 0; i < MAX_PLAYERS; i++)
8856 stored_player[i].action = recorded_player_action[i];
8859 for (i = 0; i < MAX_PLAYERS; i++)
8861 summarized_player_action |= stored_player[i].action;
8863 if (!network_playing)
8864 stored_player[i].effective_action = stored_player[i].action;
8867 #if defined(NETWORK_AVALIABLE)
8868 if (network_playing)
8869 SendToServer_MovePlayer(summarized_player_action);
8872 if (!options.network && !setup.team_mode)
8873 local_player->effective_action = summarized_player_action;
8876 if (recorded_player_action != NULL)
8877 for (i = 0; i < MAX_PLAYERS; i++)
8878 stored_player[i].effective_action = recorded_player_action[i];
8882 for (i = 0; i < MAX_PLAYERS; i++)
8884 tape_action[i] = stored_player[i].effective_action;
8886 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8887 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8890 /* only save actions from input devices, but not programmed actions */
8892 TapeRecordAction(tape_action);
8895 for (i = 0; i < MAX_PLAYERS; i++)
8897 int actual_player_action = stored_player[i].effective_action;
8900 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8901 - rnd_equinox_tetrachloride 048
8902 - rnd_equinox_tetrachloride_ii 096
8903 - rnd_emanuel_schmieg 002
8904 - doctor_sloan_ww 001, 020
8906 if (stored_player[i].MovPos == 0)
8907 CheckGravityMovement(&stored_player[i]);
8911 /* overwrite programmed action with tape action */
8912 if (stored_player[i].programmed_action)
8913 actual_player_action = stored_player[i].programmed_action;
8917 if (stored_player[i].programmed_action)
8918 printf("::: %d\n", stored_player[i].programmed_action);
8921 if (recorded_player_action)
8924 if (stored_player[i].programmed_action &&
8925 stored_player[i].programmed_action != recorded_player_action[i])
8926 printf("::: %d: %d <-> %d\n", i,
8927 stored_player[i].programmed_action, recorded_player_action[i]);
8931 actual_player_action = recorded_player_action[i];
8936 /* overwrite tape action with programmed action */
8937 if (stored_player[i].programmed_action)
8938 actual_player_action = stored_player[i].programmed_action;
8943 printf("::: action: %d: %x [%d]\n",
8944 stored_player[i].MovPos, actual_player_action, FrameCounter);
8948 PlayerActions(&stored_player[i], actual_player_action);
8950 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8952 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8953 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8956 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8961 TapeRecordAction(tape_action);
8964 network_player_action_received = FALSE;
8966 ScrollScreen(NULL, SCROLL_GO_ON);
8972 for (i = 0; i < MAX_PLAYERS; i++)
8973 stored_player[i].Frame++;
8977 /* for backwards compatibility, the following code emulates a fixed bug that
8978 occured when pushing elements (causing elements that just made their last
8979 pushing step to already (if possible) make their first falling step in the
8980 same game frame, which is bad); this code is also needed to use the famous
8981 "spring push bug" which is used in older levels and might be wanted to be
8982 used also in newer levels, but in this case the buggy pushing code is only
8983 affecting the "spring" element and no other elements */
8986 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8988 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8991 for (i = 0; i < MAX_PLAYERS; i++)
8993 struct PlayerInfo *player = &stored_player[i];
8998 if (player->active && player->is_pushing && player->is_moving &&
9000 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9001 Feld[x][y] == EL_SPRING))
9003 if (player->active && player->is_pushing && player->is_moving &&
9007 ContinueMoving(x, y);
9009 /* continue moving after pushing (this is actually a bug) */
9010 if (!IS_MOVING(x, y))
9019 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9021 Changed[x][y] = FALSE;
9022 ChangeEvent[x][y] = -1;
9024 #if USE_NEW_BLOCK_STYLE
9025 /* this must be handled before main playfield loop */
9026 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9029 if (MovDelay[x][y] <= 0)
9035 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9037 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9038 printf("GameActions(): This should never happen!\n");
9040 ChangePage[x][y] = -1;
9045 if (WasJustMoving[x][y] > 0)
9046 WasJustMoving[x][y]--;
9047 if (WasJustFalling[x][y] > 0)
9048 WasJustFalling[x][y]--;
9049 if (CheckCollision[x][y] > 0)
9050 CheckCollision[x][y]--;
9055 /* reset finished pushing action (not done in ContinueMoving() to allow
9056 continous pushing animation for elements with zero push delay) */
9057 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9059 ResetGfxAnimation(x, y);
9060 DrawLevelField(x, y);
9065 if (IS_BLOCKED(x, y))
9069 Blocked2Moving(x, y, &oldx, &oldy);
9070 if (!IS_MOVING(oldx, oldy))
9072 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9073 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9074 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9075 printf("GameActions(): This should never happen!\n");
9081 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9083 element = Feld[x][y];
9085 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9087 graphic = el2img(element);
9093 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9095 element = graphic = 0;
9099 if (graphic_info[graphic].anim_global_sync)
9100 GfxFrame[x][y] = FrameCounter;
9102 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9103 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9104 ResetRandomAnimationValue(x, y);
9106 SetRandomAnimationValue(x, y);
9109 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9112 if (IS_INACTIVE(element))
9114 if (IS_ANIMATED(graphic))
9115 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9121 /* this may take place after moving, so 'element' may have changed */
9123 if (IS_CHANGING(x, y))
9125 if (IS_CHANGING(x, y) &&
9126 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9130 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9131 element_info[element].event_page_nr[CE_DELAY]);
9133 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9136 element = Feld[x][y];
9137 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9141 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9146 element = Feld[x][y];
9147 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9149 if (element == EL_MOLE)
9150 printf("::: %d, %d, %d [%d]\n",
9151 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9155 if (element == EL_YAMYAM)
9156 printf("::: %d, %d, %d\n",
9157 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9161 if (IS_ANIMATED(graphic) &&
9165 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9168 if (element == EL_BUG)
9169 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9173 if (element == EL_MOLE)
9174 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9178 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9179 EdelsteinFunkeln(x, y);
9181 else if ((element == EL_ACID ||
9182 element == EL_EXIT_OPEN ||
9183 element == EL_SP_EXIT_OPEN ||
9184 element == EL_SP_TERMINAL ||
9185 element == EL_SP_TERMINAL_ACTIVE ||
9186 element == EL_EXTRA_TIME ||
9187 element == EL_SHIELD_NORMAL ||
9188 element == EL_SHIELD_DEADLY) &&
9189 IS_ANIMATED(graphic))
9190 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9191 else if (IS_MOVING(x, y))
9192 ContinueMoving(x, y);
9193 else if (IS_ACTIVE_BOMB(element))
9194 CheckDynamite(x, y);
9196 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9197 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9199 else if (element == EL_AMOEBA_GROWING)
9200 AmoebeWaechst(x, y);
9201 else if (element == EL_AMOEBA_SHRINKING)
9202 AmoebaDisappearing(x, y);
9204 #if !USE_NEW_AMOEBA_CODE
9205 else if (IS_AMOEBALIVE(element))
9206 AmoebeAbleger(x, y);
9209 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9211 else if (element == EL_EXIT_CLOSED)
9213 else if (element == EL_SP_EXIT_CLOSED)
9215 else if (element == EL_EXPANDABLE_WALL_GROWING)
9217 else if (element == EL_EXPANDABLE_WALL ||
9218 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9219 element == EL_EXPANDABLE_WALL_VERTICAL ||
9220 element == EL_EXPANDABLE_WALL_ANY)
9222 else if (element == EL_FLAMES)
9223 CheckForDragon(x, y);
9225 else if (IS_AUTO_CHANGING(element))
9226 ChangeElement(x, y);
9228 else if (element == EL_EXPLOSION)
9229 ; /* drawing of correct explosion animation is handled separately */
9230 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9231 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9234 /* this may take place after moving, so 'element' may have changed */
9235 if (IS_AUTO_CHANGING(Feld[x][y]))
9236 ChangeElement(x, y);
9239 if (IS_BELT_ACTIVE(element))
9240 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9242 if (game.magic_wall_active)
9244 int jx = local_player->jx, jy = local_player->jy;
9246 /* play the element sound at the position nearest to the player */
9247 if ((element == EL_MAGIC_WALL_FULL ||
9248 element == EL_MAGIC_WALL_ACTIVE ||
9249 element == EL_MAGIC_WALL_EMPTYING ||
9250 element == EL_BD_MAGIC_WALL_FULL ||
9251 element == EL_BD_MAGIC_WALL_ACTIVE ||
9252 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9253 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9261 #if USE_NEW_AMOEBA_CODE
9262 /* new experimental amoeba growth stuff */
9264 if (!(FrameCounter % 8))
9267 static unsigned long random = 1684108901;
9269 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9272 x = (random >> 10) % lev_fieldx;
9273 y = (random >> 20) % lev_fieldy;
9275 x = RND(lev_fieldx);
9276 y = RND(lev_fieldy);
9278 element = Feld[x][y];
9281 if (!IS_PLAYER(x,y) &&
9282 (element == EL_EMPTY ||
9283 CAN_GROW_INTO(element) ||
9284 element == EL_QUICKSAND_EMPTY ||
9285 element == EL_ACID_SPLASH_LEFT ||
9286 element == EL_ACID_SPLASH_RIGHT))
9288 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9289 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9290 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9291 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9292 Feld[x][y] = EL_AMOEBA_DROP;
9295 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9296 if (!IS_PLAYER(x,y) &&
9297 (element == EL_EMPTY ||
9298 element == EL_SAND ||
9299 element == EL_QUICKSAND_EMPTY ||
9300 element == EL_ACID_SPLASH_LEFT ||
9301 element == EL_ACID_SPLASH_RIGHT))
9303 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9304 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9305 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9306 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9307 Feld[x][y] = EL_AMOEBA_DROP;
9311 random = random * 129 + 1;
9317 if (game.explosions_delayed)
9320 game.explosions_delayed = FALSE;
9322 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9324 element = Feld[x][y];
9326 if (ExplodeField[x][y])
9327 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9328 else if (element == EL_EXPLOSION)
9329 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9331 ExplodeField[x][y] = EX_TYPE_NONE;
9334 game.explosions_delayed = TRUE;
9337 if (game.magic_wall_active)
9339 if (!(game.magic_wall_time_left % 4))
9341 int element = Feld[magic_wall_x][magic_wall_y];
9343 if (element == EL_BD_MAGIC_WALL_FULL ||
9344 element == EL_BD_MAGIC_WALL_ACTIVE ||
9345 element == EL_BD_MAGIC_WALL_EMPTYING)
9346 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9348 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9351 if (game.magic_wall_time_left > 0)
9353 game.magic_wall_time_left--;
9354 if (!game.magic_wall_time_left)
9356 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9358 element = Feld[x][y];
9360 if (element == EL_MAGIC_WALL_ACTIVE ||
9361 element == EL_MAGIC_WALL_FULL)
9363 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9364 DrawLevelField(x, y);
9366 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9367 element == EL_BD_MAGIC_WALL_FULL)
9369 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9370 DrawLevelField(x, y);
9374 game.magic_wall_active = FALSE;
9379 if (game.light_time_left > 0)
9381 game.light_time_left--;
9383 if (game.light_time_left == 0)
9384 RedrawAllLightSwitchesAndInvisibleElements();
9387 if (game.timegate_time_left > 0)
9389 game.timegate_time_left--;
9391 if (game.timegate_time_left == 0)
9392 CloseAllOpenTimegates();
9395 for (i = 0; i < MAX_PLAYERS; i++)
9397 struct PlayerInfo *player = &stored_player[i];
9399 if (SHIELD_ON(player))
9401 if (player->shield_deadly_time_left)
9402 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9403 else if (player->shield_normal_time_left)
9404 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9408 if (TimeFrames >= FRAMES_PER_SECOND)
9413 for (i = 0; i < MAX_PLAYERS; i++)
9415 struct PlayerInfo *player = &stored_player[i];
9417 if (SHIELD_ON(player))
9419 player->shield_normal_time_left--;
9421 if (player->shield_deadly_time_left > 0)
9422 player->shield_deadly_time_left--;
9426 if (!level.use_step_counter)
9434 if (TimeLeft <= 10 && setup.time_limit)
9435 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9437 DrawGameValue_Time(TimeLeft);
9439 if (!TimeLeft && setup.time_limit)
9440 for (i = 0; i < MAX_PLAYERS; i++)
9441 KillHero(&stored_player[i]);
9443 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9444 DrawGameValue_Time(TimePlayed);
9447 if (tape.recording || tape.playing)
9448 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9452 PlayAllPlayersSound();
9454 if (options.debug) /* calculate frames per second */
9456 static unsigned long fps_counter = 0;
9457 static int fps_frames = 0;
9458 unsigned long fps_delay_ms = Counter() - fps_counter;
9462 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9464 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9467 fps_counter = Counter();
9470 redraw_mask |= REDRAW_FPS;
9474 if (stored_player[0].jx != stored_player[0].last_jx ||
9475 stored_player[0].jy != stored_player[0].last_jy)
9476 printf("::: %d, %d, %d, %d, %d\n",
9477 stored_player[0].MovDir,
9478 stored_player[0].MovPos,
9479 stored_player[0].GfxPos,
9480 stored_player[0].Frame,
9481 stored_player[0].StepFrame);
9484 #if USE_NEW_MOVE_DELAY
9485 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9490 for (i = 0; i < MAX_PLAYERS; i++)
9493 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9495 stored_player[i].Frame += move_frames;
9497 if (stored_player[i].MovPos != 0)
9498 stored_player[i].StepFrame += move_frames;
9500 #if USE_NEW_MOVE_DELAY
9501 if (stored_player[i].move_delay > 0)
9502 stored_player[i].move_delay--;
9505 if (stored_player[i].drop_delay > 0)
9506 stored_player[i].drop_delay--;
9511 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9513 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9515 local_player->show_envelope = 0;
9519 #if USE_NEW_RANDOMIZE
9520 /* use random number generator in every frame to make it less predictable */
9521 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9526 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9528 int min_x = x, min_y = y, max_x = x, max_y = y;
9531 for (i = 0; i < MAX_PLAYERS; i++)
9533 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9535 if (!stored_player[i].active || &stored_player[i] == player)
9538 min_x = MIN(min_x, jx);
9539 min_y = MIN(min_y, jy);
9540 max_x = MAX(max_x, jx);
9541 max_y = MAX(max_y, jy);
9544 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9547 static boolean AllPlayersInVisibleScreen()
9551 for (i = 0; i < MAX_PLAYERS; i++)
9553 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9555 if (!stored_player[i].active)
9558 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9565 void ScrollLevel(int dx, int dy)
9567 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9570 BlitBitmap(drawto_field, drawto_field,
9571 FX + TILEX * (dx == -1) - softscroll_offset,
9572 FY + TILEY * (dy == -1) - softscroll_offset,
9573 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9574 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9575 FX + TILEX * (dx == 1) - softscroll_offset,
9576 FY + TILEY * (dy == 1) - softscroll_offset);
9580 x = (dx == 1 ? BX1 : BX2);
9581 for (y = BY1; y <= BY2; y++)
9582 DrawScreenField(x, y);
9587 y = (dy == 1 ? BY1 : BY2);
9588 for (x = BX1; x <= BX2; x++)
9589 DrawScreenField(x, y);
9592 redraw_mask |= REDRAW_FIELD;
9596 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9598 int nextx = x + dx, nexty = y + dy;
9599 int element = Feld[x][y];
9602 element != EL_SP_PORT_LEFT &&
9603 element != EL_SP_GRAVITY_PORT_LEFT &&
9604 element != EL_SP_PORT_HORIZONTAL &&
9605 element != EL_SP_PORT_ANY) ||
9607 element != EL_SP_PORT_RIGHT &&
9608 element != EL_SP_GRAVITY_PORT_RIGHT &&
9609 element != EL_SP_PORT_HORIZONTAL &&
9610 element != EL_SP_PORT_ANY) ||
9612 element != EL_SP_PORT_UP &&
9613 element != EL_SP_GRAVITY_PORT_UP &&
9614 element != EL_SP_PORT_VERTICAL &&
9615 element != EL_SP_PORT_ANY) ||
9617 element != EL_SP_PORT_DOWN &&
9618 element != EL_SP_GRAVITY_PORT_DOWN &&
9619 element != EL_SP_PORT_VERTICAL &&
9620 element != EL_SP_PORT_ANY) ||
9621 !IN_LEV_FIELD(nextx, nexty) ||
9622 !IS_FREE(nextx, nexty))
9629 static boolean canFallDown(struct PlayerInfo *player)
9631 int jx = player->jx, jy = player->jy;
9633 return (IN_LEV_FIELD(jx, jy + 1) &&
9634 (IS_FREE(jx, jy + 1) ||
9635 #if USE_NEW_BLOCK_STYLE
9636 #if USE_GRAVITY_BUGFIX_OLD
9637 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9640 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9641 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9642 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9645 static boolean canPassField(int x, int y, int move_dir)
9647 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9648 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9649 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9652 int element = Feld[x][y];
9654 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9655 !CAN_MOVE(element) &&
9656 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9657 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9658 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9661 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9663 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9664 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9665 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9669 int nextx = newx + dx;
9670 int nexty = newy + dy;
9674 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9675 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9677 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9679 (IS_DIGGABLE(Feld[newx][newy]) ||
9680 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9681 canPassField(newx, newy, move_dir)));
9684 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9685 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9686 (IS_DIGGABLE(Feld[newx][newy]) ||
9687 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9688 canPassField(newx, newy, move_dir)));
9691 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9692 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9693 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9694 canPassField(newx, newy, move_dir)));
9696 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9697 (IS_DIGGABLE(Feld[newx][newy]) ||
9698 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9699 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9700 !CAN_MOVE(Feld[newx][newy]) &&
9701 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9702 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9703 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9709 static void CheckGravityMovement(struct PlayerInfo *player)
9711 if (game.gravity && !player->programmed_action)
9714 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9715 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9717 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9718 int move_dir_vertical = player->action & MV_VERTICAL;
9722 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9724 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9727 int jx = player->jx, jy = player->jy;
9729 boolean player_is_moving_to_valid_field =
9730 (!player_is_snapping &&
9731 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9732 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9736 (player->last_move_dir & MV_HORIZONTAL ?
9737 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9738 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9742 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9743 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9744 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9745 int new_jx = jx + dx, new_jy = jy + dy;
9746 int nextx = new_jx + dx, nexty = new_jy + dy;
9752 boolean player_can_fall_down = canFallDown(player);
9754 boolean player_can_fall_down =
9755 (IN_LEV_FIELD(jx, jy + 1) &&
9756 (IS_FREE(jx, jy + 1) ||
9757 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9761 boolean player_can_fall_down =
9762 (IN_LEV_FIELD(jx, jy + 1) &&
9763 (IS_FREE(jx, jy + 1)));
9767 boolean player_is_moving_to_valid_field =
9770 !player_is_snapping &&
9774 IN_LEV_FIELD(new_jx, new_jy) &&
9775 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9776 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9777 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9778 IN_LEV_FIELD(nextx, nexty) &&
9779 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9781 IN_LEV_FIELD(new_jx, new_jy) &&
9782 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9783 Feld[new_jx][new_jy] == EL_SAND ||
9784 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9785 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9786 /* !!! extend EL_SAND to anything diggable !!! */
9792 boolean player_is_standing_on_valid_field =
9793 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9794 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9798 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9799 player_can_fall_down,
9800 player_is_standing_on_valid_field,
9801 player_is_moving_to_valid_field,
9802 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9803 player->effective_action,
9804 player->can_fall_into_acid);
9807 if (player_can_fall_down &&
9809 !player_is_standing_on_valid_field &&
9811 !player_is_moving_to_valid_field)
9814 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9815 jx, jy, FrameCounter);
9818 player->programmed_action = MV_DOWN;
9823 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9826 return CheckGravityMovement(player);
9829 if (game.gravity && !player->programmed_action)
9831 int jx = player->jx, jy = player->jy;
9832 boolean field_under_player_is_free =
9833 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9834 boolean player_is_standing_on_valid_field =
9835 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9836 (IS_WALKABLE(Feld[jx][jy]) &&
9837 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9839 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9840 player->programmed_action = MV_DOWN;
9846 -----------------------------------------------------------------------------
9847 dx, dy: direction (non-diagonal) to try to move the player to
9848 real_dx, real_dy: direction as read from input device (can be diagonal)
9851 boolean MovePlayerOneStep(struct PlayerInfo *player,
9852 int dx, int dy, int real_dx, int real_dy)
9855 static int trigger_sides[4][2] =
9857 /* enter side leave side */
9858 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9859 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9860 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9861 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9863 int move_direction = (dx == -1 ? MV_LEFT :
9864 dx == +1 ? MV_RIGHT :
9866 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9867 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9868 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9870 int jx = player->jx, jy = player->jy;
9871 int new_jx = jx + dx, new_jy = jy + dy;
9875 if (!player->active || (!dx && !dy))
9876 return MF_NO_ACTION;
9878 player->MovDir = (dx < 0 ? MV_LEFT :
9881 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9883 if (!IN_LEV_FIELD(new_jx, new_jy))
9884 return MF_NO_ACTION;
9886 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9887 return MF_NO_ACTION;
9890 element = MovingOrBlocked2Element(new_jx, new_jy);
9892 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9895 if (DONT_RUN_INTO(element))
9897 if (element == EL_ACID && dx == 0 && dy == 1)
9899 SplashAcid(new_jx, new_jy);
9900 Feld[jx][jy] = EL_PLAYER_1;
9901 InitMovingField(jx, jy, MV_DOWN);
9902 Store[jx][jy] = EL_ACID;
9903 ContinueMoving(jx, jy);
9907 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9912 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9913 if (can_move != MF_MOVING)
9916 /* check if DigField() has caused relocation of the player */
9917 if (player->jx != jx || player->jy != jy)
9918 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9920 StorePlayer[jx][jy] = 0;
9921 player->last_jx = jx;
9922 player->last_jy = jy;
9923 player->jx = new_jx;
9924 player->jy = new_jy;
9925 StorePlayer[new_jx][new_jy] = player->element_nr;
9928 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9930 player->step_counter++;
9933 player->drop_delay = 0;
9936 PlayerVisit[jx][jy] = FrameCounter;
9938 ScrollPlayer(player, SCROLL_INIT);
9941 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9943 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9945 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9948 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9950 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9951 CE_PLAYER_ENTERS_X, enter_side);
9952 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9953 CE_ENTERED_BY_PLAYER, enter_side);
9960 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9962 int jx = player->jx, jy = player->jy;
9963 int old_jx = jx, old_jy = jy;
9964 int moved = MF_NO_ACTION;
9967 if (!player->active)
9972 if (player->MovPos == 0)
9974 player->is_moving = FALSE;
9975 player->is_digging = FALSE;
9976 player->is_collecting = FALSE;
9977 player->is_snapping = FALSE;
9978 player->is_pushing = FALSE;
9984 if (!player->active || (!dx && !dy))
9989 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9997 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9998 player->move_delay + player->move_delay_value);
10001 #if USE_NEW_MOVE_DELAY
10002 if (player->move_delay > 0)
10004 if (!FrameReached(&player->move_delay, player->move_delay_value))
10008 printf("::: can NOT move\n");
10014 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10015 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10022 printf("::: COULD move now\n");
10025 #if USE_NEW_MOVE_DELAY
10026 player->move_delay = -1; /* set to "uninitialized" value */
10029 /* store if player is automatically moved to next field */
10030 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10032 /* remove the last programmed player action */
10033 player->programmed_action = 0;
10035 if (player->MovPos)
10037 /* should only happen if pre-1.2 tape recordings are played */
10038 /* this is only for backward compatibility */
10040 int original_move_delay_value = player->move_delay_value;
10043 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10047 /* scroll remaining steps with finest movement resolution */
10048 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10050 while (player->MovPos)
10052 ScrollPlayer(player, SCROLL_GO_ON);
10053 ScrollScreen(NULL, SCROLL_GO_ON);
10055 #if USE_NEW_MOVE_DELAY
10056 AdvanceFrameAndPlayerCounters(player->index_nr);
10065 player->move_delay_value = original_move_delay_value;
10068 if (player->last_move_dir & MV_HORIZONTAL)
10070 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10071 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10075 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10076 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10082 if (moved & MF_MOVING && !ScreenMovPos &&
10083 (player == local_player || !options.network))
10085 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10086 int offset = (setup.scroll_delay ? 3 : 0);
10088 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10090 /* actual player has left the screen -- scroll in that direction */
10091 if (jx != old_jx) /* player has moved horizontally */
10092 scroll_x += (jx - old_jx);
10093 else /* player has moved vertically */
10094 scroll_y += (jy - old_jy);
10098 if (jx != old_jx) /* player has moved horizontally */
10100 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10101 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10102 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10104 /* don't scroll over playfield boundaries */
10105 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10106 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10108 /* don't scroll more than one field at a time */
10109 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10111 /* don't scroll against the player's moving direction */
10112 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10113 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10114 scroll_x = old_scroll_x;
10116 else /* player has moved vertically */
10118 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10119 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10120 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10122 /* don't scroll over playfield boundaries */
10123 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10124 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10126 /* don't scroll more than one field at a time */
10127 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10129 /* don't scroll against the player's moving direction */
10130 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10131 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10132 scroll_y = old_scroll_y;
10136 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10138 if (!options.network && !AllPlayersInVisibleScreen())
10140 scroll_x = old_scroll_x;
10141 scroll_y = old_scroll_y;
10145 ScrollScreen(player, SCROLL_INIT);
10146 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10153 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10155 if (!(moved & MF_MOVING) && !player->is_pushing)
10160 player->StepFrame = 0;
10162 if (moved & MF_MOVING)
10165 printf("::: REALLY moves now\n");
10168 if (old_jx != jx && old_jy == jy)
10169 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10170 else if (old_jx == jx && old_jy != jy)
10171 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10173 DrawLevelField(jx, jy); /* for "crumbled sand" */
10175 player->last_move_dir = player->MovDir;
10176 player->is_moving = TRUE;
10178 player->is_snapping = FALSE;
10182 player->is_switching = FALSE;
10185 player->is_dropping = FALSE;
10189 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10192 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10195 int move_direction = player->MovDir;
10197 int enter_side = MV_DIR_OPPOSITE(move_direction);
10198 int leave_side = move_direction;
10200 static int trigger_sides[4][2] =
10202 /* enter side leave side */
10203 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10204 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10205 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10206 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10208 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10209 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10211 int old_element = Feld[old_jx][old_jy];
10212 int new_element = Feld[jx][jy];
10215 /* !!! TEST ONLY !!! */
10216 if (IS_CUSTOM_ELEMENT(old_element))
10217 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10219 player->index_bit, leave_side);
10221 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10222 CE_PLAYER_LEAVES_X,
10223 player->index_bit, leave_side);
10225 if (IS_CUSTOM_ELEMENT(new_element))
10226 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10227 player->index_bit, enter_side);
10229 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10230 CE_PLAYER_ENTERS_X,
10231 player->index_bit, enter_side);
10241 CheckGravityMovementWhenNotMoving(player);
10244 player->last_move_dir = MV_NO_MOVING;
10246 player->is_moving = FALSE;
10248 #if USE_NEW_MOVE_STYLE
10249 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10250 /* ensure that the player is also allowed to move in the next frame */
10251 /* (currently, the player is forced to wait eight frames before he can try
10254 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10255 player->move_delay = 0; /* allow direct movement in the next frame */
10259 #if USE_NEW_MOVE_DELAY
10260 if (player->move_delay == -1) /* not yet initialized by DigField() */
10261 player->move_delay = player->move_delay_value;
10264 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10266 TestIfHeroTouchesBadThing(jx, jy);
10267 TestIfPlayerTouchesCustomElement(jx, jy);
10270 if (!player->active)
10271 RemoveHero(player);
10276 void ScrollPlayer(struct PlayerInfo *player, int mode)
10278 int jx = player->jx, jy = player->jy;
10279 int last_jx = player->last_jx, last_jy = player->last_jy;
10280 int move_stepsize = TILEX / player->move_delay_value;
10282 if (!player->active || !player->MovPos)
10285 if (mode == SCROLL_INIT)
10287 player->actual_frame_counter = FrameCounter;
10288 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10291 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10293 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10294 player->block_delay);
10297 #if USE_NEW_BLOCK_STYLE
10300 if (player->block_delay <= 0)
10301 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10304 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10305 Feld[last_jx][last_jy] == EL_EMPTY)
10307 int last_field_block_delay = 0; /* start with no blocking at all */
10308 int block_delay_adjustment = player->block_delay_adjustment;
10310 /* if player blocks last field, add delay for exactly one move */
10311 if (player->block_last_field)
10313 last_field_block_delay += player->move_delay_value;
10315 #if USE_GRAVITY_BUGFIX_NEW
10316 /* when blocking enabled, prevent moving up despite gravity */
10317 if (game.gravity && player->MovDir == MV_UP)
10318 block_delay_adjustment = -1;
10322 /* add block delay adjustment (also possible when not blocking) */
10323 last_field_block_delay += block_delay_adjustment;
10326 #if USE_BLOCK_DELAY_BUGFIX
10327 /* when blocking enabled, correct block delay for fast movement */
10328 if (player->block_last_field &&
10329 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10330 last_field_block_delay =
10331 player->move_delay_value + player->block_delay_adjustment;
10336 #if USE_GRAVITY_BUGFIX_NEW
10337 /* when blocking enabled, correct block delay for gravity movement */
10338 if (player->block_last_field &&
10339 game.gravity && player->MovDir == MV_UP)
10340 last_field_block_delay = player->move_delay_value - 1;
10344 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10345 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10348 #if USE_NEW_MOVE_STYLE
10349 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10350 player->block_last_field) &&
10351 Feld[last_jx][last_jy] == EL_EMPTY)
10352 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10354 if (Feld[last_jx][last_jy] == EL_EMPTY)
10355 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10360 DrawPlayer(player);
10365 else if (!FrameReached(&player->actual_frame_counter, 1))
10368 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10369 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10371 #if USE_NEW_BLOCK_STYLE
10373 if (!player->block_last_field &&
10374 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10376 RemoveField(last_jx, last_jy);
10378 Feld[last_jx][last_jy] = EL_EMPTY;
10382 /* before DrawPlayer() to draw correct player graphic for this case */
10383 if (player->MovPos == 0)
10384 CheckGravityMovement(player);
10387 DrawPlayer(player); /* needed here only to cleanup last field */
10390 if (player->MovPos == 0) /* player reached destination field */
10393 if (player->move_delay_reset_counter > 0)
10395 player->move_delay_reset_counter--;
10397 if (player->move_delay_reset_counter == 0)
10399 /* continue with normal speed after quickly moving through gate */
10400 HALVE_PLAYER_SPEED(player);
10402 /* be able to make the next move without delay */
10403 player->move_delay = 0;
10407 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10409 /* continue with normal speed after quickly moving through gate */
10410 HALVE_PLAYER_SPEED(player);
10412 /* be able to make the next move without delay */
10413 player->move_delay = 0;
10417 #if USE_NEW_BLOCK_STYLE
10419 if (player->block_last_field &&
10420 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10422 RemoveField(last_jx, last_jy);
10424 Feld[last_jx][last_jy] = EL_EMPTY;
10428 player->last_jx = jx;
10429 player->last_jy = jy;
10431 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10432 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10433 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10435 DrawPlayer(player); /* needed here only to cleanup last field */
10436 RemoveHero(player);
10438 if (local_player->friends_still_needed == 0 ||
10439 IS_SP_ELEMENT(Feld[jx][jy]))
10440 player->LevelSolved = player->GameOver = TRUE;
10444 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10445 /* this breaks one level: "machine", level 000 */
10447 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10450 int move_direction = player->MovDir;
10452 int enter_side = MV_DIR_OPPOSITE(move_direction);
10453 int leave_side = move_direction;
10455 static int trigger_sides[4][2] =
10457 /* enter side leave side */
10458 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10459 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10460 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10461 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10463 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10464 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10466 int old_jx = last_jx;
10467 int old_jy = last_jy;
10468 int old_element = Feld[old_jx][old_jy];
10469 int new_element = Feld[jx][jy];
10472 /* !!! TEST ONLY !!! */
10473 if (IS_CUSTOM_ELEMENT(old_element))
10474 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10476 player->index_bit, leave_side);
10478 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10479 CE_PLAYER_LEAVES_X,
10480 player->index_bit, leave_side);
10482 if (IS_CUSTOM_ELEMENT(new_element))
10483 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10484 player->index_bit, enter_side);
10486 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10487 CE_PLAYER_ENTERS_X,
10488 player->index_bit, enter_side);
10494 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10496 TestIfHeroTouchesBadThing(jx, jy);
10497 TestIfPlayerTouchesCustomElement(jx, jy);
10500 /* needed because pushed element has not yet reached its destination,
10501 so it would trigger a change event at its previous field location */
10502 if (!player->is_pushing)
10504 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10507 if (!player->active)
10508 RemoveHero(player);
10511 if (level.use_step_counter)
10521 if (TimeLeft <= 10 && setup.time_limit)
10522 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10524 DrawGameValue_Time(TimeLeft);
10526 if (!TimeLeft && setup.time_limit)
10527 for (i = 0; i < MAX_PLAYERS; i++)
10528 KillHero(&stored_player[i]);
10530 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10531 DrawGameValue_Time(TimePlayed);
10534 if (tape.single_step && tape.recording && !tape.pausing &&
10535 !player->programmed_action)
10536 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10540 void ScrollScreen(struct PlayerInfo *player, int mode)
10542 static unsigned long screen_frame_counter = 0;
10544 if (mode == SCROLL_INIT)
10546 /* set scrolling step size according to actual player's moving speed */
10547 ScrollStepSize = TILEX / player->move_delay_value;
10549 screen_frame_counter = FrameCounter;
10550 ScreenMovDir = player->MovDir;
10551 ScreenMovPos = player->MovPos;
10552 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10555 else if (!FrameReached(&screen_frame_counter, 1))
10560 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10561 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10562 redraw_mask |= REDRAW_FIELD;
10565 ScreenMovDir = MV_NO_MOVING;
10568 void TestIfPlayerTouchesCustomElement(int x, int y)
10570 static int xy[4][2] =
10577 static int trigger_sides[4][2] =
10579 /* center side border side */
10580 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10581 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10582 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10583 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10585 static int touch_dir[4] =
10587 MV_LEFT | MV_RIGHT,
10592 int center_element = Feld[x][y]; /* should always be non-moving! */
10595 for (i = 0; i < NUM_DIRECTIONS; i++)
10597 int xx = x + xy[i][0];
10598 int yy = y + xy[i][1];
10599 int center_side = trigger_sides[i][0];
10600 int border_side = trigger_sides[i][1];
10601 int border_element;
10603 if (!IN_LEV_FIELD(xx, yy))
10606 if (IS_PLAYER(x, y))
10608 struct PlayerInfo *player = PLAYERINFO(x, y);
10610 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10611 border_element = Feld[xx][yy]; /* may be moving! */
10612 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10613 border_element = Feld[xx][yy];
10614 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10615 border_element = MovingOrBlocked2Element(xx, yy);
10617 continue; /* center and border element do not touch */
10620 /* !!! TEST ONLY !!! */
10621 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10622 player->index_bit, border_side);
10623 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10624 CE_PLAYER_TOUCHES_X,
10625 player->index_bit, border_side);
10627 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10628 CE_PLAYER_TOUCHES_X,
10629 player->index_bit, border_side);
10630 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10631 player->index_bit, border_side);
10634 else if (IS_PLAYER(xx, yy))
10636 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10638 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10640 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10641 continue; /* center and border element do not touch */
10645 /* !!! TEST ONLY !!! */
10646 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10647 player->index_bit, center_side);
10648 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10649 CE_PLAYER_TOUCHES_X,
10650 player->index_bit, center_side);
10652 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10653 CE_PLAYER_TOUCHES_X,
10654 player->index_bit, center_side);
10655 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10656 player->index_bit, center_side);
10664 void TestIfElementTouchesCustomElement(int x, int y)
10666 static int xy[4][2] =
10673 static int trigger_sides[4][2] =
10675 /* center side border side */
10676 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10677 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10678 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10679 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10681 static int touch_dir[4] =
10683 MV_LEFT | MV_RIGHT,
10688 boolean change_center_element = FALSE;
10689 int center_element_change_page = 0;
10690 int center_element = Feld[x][y]; /* should always be non-moving! */
10691 int border_trigger_element = EL_UNDEFINED;
10694 for (i = 0; i < NUM_DIRECTIONS; i++)
10696 int xx = x + xy[i][0];
10697 int yy = y + xy[i][1];
10698 int center_side = trigger_sides[i][0];
10699 int border_side = trigger_sides[i][1];
10700 int border_element;
10702 if (!IN_LEV_FIELD(xx, yy))
10705 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10706 border_element = Feld[xx][yy]; /* may be moving! */
10707 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10708 border_element = Feld[xx][yy];
10709 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10710 border_element = MovingOrBlocked2Element(xx, yy);
10712 continue; /* center and border element do not touch */
10714 /* check for change of center element (but change it only once) */
10715 if (IS_CUSTOM_ELEMENT(center_element) &&
10716 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10717 !change_center_element)
10719 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10721 struct ElementChangeInfo *change =
10722 &element_info[center_element].change_page[j];
10724 if (change->can_change &&
10725 change->has_event[CE_TOUCHING_X] &&
10726 change->trigger_side & border_side &&
10728 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10730 change->trigger_element == border_element
10734 change_center_element = TRUE;
10735 center_element_change_page = j;
10736 border_trigger_element = border_element;
10743 /* check for change of border element */
10744 if (IS_CUSTOM_ELEMENT(border_element) &&
10745 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10747 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10749 struct ElementChangeInfo *change =
10750 &element_info[border_element].change_page[j];
10752 if (change->can_change &&
10753 change->has_event[CE_TOUCHING_X] &&
10754 change->trigger_side & center_side &&
10756 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10758 change->trigger_element == center_element
10763 printf("::: border_element %d, %d\n", x, y);
10766 CheckElementChangeByPage(xx, yy, border_element, center_element,
10774 if (change_center_element)
10777 printf("::: center_element %d, %d\n", x, y);
10780 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10781 CE_TOUCHING_X, center_element_change_page);
10785 void TestIfElementHitsCustomElement(int x, int y, int direction)
10787 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10788 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10789 int hitx = x + dx, hity = y + dy;
10790 int hitting_element = Feld[x][y];
10791 int touched_element;
10793 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10794 !IS_FREE(hitx, hity) &&
10795 (!IS_MOVING(hitx, hity) ||
10796 MovDir[hitx][hity] != direction ||
10797 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10800 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10804 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10808 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10809 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10811 #if !USE_HITTING_SOMETHING_BUGFIX
10812 /* "hitting something" is also true when hitting the playfield border */
10813 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10814 CE_HITTING_SOMETHING, direction);
10817 if (IN_LEV_FIELD(hitx, hity))
10819 int opposite_direction = MV_DIR_OPPOSITE(direction);
10820 int hitting_side = direction;
10821 int touched_side = opposite_direction;
10823 int touched_element = MovingOrBlocked2Element(hitx, hity);
10826 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10827 MovDir[hitx][hity] != direction ||
10828 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10837 #if !USE_HIT_BY_SOMETHING_BUGFIX
10838 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10839 CE_HIT_BY_SOMETHING, opposite_direction);
10842 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10843 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10845 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10847 struct ElementChangeInfo *change =
10848 &element_info[hitting_element].change_page[i];
10850 if (change->can_change &&
10851 change->has_event[CE_HITTING_X] &&
10852 change->trigger_side & touched_side &&
10855 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10857 change->trigger_element == touched_element
10861 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10868 if (IS_CUSTOM_ELEMENT(touched_element) &&
10869 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10871 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10873 struct ElementChangeInfo *change =
10874 &element_info[touched_element].change_page[i];
10876 if (change->can_change &&
10877 change->has_event[CE_HIT_BY_X] &&
10878 change->trigger_side & hitting_side &&
10880 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10882 change->trigger_element == hitting_element
10886 CheckElementChangeByPage(hitx, hity, touched_element,
10887 hitting_element, CE_HIT_BY_X, i);
10893 #if USE_HIT_BY_SOMETHING_BUGFIX
10894 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10895 CE_HIT_BY_SOMETHING, opposite_direction);
10900 #if USE_HITTING_SOMETHING_BUGFIX
10901 /* "hitting something" is also true when hitting the playfield border */
10902 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10903 CE_HITTING_SOMETHING, direction);
10908 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10910 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10911 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10912 int hitx = x + dx, hity = y + dy;
10913 int hitting_element = Feld[x][y];
10914 int touched_element;
10916 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10917 !IS_FREE(hitx, hity) &&
10918 (!IS_MOVING(hitx, hity) ||
10919 MovDir[hitx][hity] != direction ||
10920 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10923 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10927 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10931 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10932 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10934 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10935 EP_CAN_SMASH_EVERYTHING, direction);
10937 if (IN_LEV_FIELD(hitx, hity))
10939 int opposite_direction = MV_DIR_OPPOSITE(direction);
10940 int hitting_side = direction;
10941 int touched_side = opposite_direction;
10943 int touched_element = MovingOrBlocked2Element(hitx, hity);
10946 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10947 MovDir[hitx][hity] != direction ||
10948 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10957 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10958 CE_SMASHED_BY_SOMETHING, opposite_direction);
10960 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10961 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10963 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10965 struct ElementChangeInfo *change =
10966 &element_info[hitting_element].change_page[i];
10968 if (change->can_change &&
10969 change->has_event[CE_OTHER_IS_SMASHING] &&
10970 change->trigger_side & touched_side &&
10973 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10975 change->trigger_element == touched_element
10979 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10980 CE_OTHER_IS_SMASHING, i);
10986 if (IS_CUSTOM_ELEMENT(touched_element) &&
10987 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10989 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10991 struct ElementChangeInfo *change =
10992 &element_info[touched_element].change_page[i];
10994 if (change->can_change &&
10995 change->has_event[CE_OTHER_GETS_SMASHED] &&
10996 change->trigger_side & hitting_side &&
10998 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11000 change->trigger_element == hitting_element
11004 CheckElementChangeByPage(hitx, hity, touched_element,
11005 hitting_element, CE_OTHER_GETS_SMASHED,i);
11015 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11017 int i, kill_x = -1, kill_y = -1;
11018 int bad_element = -1;
11019 static int test_xy[4][2] =
11026 static int test_dir[4] =
11034 for (i = 0; i < NUM_DIRECTIONS; i++)
11036 int test_x, test_y, test_move_dir, test_element;
11038 test_x = good_x + test_xy[i][0];
11039 test_y = good_y + test_xy[i][1];
11041 if (!IN_LEV_FIELD(test_x, test_y))
11045 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11048 test_element = Feld[test_x][test_y];
11050 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11053 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11054 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11056 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11057 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11061 bad_element = test_element;
11067 if (kill_x != -1 || kill_y != -1)
11069 if (IS_PLAYER(good_x, good_y))
11071 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11074 if (player->shield_deadly_time_left > 0 &&
11075 !IS_INDESTRUCTIBLE(bad_element))
11076 Bang(kill_x, kill_y);
11077 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11080 if (player->shield_deadly_time_left > 0)
11081 Bang(kill_x, kill_y);
11082 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11087 Bang(good_x, good_y);
11091 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11093 int i, kill_x = -1, kill_y = -1;
11094 int bad_element = Feld[bad_x][bad_y];
11095 static int test_xy[4][2] =
11102 static int touch_dir[4] =
11104 MV_LEFT | MV_RIGHT,
11109 static int test_dir[4] =
11117 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11120 for (i = 0; i < NUM_DIRECTIONS; i++)
11122 int test_x, test_y, test_move_dir, test_element;
11124 test_x = bad_x + test_xy[i][0];
11125 test_y = bad_y + test_xy[i][1];
11126 if (!IN_LEV_FIELD(test_x, test_y))
11130 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11132 test_element = Feld[test_x][test_y];
11134 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11135 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11137 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11138 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11140 /* good thing is player or penguin that does not move away */
11141 if (IS_PLAYER(test_x, test_y))
11143 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11145 if (bad_element == EL_ROBOT && player->is_moving)
11146 continue; /* robot does not kill player if he is moving */
11148 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11150 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11151 continue; /* center and border element do not touch */
11158 else if (test_element == EL_PENGUIN)
11167 if (kill_x != -1 || kill_y != -1)
11169 if (IS_PLAYER(kill_x, kill_y))
11171 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11174 if (player->shield_deadly_time_left > 0 &&
11175 !IS_INDESTRUCTIBLE(bad_element))
11176 Bang(bad_x, bad_y);
11177 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11180 if (player->shield_deadly_time_left > 0)
11181 Bang(bad_x, bad_y);
11182 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11187 Bang(kill_x, kill_y);
11191 void TestIfHeroTouchesBadThing(int x, int y)
11193 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11196 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11198 TestIfGoodThingHitsBadThing(x, y, move_dir);
11201 void TestIfBadThingTouchesHero(int x, int y)
11203 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11206 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11208 TestIfBadThingHitsGoodThing(x, y, move_dir);
11211 void TestIfFriendTouchesBadThing(int x, int y)
11213 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11216 void TestIfBadThingTouchesFriend(int x, int y)
11218 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11221 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11223 int i, kill_x = bad_x, kill_y = bad_y;
11224 static int xy[4][2] =
11232 for (i = 0; i < NUM_DIRECTIONS; i++)
11236 x = bad_x + xy[i][0];
11237 y = bad_y + xy[i][1];
11238 if (!IN_LEV_FIELD(x, y))
11241 element = Feld[x][y];
11242 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11243 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11251 if (kill_x != bad_x || kill_y != bad_y)
11252 Bang(bad_x, bad_y);
11255 void KillHero(struct PlayerInfo *player)
11257 int jx = player->jx, jy = player->jy;
11259 if (!player->active)
11262 /* remove accessible field at the player's position */
11263 Feld[jx][jy] = EL_EMPTY;
11265 /* deactivate shield (else Bang()/Explode() would not work right) */
11266 player->shield_normal_time_left = 0;
11267 player->shield_deadly_time_left = 0;
11273 static void KillHeroUnlessEnemyProtected(int x, int y)
11275 if (!PLAYER_ENEMY_PROTECTED(x, y))
11276 KillHero(PLAYERINFO(x, y));
11279 static void KillHeroUnlessExplosionProtected(int x, int y)
11281 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11282 KillHero(PLAYERINFO(x, y));
11285 void BuryHero(struct PlayerInfo *player)
11287 int jx = player->jx, jy = player->jy;
11289 if (!player->active)
11293 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11295 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11297 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11299 player->GameOver = TRUE;
11300 RemoveHero(player);
11303 void RemoveHero(struct PlayerInfo *player)
11305 int jx = player->jx, jy = player->jy;
11306 int i, found = FALSE;
11308 player->present = FALSE;
11309 player->active = FALSE;
11311 if (!ExplodeField[jx][jy])
11312 StorePlayer[jx][jy] = 0;
11314 for (i = 0; i < MAX_PLAYERS; i++)
11315 if (stored_player[i].active)
11319 AllPlayersGone = TRUE;
11326 =============================================================================
11327 checkDiagonalPushing()
11328 -----------------------------------------------------------------------------
11329 check if diagonal input device direction results in pushing of object
11330 (by checking if the alternative direction is walkable, diggable, ...)
11331 =============================================================================
11334 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11335 int x, int y, int real_dx, int real_dy)
11337 int jx, jy, dx, dy, xx, yy;
11339 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11342 /* diagonal direction: check alternative direction */
11347 xx = jx + (dx == 0 ? real_dx : 0);
11348 yy = jy + (dy == 0 ? real_dy : 0);
11350 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11354 =============================================================================
11356 -----------------------------------------------------------------------------
11357 x, y: field next to player (non-diagonal) to try to dig to
11358 real_dx, real_dy: direction as read from input device (can be diagonal)
11359 =============================================================================
11362 int DigField(struct PlayerInfo *player,
11363 int oldx, int oldy, int x, int y,
11364 int real_dx, int real_dy, int mode)
11367 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11369 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11370 boolean player_was_pushing = player->is_pushing;
11371 int jx = oldx, jy = oldy;
11372 int dx = x - jx, dy = y - jy;
11373 int nextx = x + dx, nexty = y + dy;
11374 int move_direction = (dx == -1 ? MV_LEFT :
11375 dx == +1 ? MV_RIGHT :
11377 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11378 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11380 int dig_side = MV_DIR_OPPOSITE(move_direction);
11382 static int trigger_sides[4] =
11384 CH_SIDE_RIGHT, /* moving left */
11385 CH_SIDE_LEFT, /* moving right */
11386 CH_SIDE_BOTTOM, /* moving up */
11387 CH_SIDE_TOP, /* moving down */
11389 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11391 int old_element = Feld[jx][jy];
11394 if (is_player) /* function can also be called by EL_PENGUIN */
11396 if (player->MovPos == 0)
11398 player->is_digging = FALSE;
11399 player->is_collecting = FALSE;
11402 if (player->MovPos == 0) /* last pushing move finished */
11403 player->is_pushing = FALSE;
11405 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11407 player->is_switching = FALSE;
11408 #if USE_NEW_PUSH_DELAY
11409 player->push_delay = -1;
11411 player->push_delay = 0;
11414 return MF_NO_ACTION;
11418 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11419 return MF_NO_ACTION;
11424 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11426 if (IS_TUBE(Feld[jx][jy]) ||
11427 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11431 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11432 int tube_leave_directions[][2] =
11434 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11435 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11436 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11437 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11438 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11439 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11440 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11441 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11442 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11443 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11444 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11445 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11448 while (tube_leave_directions[i][0] != tube_element)
11451 if (tube_leave_directions[i][0] == -1) /* should not happen */
11455 if (!(tube_leave_directions[i][1] & move_direction))
11456 return MF_NO_ACTION; /* tube has no opening in this direction */
11461 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11462 old_element = Back[jx][jy];
11466 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11467 return MF_NO_ACTION; /* field has no opening in this direction */
11469 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11470 return MF_NO_ACTION; /* field has no opening in this direction */
11472 element = Feld[x][y];
11474 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11475 return MF_NO_ACTION;
11477 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11478 game.engine_version >= VERSION_IDENT(2,2,0,0))
11479 return MF_NO_ACTION;
11482 if (game.gravity && is_player && !player->is_auto_moving &&
11483 canFallDown(player) && move_direction != MV_DOWN &&
11484 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11485 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11489 if (element == EL_EMPTY_SPACE &&
11490 game.gravity && !player->is_auto_moving &&
11491 canFallDown(player) && move_direction != MV_DOWN)
11492 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11498 case EL_SP_PORT_LEFT:
11499 case EL_SP_PORT_RIGHT:
11500 case EL_SP_PORT_UP:
11501 case EL_SP_PORT_DOWN:
11502 case EL_SP_PORT_HORIZONTAL:
11503 case EL_SP_PORT_VERTICAL:
11504 case EL_SP_PORT_ANY:
11505 case EL_SP_GRAVITY_PORT_LEFT:
11506 case EL_SP_GRAVITY_PORT_RIGHT:
11507 case EL_SP_GRAVITY_PORT_UP:
11508 case EL_SP_GRAVITY_PORT_DOWN:
11510 if (!canEnterSupaplexPort(x, y, dx, dy))
11511 return MF_NO_ACTION;
11514 element != EL_SP_PORT_LEFT &&
11515 element != EL_SP_GRAVITY_PORT_LEFT &&
11516 element != EL_SP_PORT_HORIZONTAL &&
11517 element != EL_SP_PORT_ANY) ||
11519 element != EL_SP_PORT_RIGHT &&
11520 element != EL_SP_GRAVITY_PORT_RIGHT &&
11521 element != EL_SP_PORT_HORIZONTAL &&
11522 element != EL_SP_PORT_ANY) ||
11524 element != EL_SP_PORT_UP &&
11525 element != EL_SP_GRAVITY_PORT_UP &&
11526 element != EL_SP_PORT_VERTICAL &&
11527 element != EL_SP_PORT_ANY) ||
11529 element != EL_SP_PORT_DOWN &&
11530 element != EL_SP_GRAVITY_PORT_DOWN &&
11531 element != EL_SP_PORT_VERTICAL &&
11532 element != EL_SP_PORT_ANY) ||
11533 !IN_LEV_FIELD(nextx, nexty) ||
11534 !IS_FREE(nextx, nexty))
11535 return MF_NO_ACTION;
11538 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11539 element == EL_SP_GRAVITY_PORT_RIGHT ||
11540 element == EL_SP_GRAVITY_PORT_UP ||
11541 element == EL_SP_GRAVITY_PORT_DOWN)
11542 game.gravity = !game.gravity;
11544 /* automatically move to the next field with double speed */
11545 player->programmed_action = move_direction;
11547 if (player->move_delay_reset_counter == 0)
11549 player->move_delay_reset_counter = 2; /* two double speed steps */
11551 DOUBLE_PLAYER_SPEED(player);
11554 player->move_delay_reset_counter = 2;
11556 DOUBLE_PLAYER_SPEED(player);
11560 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11563 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11569 case EL_TUBE_VERTICAL:
11570 case EL_TUBE_HORIZONTAL:
11571 case EL_TUBE_VERTICAL_LEFT:
11572 case EL_TUBE_VERTICAL_RIGHT:
11573 case EL_TUBE_HORIZONTAL_UP:
11574 case EL_TUBE_HORIZONTAL_DOWN:
11575 case EL_TUBE_LEFT_UP:
11576 case EL_TUBE_LEFT_DOWN:
11577 case EL_TUBE_RIGHT_UP:
11578 case EL_TUBE_RIGHT_DOWN:
11581 int tube_enter_directions[][2] =
11583 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11584 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11585 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11586 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11587 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11588 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11589 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11590 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11591 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11592 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11593 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11594 { -1, MV_NO_MOVING }
11597 while (tube_enter_directions[i][0] != element)
11600 if (tube_enter_directions[i][0] == -1) /* should not happen */
11604 if (!(tube_enter_directions[i][1] & move_direction))
11605 return MF_NO_ACTION; /* tube has no opening in this direction */
11607 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11615 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11617 if (IS_WALKABLE(element))
11620 int sound_element = SND_ELEMENT(element);
11621 int sound_action = ACTION_WALKING;
11624 if (!ACCESS_FROM(element, opposite_direction))
11625 return MF_NO_ACTION; /* field not accessible from this direction */
11629 if (element == EL_EMPTY_SPACE &&
11630 game.gravity && !player->is_auto_moving &&
11631 canFallDown(player) && move_direction != MV_DOWN)
11632 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11635 if (IS_RND_GATE(element))
11637 if (!player->key[RND_GATE_NR(element)])
11638 return MF_NO_ACTION;
11640 else if (IS_RND_GATE_GRAY(element))
11642 if (!player->key[RND_GATE_GRAY_NR(element)])
11643 return MF_NO_ACTION;
11645 else if (element == EL_EXIT_OPEN ||
11646 element == EL_SP_EXIT_OPEN ||
11647 element == EL_SP_EXIT_OPENING)
11649 sound_action = ACTION_PASSING; /* player is passing exit */
11651 else if (element == EL_EMPTY)
11653 sound_action = ACTION_MOVING; /* nothing to walk on */
11656 /* play sound from background or player, whatever is available */
11657 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11658 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11660 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11665 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11667 else if (IS_PASSABLE(element))
11671 if (!canPassField(x, y, move_direction))
11672 return MF_NO_ACTION;
11677 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11678 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11679 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11680 return MF_NO_ACTION;
11682 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11683 return MF_NO_ACTION;
11688 if (!ACCESS_FROM(element, opposite_direction))
11689 return MF_NO_ACTION; /* field not accessible from this direction */
11691 if (IS_CUSTOM_ELEMENT(element) &&
11692 !ACCESS_FROM(element, opposite_direction))
11693 return MF_NO_ACTION; /* field not accessible from this direction */
11697 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11698 return MF_NO_ACTION;
11703 if (IS_EM_GATE(element))
11705 if (!player->key[EM_GATE_NR(element)])
11706 return MF_NO_ACTION;
11708 else if (IS_EM_GATE_GRAY(element))
11710 if (!player->key[EM_GATE_GRAY_NR(element)])
11711 return MF_NO_ACTION;
11713 else if (IS_SP_PORT(element))
11715 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11716 element == EL_SP_GRAVITY_PORT_RIGHT ||
11717 element == EL_SP_GRAVITY_PORT_UP ||
11718 element == EL_SP_GRAVITY_PORT_DOWN)
11719 game.gravity = !game.gravity;
11720 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11721 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11722 element == EL_SP_GRAVITY_ON_PORT_UP ||
11723 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11724 game.gravity = TRUE;
11725 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11726 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11727 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11728 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11729 game.gravity = FALSE;
11732 /* automatically move to the next field with double speed */
11733 player->programmed_action = move_direction;
11735 if (player->move_delay_reset_counter == 0)
11737 player->move_delay_reset_counter = 2; /* two double speed steps */
11739 DOUBLE_PLAYER_SPEED(player);
11742 player->move_delay_reset_counter = 2;
11744 DOUBLE_PLAYER_SPEED(player);
11747 PlayLevelSoundAction(x, y, ACTION_PASSING);
11751 else if (IS_DIGGABLE(element))
11755 if (mode != DF_SNAP)
11758 GfxElement[x][y] = GFX_ELEMENT(element);
11761 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11763 player->is_digging = TRUE;
11766 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11768 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11769 player->index_bit, dig_side);
11772 if (mode == DF_SNAP)
11773 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11778 else if (IS_COLLECTIBLE(element))
11782 if (is_player && mode != DF_SNAP)
11784 GfxElement[x][y] = element;
11785 player->is_collecting = TRUE;
11788 if (element == EL_SPEED_PILL)
11789 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11790 else if (element == EL_EXTRA_TIME && level.time > 0)
11793 DrawGameValue_Time(TimeLeft);
11795 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11797 player->shield_normal_time_left += 10;
11798 if (element == EL_SHIELD_DEADLY)
11799 player->shield_deadly_time_left += 10;
11801 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11803 if (player->inventory_size < MAX_INVENTORY_SIZE)
11804 player->inventory_element[player->inventory_size++] = element;
11806 DrawGameValue_Dynamite(local_player->inventory_size);
11808 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11810 player->dynabomb_count++;
11811 player->dynabombs_left++;
11813 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11815 player->dynabomb_size++;
11817 else if (element == EL_DYNABOMB_INCREASE_POWER)
11819 player->dynabomb_xl = TRUE;
11821 else if (IS_KEY(element))
11823 player->key[KEY_NR(element)] = TRUE;
11825 DrawGameValue_Keys(player->key);
11827 redraw_mask |= REDRAW_DOOR_1;
11829 else if (IS_ENVELOPE(element))
11832 player->show_envelope = element;
11834 ShowEnvelope(element - EL_ENVELOPE_1);
11837 else if (IS_DROPPABLE(element) ||
11838 IS_THROWABLE(element)) /* can be collected and dropped */
11842 if (element_info[element].collect_count == 0)
11843 player->inventory_infinite_element = element;
11845 for (i = 0; i < element_info[element].collect_count; i++)
11846 if (player->inventory_size < MAX_INVENTORY_SIZE)
11847 player->inventory_element[player->inventory_size++] = element;
11849 DrawGameValue_Dynamite(local_player->inventory_size);
11851 else if (element_info[element].collect_count > 0)
11853 local_player->gems_still_needed -=
11854 element_info[element].collect_count;
11855 if (local_player->gems_still_needed < 0)
11856 local_player->gems_still_needed = 0;
11858 DrawGameValue_Emeralds(local_player->gems_still_needed);
11861 RaiseScoreElement(element);
11862 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11865 CheckTriggeredElementChangeByPlayer(x, y, element,
11866 CE_PLAYER_COLLECTS_X,
11867 player->index_bit, dig_side);
11870 if (mode == DF_SNAP)
11871 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11876 else if (IS_PUSHABLE(element))
11878 if (mode == DF_SNAP && element != EL_BD_ROCK)
11879 return MF_NO_ACTION;
11881 if (CAN_FALL(element) && dy)
11882 return MF_NO_ACTION;
11884 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11885 !(element == EL_SPRING && level.use_spring_bug))
11886 return MF_NO_ACTION;
11889 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11890 ((move_direction & MV_VERTICAL &&
11891 ((element_info[element].move_pattern & MV_LEFT &&
11892 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11893 (element_info[element].move_pattern & MV_RIGHT &&
11894 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11895 (move_direction & MV_HORIZONTAL &&
11896 ((element_info[element].move_pattern & MV_UP &&
11897 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11898 (element_info[element].move_pattern & MV_DOWN &&
11899 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11900 return MF_NO_ACTION;
11904 /* do not push elements already moving away faster than player */
11905 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11906 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11907 return MF_NO_ACTION;
11909 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11910 return MF_NO_ACTION;
11916 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11918 if (player->push_delay_value == -1 || !player_was_pushing)
11919 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11921 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11923 if (player->push_delay_value == -1)
11924 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11927 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11929 if (player->push_delay_value == -1 || !player_was_pushing)
11930 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11933 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11935 if (!player->is_pushing)
11936 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11940 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11941 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11942 !player_is_pushing))
11943 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11946 if (!player->is_pushing &&
11947 game.engine_version >= VERSION_IDENT(2,2,0,7))
11948 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11952 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11953 player->push_delay, player->push_delay_value,
11954 FrameCounter, game.engine_version,
11955 player_was_pushing, player->is_pushing,
11956 element, element_info[element].token_name,
11957 GET_NEW_PUSH_DELAY(element));
11960 player->is_pushing = TRUE;
11962 if (!(IN_LEV_FIELD(nextx, nexty) &&
11963 (IS_FREE(nextx, nexty) ||
11964 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11965 IS_SB_ELEMENT(element)))))
11966 return MF_NO_ACTION;
11968 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11969 return MF_NO_ACTION;
11971 #if USE_NEW_PUSH_DELAY
11974 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11975 printf("::: ALERT: %d, %d [%d / %d]\n",
11976 player->push_delay, player->push_delay2,
11977 FrameCounter, FrameCounter / 50);
11980 if (player->push_delay == -1) /* new pushing; restart delay */
11981 player->push_delay = 0;
11983 if (player->push_delay == 0) /* new pushing; restart delay */
11984 player->push_delay = FrameCounter;
11987 #if USE_NEW_PUSH_DELAY
11989 if ( (player->push_delay > 0) != (!xxx_fr) )
11990 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11991 player->push_delay,
11992 xxx_pdv2, player->push_delay2, player->push_delay_value,
11993 FrameCounter, FrameCounter / 50);
11997 if (player->push_delay > 0 &&
11998 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11999 element != EL_SPRING && element != EL_BALLOON)
12002 if (player->push_delay < player->push_delay_value &&
12003 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12004 element != EL_SPRING && element != EL_BALLOON)
12008 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12009 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12010 element != EL_SPRING && element != EL_BALLOON)
12013 /* make sure that there is no move delay before next try to push */
12014 #if USE_NEW_MOVE_DELAY
12015 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12016 player->move_delay = 0;
12018 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12019 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12022 return MF_NO_ACTION;
12026 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12029 if (IS_SB_ELEMENT(element))
12031 if (element == EL_SOKOBAN_FIELD_FULL)
12033 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12034 local_player->sokobanfields_still_needed++;
12037 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12039 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12040 local_player->sokobanfields_still_needed--;
12043 Feld[x][y] = EL_SOKOBAN_OBJECT;
12045 if (Back[x][y] == Back[nextx][nexty])
12046 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12047 else if (Back[x][y] != 0)
12048 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12051 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12054 if (local_player->sokobanfields_still_needed == 0 &&
12055 game.emulation == EMU_SOKOBAN)
12057 player->LevelSolved = player->GameOver = TRUE;
12058 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12062 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12064 InitMovingField(x, y, move_direction);
12065 GfxAction[x][y] = ACTION_PUSHING;
12067 if (mode == DF_SNAP)
12068 ContinueMoving(x, y);
12070 MovPos[x][y] = (dx != 0 ? dx : dy);
12072 Pushed[x][y] = TRUE;
12073 Pushed[nextx][nexty] = TRUE;
12075 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12076 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12078 player->push_delay_value = -1; /* get new value later */
12080 #if USE_PUSH_BUGFIX
12081 /* now: check for element change _after_ element has been pushed! */
12083 if (game.use_change_when_pushing_bug)
12085 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12088 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12089 player->index_bit, dig_side);
12090 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12091 player->index_bit, dig_side);
12097 /* check for element change _after_ element has been pushed! */
12101 /* !!! TEST ONLY !!! */
12102 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12103 player->index_bit, dig_side);
12104 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12105 player->index_bit, dig_side);
12107 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12108 player->index_bit, dig_side);
12109 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12110 player->index_bit, dig_side);
12118 else if (IS_SWITCHABLE(element))
12120 if (PLAYER_SWITCHING(player, x, y))
12122 CheckTriggeredElementChangeByPlayer(x,y, element,
12123 CE_PLAYER_PRESSES_X,
12124 player->index_bit, dig_side);
12129 player->is_switching = TRUE;
12130 player->switch_x = x;
12131 player->switch_y = y;
12133 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12135 if (element == EL_ROBOT_WHEEL)
12137 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12141 DrawLevelField(x, y);
12143 else if (element == EL_SP_TERMINAL)
12147 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12149 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12151 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12152 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12155 else if (IS_BELT_SWITCH(element))
12157 ToggleBeltSwitch(x, y);
12159 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12160 element == EL_SWITCHGATE_SWITCH_DOWN)
12162 ToggleSwitchgateSwitch(x, y);
12164 else if (element == EL_LIGHT_SWITCH ||
12165 element == EL_LIGHT_SWITCH_ACTIVE)
12167 ToggleLightSwitch(x, y);
12170 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12171 SND_LIGHT_SWITCH_ACTIVATING :
12172 SND_LIGHT_SWITCH_DEACTIVATING);
12175 else if (element == EL_TIMEGATE_SWITCH)
12177 ActivateTimegateSwitch(x, y);
12179 else if (element == EL_BALLOON_SWITCH_LEFT ||
12180 element == EL_BALLOON_SWITCH_RIGHT ||
12181 element == EL_BALLOON_SWITCH_UP ||
12182 element == EL_BALLOON_SWITCH_DOWN ||
12183 element == EL_BALLOON_SWITCH_ANY)
12185 if (element == EL_BALLOON_SWITCH_ANY)
12186 game.balloon_dir = move_direction;
12188 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12189 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12190 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12191 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12194 else if (element == EL_LAMP)
12196 Feld[x][y] = EL_LAMP_ACTIVE;
12197 local_player->lights_still_needed--;
12199 ResetGfxAnimation(x, y);
12200 DrawLevelField(x, y);
12202 else if (element == EL_TIME_ORB_FULL)
12204 Feld[x][y] = EL_TIME_ORB_EMPTY;
12206 DrawGameValue_Time(TimeLeft);
12208 ResetGfxAnimation(x, y);
12209 DrawLevelField(x, y);
12212 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12216 CheckTriggeredElementChangeByPlayer(x, y, element,
12218 player->index_bit, dig_side);
12220 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12221 player->index_bit, dig_side);
12227 if (!PLAYER_SWITCHING(player, x, y))
12229 player->is_switching = TRUE;
12230 player->switch_x = x;
12231 player->switch_y = y;
12234 /* !!! TEST ONLY !!! */
12235 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12236 player->index_bit, dig_side);
12237 CheckTriggeredElementChangeByPlayer(x, y, element,
12239 player->index_bit, dig_side);
12241 CheckTriggeredElementChangeByPlayer(x, y, element,
12243 player->index_bit, dig_side);
12244 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12245 player->index_bit, dig_side);
12250 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12251 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12252 player->index_bit, dig_side);
12253 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12254 player->index_bit, dig_side);
12256 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12257 player->index_bit, dig_side);
12258 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12259 player->index_bit, dig_side);
12263 return MF_NO_ACTION;
12266 #if USE_NEW_PUSH_DELAY
12267 player->push_delay = -1;
12269 player->push_delay = 0;
12272 #if USE_PENGUIN_COLLECT_BUGFIX
12273 if (is_player) /* function can also be called by EL_PENGUIN */
12276 if (Feld[x][y] != element) /* really digged/collected something */
12277 player->is_collecting = !player->is_digging;
12283 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12285 int jx = player->jx, jy = player->jy;
12286 int x = jx + dx, y = jy + dy;
12287 int snap_direction = (dx == -1 ? MV_LEFT :
12288 dx == +1 ? MV_RIGHT :
12290 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12293 if (player->MovPos != 0)
12296 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12300 if (!player->active || !IN_LEV_FIELD(x, y))
12308 if (player->MovPos == 0)
12309 player->is_pushing = FALSE;
12311 player->is_snapping = FALSE;
12313 if (player->MovPos == 0)
12315 player->is_moving = FALSE;
12316 player->is_digging = FALSE;
12317 player->is_collecting = FALSE;
12323 if (player->is_snapping)
12326 player->MovDir = snap_direction;
12329 if (player->MovPos == 0)
12332 player->is_moving = FALSE;
12333 player->is_digging = FALSE;
12334 player->is_collecting = FALSE;
12337 player->is_dropping = FALSE;
12339 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12342 player->is_snapping = TRUE;
12345 if (player->MovPos == 0)
12348 player->is_moving = FALSE;
12349 player->is_digging = FALSE;
12350 player->is_collecting = FALSE;
12354 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12355 DrawLevelField(player->last_jx, player->last_jy);
12358 DrawLevelField(x, y);
12367 boolean DropElement(struct PlayerInfo *player)
12369 int old_element, new_element;
12370 int dropx = player->jx, dropy = player->jy;
12371 int drop_direction = player->MovDir;
12373 int drop_side = drop_direction;
12375 static int trigger_sides[4] =
12377 CH_SIDE_LEFT, /* dropping left */
12378 CH_SIDE_RIGHT, /* dropping right */
12379 CH_SIDE_TOP, /* dropping up */
12380 CH_SIDE_BOTTOM, /* dropping down */
12382 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12384 int drop_element = (player->inventory_size > 0 ?
12385 player->inventory_element[player->inventory_size - 1] :
12386 player->inventory_infinite_element != EL_UNDEFINED ?
12387 player->inventory_infinite_element :
12388 player->dynabombs_left > 0 ?
12389 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12392 #if USE_DROP_BUGFIX
12393 /* do not drop an element on top of another element; when holding drop key
12394 pressed without moving, dropped element must move away before the next
12395 element can be dropped (this is especially important if the next element
12396 is dynamite, which can be placed on background for historical reasons) */
12397 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12401 if (IS_THROWABLE(drop_element))
12403 dropx += GET_DX_FROM_DIR(drop_direction);
12404 dropy += GET_DY_FROM_DIR(drop_direction);
12406 if (!IN_LEV_FIELD(dropx, dropy))
12410 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12411 new_element = drop_element; /* default: no change when dropping */
12413 /* check if player is active, not moving and ready to drop */
12414 if (!player->active || player->MovPos || player->drop_delay > 0)
12417 /* check if player has anything that can be dropped */
12419 if (new_element == EL_UNDEFINED)
12422 if (player->inventory_size == 0 &&
12423 player->inventory_infinite_element == EL_UNDEFINED &&
12424 player->dynabombs_left == 0)
12428 /* check if anything can be dropped at the current position */
12429 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12432 /* collected custom elements can only be dropped on empty fields */
12434 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12437 if (player->inventory_size > 0 &&
12438 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12439 && old_element != EL_EMPTY)
12443 if (old_element != EL_EMPTY)
12444 Back[dropx][dropy] = old_element; /* store old element on this field */
12446 ResetGfxAnimation(dropx, dropy);
12447 ResetRandomAnimationValue(dropx, dropy);
12449 if (player->inventory_size > 0 ||
12450 player->inventory_infinite_element != EL_UNDEFINED)
12452 if (player->inventory_size > 0)
12454 player->inventory_size--;
12457 new_element = player->inventory_element[player->inventory_size];
12460 DrawGameValue_Dynamite(local_player->inventory_size);
12462 if (new_element == EL_DYNAMITE)
12463 new_element = EL_DYNAMITE_ACTIVE;
12464 else if (new_element == EL_SP_DISK_RED)
12465 new_element = EL_SP_DISK_RED_ACTIVE;
12468 Feld[dropx][dropy] = new_element;
12470 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12471 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12472 el2img(Feld[dropx][dropy]), 0);
12474 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12477 /* needed if previous element just changed to "empty" in the last frame */
12478 Changed[dropx][dropy] = FALSE; /* allow another change */
12482 /* !!! TEST ONLY !!! */
12483 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12484 player->index_bit, drop_side);
12485 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12487 player->index_bit, drop_side);
12489 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12491 player->index_bit, drop_side);
12492 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12493 player->index_bit, drop_side);
12496 TestIfElementTouchesCustomElement(dropx, dropy);
12498 else /* player is dropping a dyna bomb */
12500 player->dynabombs_left--;
12503 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12506 Feld[dropx][dropy] = new_element;
12508 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12509 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12510 el2img(Feld[dropx][dropy]), 0);
12512 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12519 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12522 InitField_WithBug1(dropx, dropy, FALSE);
12524 InitField(dropx, dropy, FALSE);
12525 if (CAN_MOVE(Feld[dropx][dropy]))
12526 InitMovDir(dropx, dropy);
12530 new_element = Feld[dropx][dropy]; /* element might have changed */
12532 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12533 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12536 int move_stepsize = element_info[new_element].move_stepsize;
12538 int move_direction, nextx, nexty;
12540 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12541 MovDir[dropx][dropy] = drop_direction;
12543 move_direction = MovDir[dropx][dropy];
12544 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12545 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12548 Changed[dropx][dropy] = FALSE; /* allow another change */
12549 CheckCollision[dropx][dropy] = 2;
12552 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12555 WasJustMoving[dropx][dropy] = 3;
12558 InitMovingField(dropx, dropy, move_direction);
12559 ContinueMoving(dropx, dropy);
12564 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12567 Changed[dropx][dropy] = FALSE; /* allow another change */
12570 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12572 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12573 CE_HITTING_SOMETHING, move_direction);
12581 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12586 player->drop_delay = 8 + 8 + 8;
12590 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12595 player->is_dropping = TRUE;
12597 #if USE_DROP_BUGFIX
12598 player->drop_x = dropx;
12599 player->drop_y = dropy;
12605 /* ------------------------------------------------------------------------- */
12606 /* game sound playing functions */
12607 /* ------------------------------------------------------------------------- */
12609 static int *loop_sound_frame = NULL;
12610 static int *loop_sound_volume = NULL;
12612 void InitPlayLevelSound()
12614 int num_sounds = getSoundListSize();
12616 checked_free(loop_sound_frame);
12617 checked_free(loop_sound_volume);
12619 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12620 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12623 static void PlayLevelSound(int x, int y, int nr)
12625 int sx = SCREENX(x), sy = SCREENY(y);
12626 int volume, stereo_position;
12627 int max_distance = 8;
12628 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12630 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12631 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12634 if (!IN_LEV_FIELD(x, y) ||
12635 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12636 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12639 volume = SOUND_MAX_VOLUME;
12641 if (!IN_SCR_FIELD(sx, sy))
12643 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12644 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12646 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12649 stereo_position = (SOUND_MAX_LEFT +
12650 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12651 (SCR_FIELDX + 2 * max_distance));
12653 if (IS_LOOP_SOUND(nr))
12655 /* This assures that quieter loop sounds do not overwrite louder ones,
12656 while restarting sound volume comparison with each new game frame. */
12658 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12661 loop_sound_volume[nr] = volume;
12662 loop_sound_frame[nr] = FrameCounter;
12665 PlaySoundExt(nr, volume, stereo_position, type);
12668 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12670 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12671 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12672 y < LEVELY(BY1) ? LEVELY(BY1) :
12673 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12677 static void PlayLevelSoundAction(int x, int y, int action)
12679 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12682 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12684 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12686 if (sound_effect != SND_UNDEFINED)
12687 PlayLevelSound(x, y, sound_effect);
12690 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12693 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12695 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12696 PlayLevelSound(x, y, sound_effect);
12699 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12701 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12703 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12704 PlayLevelSound(x, y, sound_effect);
12707 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12709 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12711 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12712 StopSound(sound_effect);
12715 static void PlayLevelMusic()
12717 if (levelset.music[level_nr] != MUS_UNDEFINED)
12718 PlayMusic(levelset.music[level_nr]); /* from config file */
12720 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12723 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12725 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12728 if (sample == SAMPLE_bug)
12729 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12735 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12739 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12743 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12747 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12751 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12755 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12759 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12762 case SAMPLE_android_clone:
12763 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12766 case SAMPLE_android_move:
12767 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12770 case SAMPLE_spring:
12771 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12775 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12779 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12782 case SAMPLE_eater_eat:
12783 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12787 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12790 case SAMPLE_collect:
12791 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12794 case SAMPLE_diamond:
12795 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12798 case SAMPLE_squash:
12799 /* !!! CHECK THIS !!! */
12801 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12803 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12807 case SAMPLE_wonderfall:
12808 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12812 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12816 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12820 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12824 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12828 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12832 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12835 case SAMPLE_wonder:
12836 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12840 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12843 case SAMPLE_exit_open:
12844 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12847 case SAMPLE_exit_leave:
12848 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12851 case SAMPLE_dynamite:
12852 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12856 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12860 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12864 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12868 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12872 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12876 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12880 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12885 void RaiseScore(int value)
12887 local_player->score += value;
12889 DrawGameValue_Score(local_player->score);
12892 void RaiseScoreElement(int element)
12897 case EL_BD_DIAMOND:
12898 case EL_EMERALD_YELLOW:
12899 case EL_EMERALD_RED:
12900 case EL_EMERALD_PURPLE:
12901 case EL_SP_INFOTRON:
12902 RaiseScore(level.score[SC_EMERALD]);
12905 RaiseScore(level.score[SC_DIAMOND]);
12908 RaiseScore(level.score[SC_CRYSTAL]);
12911 RaiseScore(level.score[SC_PEARL]);
12914 case EL_BD_BUTTERFLY:
12915 case EL_SP_ELECTRON:
12916 RaiseScore(level.score[SC_BUG]);
12919 case EL_BD_FIREFLY:
12920 case EL_SP_SNIKSNAK:
12921 RaiseScore(level.score[SC_SPACESHIP]);
12924 case EL_DARK_YAMYAM:
12925 RaiseScore(level.score[SC_YAMYAM]);
12928 RaiseScore(level.score[SC_ROBOT]);
12931 RaiseScore(level.score[SC_PACMAN]);
12934 RaiseScore(level.score[SC_NUT]);
12937 case EL_SP_DISK_RED:
12938 case EL_DYNABOMB_INCREASE_NUMBER:
12939 case EL_DYNABOMB_INCREASE_SIZE:
12940 case EL_DYNABOMB_INCREASE_POWER:
12941 RaiseScore(level.score[SC_DYNAMITE]);
12943 case EL_SHIELD_NORMAL:
12944 case EL_SHIELD_DEADLY:
12945 RaiseScore(level.score[SC_SHIELD]);
12947 case EL_EXTRA_TIME:
12948 RaiseScore(level.score[SC_TIME_BONUS]);
12962 RaiseScore(level.score[SC_KEY]);
12965 RaiseScore(element_info[element].collect_score);
12970 void RequestQuitGame(boolean ask_if_really_quit)
12972 if (AllPlayersGone ||
12973 !ask_if_really_quit ||
12974 level_editor_test_game ||
12975 Request("Do you really want to quit the game ?",
12976 REQ_ASK | REQ_STAY_CLOSED))
12978 #if defined(NETWORK_AVALIABLE)
12979 if (options.network)
12980 SendToServer_StopPlaying();
12984 game_status = GAME_MODE_MAIN;
12992 if (tape.playing && tape.deactivate_display)
12993 TapeDeactivateDisplayOff(TRUE);
12996 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12999 if (tape.playing && tape.deactivate_display)
13000 TapeDeactivateDisplayOn();
13007 /* ---------- new game button stuff ---------------------------------------- */
13009 /* graphic position values for game buttons */
13010 #define GAME_BUTTON_XSIZE 30
13011 #define GAME_BUTTON_YSIZE 30
13012 #define GAME_BUTTON_XPOS 5
13013 #define GAME_BUTTON_YPOS 215
13014 #define SOUND_BUTTON_XPOS 5
13015 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13017 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13018 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13019 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13020 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13021 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13022 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13029 } gamebutton_info[NUM_GAME_BUTTONS] =
13032 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13037 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13038 GAME_CTRL_ID_PAUSE,
13042 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13047 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13048 SOUND_CTRL_ID_MUSIC,
13049 "background music on/off"
13052 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13053 SOUND_CTRL_ID_LOOPS,
13054 "sound loops on/off"
13057 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13058 SOUND_CTRL_ID_SIMPLE,
13059 "normal sounds on/off"
13063 void CreateGameButtons()
13067 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13069 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13070 struct GadgetInfo *gi;
13073 unsigned long event_mask;
13074 int gd_xoffset, gd_yoffset;
13075 int gd_x1, gd_x2, gd_y1, gd_y2;
13078 gd_xoffset = gamebutton_info[i].x;
13079 gd_yoffset = gamebutton_info[i].y;
13080 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13081 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13083 if (id == GAME_CTRL_ID_STOP ||
13084 id == GAME_CTRL_ID_PAUSE ||
13085 id == GAME_CTRL_ID_PLAY)
13087 button_type = GD_TYPE_NORMAL_BUTTON;
13089 event_mask = GD_EVENT_RELEASED;
13090 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13091 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13095 button_type = GD_TYPE_CHECK_BUTTON;
13097 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13098 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13099 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13100 event_mask = GD_EVENT_PRESSED;
13101 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13102 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13105 gi = CreateGadget(GDI_CUSTOM_ID, id,
13106 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13107 GDI_X, DX + gd_xoffset,
13108 GDI_Y, DY + gd_yoffset,
13109 GDI_WIDTH, GAME_BUTTON_XSIZE,
13110 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13111 GDI_TYPE, button_type,
13112 GDI_STATE, GD_BUTTON_UNPRESSED,
13113 GDI_CHECKED, checked,
13114 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13115 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13116 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13117 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13118 GDI_EVENT_MASK, event_mask,
13119 GDI_CALLBACK_ACTION, HandleGameButtons,
13123 Error(ERR_EXIT, "cannot create gadget");
13125 game_gadget[id] = gi;
13129 void FreeGameButtons()
13133 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13134 FreeGadget(game_gadget[i]);
13137 static void MapGameButtons()
13141 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13142 MapGadget(game_gadget[i]);
13145 void UnmapGameButtons()
13149 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13150 UnmapGadget(game_gadget[i]);
13153 static void HandleGameButtons(struct GadgetInfo *gi)
13155 int id = gi->custom_id;
13157 if (game_status != GAME_MODE_PLAYING)
13162 case GAME_CTRL_ID_STOP:
13163 RequestQuitGame(TRUE);
13166 case GAME_CTRL_ID_PAUSE:
13167 if (options.network)
13169 #if defined(NETWORK_AVALIABLE)
13171 SendToServer_ContinuePlaying();
13173 SendToServer_PausePlaying();
13177 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13180 case GAME_CTRL_ID_PLAY:
13183 #if defined(NETWORK_AVALIABLE)
13184 if (options.network)
13185 SendToServer_ContinuePlaying();
13189 tape.pausing = FALSE;
13190 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13195 case SOUND_CTRL_ID_MUSIC:
13196 if (setup.sound_music)
13198 setup.sound_music = FALSE;
13201 else if (audio.music_available)
13203 setup.sound = setup.sound_music = TRUE;
13205 SetAudioMode(setup.sound);
13211 case SOUND_CTRL_ID_LOOPS:
13212 if (setup.sound_loops)
13213 setup.sound_loops = FALSE;
13214 else if (audio.loops_available)
13216 setup.sound = setup.sound_loops = TRUE;
13217 SetAudioMode(setup.sound);
13221 case SOUND_CTRL_ID_SIMPLE:
13222 if (setup.sound_simple)
13223 setup.sound_simple = FALSE;
13224 else if (audio.sound_available)
13226 setup.sound = setup.sound_simple = TRUE;
13227 SetAudioMode(setup.sound);