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)
65 /* for MovePlayer() */
66 #define MF_NO_ACTION 0
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
83 /* special positions in the game control window (relative to control window) */
86 #define XX_EMERALDS 29
87 #define YY_EMERALDS 54
88 #define XX_DYNAMITE 29
89 #define YY_DYNAMITE 89
98 /* special positions in the game control window (relative to main window) */
99 #define DX_LEVEL (DX + XX_LEVEL)
100 #define DY_LEVEL (DY + YY_LEVEL)
101 #define DX_EMERALDS (DX + XX_EMERALDS)
102 #define DY_EMERALDS (DY + YY_EMERALDS)
103 #define DX_DYNAMITE (DX + XX_DYNAMITE)
104 #define DY_DYNAMITE (DY + YY_DYNAMITE)
105 #define DX_KEYS (DX + XX_KEYS)
106 #define DY_KEYS (DY + YY_KEYS)
107 #define DX_SCORE (DX + XX_SCORE)
108 #define DY_SCORE (DY + YY_SCORE)
109 #define DX_TIME1 (DX + XX_TIME1)
110 #define DX_TIME2 (DX + XX_TIME2)
111 #define DY_TIME (DY + YY_TIME)
113 /* values for initial player move delay (initial delay counter value) */
114 #define INITIAL_MOVE_DELAY_OFF -1
115 #define INITIAL_MOVE_DELAY_ON 0
117 /* values for player movement speed (which is in fact a delay value) */
118 #define MOVE_DELAY_NORMAL_SPEED 8
119 #define MOVE_DELAY_HIGH_SPEED 4
121 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
122 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
123 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
124 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
126 /* values for other actions */
127 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
129 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
130 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
132 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
134 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
135 RND(element_info[e].push_delay_random))
136 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
137 RND(element_info[e].drop_delay_random))
138 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
139 RND(element_info[e].move_delay_random))
140 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
141 (element_info[e].move_delay_random))
143 #define GET_TARGET_ELEMENT(e, ch) \
144 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
145 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
147 #define GET_VALID_PLAYER_ELEMENT(e) \
148 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
150 #define CAN_GROW_INTO(e) \
151 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
153 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
154 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
157 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
158 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
159 (CAN_MOVE_INTO_ACID(e) && \
160 Feld[x][y] == EL_ACID) || \
163 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
164 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
165 (CAN_MOVE_INTO_ACID(e) && \
166 Feld[x][y] == EL_ACID) || \
169 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
170 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
172 (CAN_MOVE_INTO_ACID(e) && \
173 Feld[x][y] == EL_ACID) || \
174 (DONT_COLLIDE_WITH(e) && \
176 !PLAYER_ENEMY_PROTECTED(x, y))))
179 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
180 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
182 (DONT_COLLIDE_WITH(e) && \
184 !PLAYER_ENEMY_PROTECTED(x, y))))
187 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
191 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
194 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
195 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
199 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
202 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
207 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
208 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
210 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
211 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
213 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
214 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
216 #define PIG_CAN_ENTER_FIELD(e, x, y) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
219 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
221 IS_FOOD_PENGUIN(Feld[x][y])))
222 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
223 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
225 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
228 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
235 (CAN_MOVE_INTO_ACID(e) && \
236 Feld[x][y] == EL_ACID) || \
237 Feld[x][y] == EL_DIAMOND))
239 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID) || \
243 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
245 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
246 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
247 (CAN_MOVE_INTO_ACID(e) && \
248 Feld[x][y] == EL_ACID) || \
249 IS_AMOEBOID(Feld[x][y])))
251 #define PIG_CAN_ENTER_FIELD(e, x, y) \
252 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
253 (CAN_MOVE_INTO_ACID(e) && \
254 Feld[x][y] == EL_ACID) || \
255 IS_FOOD_PIG(Feld[x][y])))
257 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
258 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
259 (CAN_MOVE_INTO_ACID(e) && \
260 Feld[x][y] == EL_ACID) || \
261 IS_FOOD_PENGUIN(Feld[x][y]) || \
262 Feld[x][y] == EL_EXIT_OPEN))
264 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
265 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
266 (CAN_MOVE_INTO_ACID(e) && \
267 Feld[x][y] == EL_ACID)))
269 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
270 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
271 (CAN_MOVE_INTO_ACID(e) && \
272 Feld[x][y] == EL_ACID) || \
275 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
276 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
277 (CAN_MOVE_INTO_ACID(e) && \
278 Feld[x][y] == EL_ACID)))
282 #define GROUP_NR(e) ((e) - EL_GROUP_START)
283 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
284 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
285 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
287 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
288 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
291 #define CE_ENTER_FIELD_COND(e, x, y) \
292 (!IS_PLAYER(x, y) && \
293 (Feld[x][y] == EL_ACID || \
294 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
296 #define CE_ENTER_FIELD_COND(e, x, y) \
297 (!IS_PLAYER(x, y) && \
298 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
301 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
302 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
304 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
305 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
307 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
308 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
309 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
310 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
312 /* game button identifiers */
313 #define GAME_CTRL_ID_STOP 0
314 #define GAME_CTRL_ID_PAUSE 1
315 #define GAME_CTRL_ID_PLAY 2
316 #define SOUND_CTRL_ID_MUSIC 3
317 #define SOUND_CTRL_ID_LOOPS 4
318 #define SOUND_CTRL_ID_SIMPLE 5
320 #define NUM_GAME_BUTTONS 6
323 /* forward declaration for internal use */
325 static void AdvanceFrameAndPlayerCounters(int);
327 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
328 static boolean MovePlayer(struct PlayerInfo *, int, int);
329 static void ScrollPlayer(struct PlayerInfo *, int);
330 static void ScrollScreen(struct PlayerInfo *, int);
332 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
334 static void InitBeltMovement(void);
335 static void CloseAllOpenTimegates(void);
336 static void CheckGravityMovement(struct PlayerInfo *);
337 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
338 static void KillHeroUnlessEnemyProtected(int, int);
339 static void KillHeroUnlessExplosionProtected(int, int);
341 static void TestIfPlayerTouchesCustomElement(int, int);
342 static void TestIfElementTouchesCustomElement(int, int);
343 static void TestIfElementHitsCustomElement(int, int, int);
345 static void TestIfElementSmashesCustomElement(int, int, int);
348 static void ChangeElement(int, int, int);
350 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
351 #define CheckTriggeredElementChange(x, y, e, ev) \
352 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
354 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
355 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
356 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
357 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
358 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
359 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
362 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
363 #define CheckElementChange(x, y, e, te, ev) \
364 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
365 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
366 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
367 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
368 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
369 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
370 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
372 static void PlayLevelSound(int, int, int);
373 static void PlayLevelSoundNearest(int, int, int);
374 static void PlayLevelSoundAction(int, int, int);
375 static void PlayLevelSoundElementAction(int, int, int, int);
376 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
377 static void PlayLevelSoundActionIfLoop(int, int, int);
378 static void StopLevelSoundActionIfLoop(int, int, int);
379 static void PlayLevelMusic();
381 static void MapGameButtons();
382 static void HandleGameButtons(struct GadgetInfo *);
384 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
387 /* ------------------------------------------------------------------------- */
388 /* definition of elements that automatically change to other elements after */
389 /* a specified time, eventually calling a function when changing */
390 /* ------------------------------------------------------------------------- */
392 /* forward declaration for changer functions */
393 static void InitBuggyBase(int x, int y);
394 static void WarnBuggyBase(int x, int y);
396 static void InitTrap(int x, int y);
397 static void ActivateTrap(int x, int y);
398 static void ChangeActiveTrap(int x, int y);
400 static void InitRobotWheel(int x, int y);
401 static void RunRobotWheel(int x, int y);
402 static void StopRobotWheel(int x, int y);
404 static void InitTimegateWheel(int x, int y);
405 static void RunTimegateWheel(int x, int y);
407 struct ChangingElementInfo
412 void (*pre_change_function)(int x, int y);
413 void (*change_function)(int x, int y);
414 void (*post_change_function)(int x, int y);
417 static struct ChangingElementInfo change_delay_list[] =
468 EL_SWITCHGATE_OPENING,
476 EL_SWITCHGATE_CLOSING,
477 EL_SWITCHGATE_CLOSED,
509 EL_ACID_SPLASH_RIGHT,
518 EL_SP_BUGGY_BASE_ACTIVATING,
525 EL_SP_BUGGY_BASE_ACTIVATING,
526 EL_SP_BUGGY_BASE_ACTIVE,
533 EL_SP_BUGGY_BASE_ACTIVE,
557 EL_ROBOT_WHEEL_ACTIVE,
565 EL_TIMEGATE_SWITCH_ACTIVE,
586 int push_delay_fixed, push_delay_random;
591 { EL_BALLOON, 0, 0 },
593 { EL_SOKOBAN_OBJECT, 2, 0 },
594 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
595 { EL_SATELLITE, 2, 0 },
596 { EL_SP_DISK_YELLOW, 2, 0 },
598 { EL_UNDEFINED, 0, 0 },
606 move_stepsize_list[] =
608 { EL_AMOEBA_DROP, 2 },
609 { EL_AMOEBA_DROPPING, 2 },
610 { EL_QUICKSAND_FILLING, 1 },
611 { EL_QUICKSAND_EMPTYING, 1 },
612 { EL_MAGIC_WALL_FILLING, 2 },
613 { EL_BD_MAGIC_WALL_FILLING, 2 },
614 { EL_MAGIC_WALL_EMPTYING, 2 },
615 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
625 collect_count_list[] =
628 { EL_BD_DIAMOND, 1 },
629 { EL_EMERALD_YELLOW, 1 },
630 { EL_EMERALD_RED, 1 },
631 { EL_EMERALD_PURPLE, 1 },
633 { EL_SP_INFOTRON, 1 },
645 access_direction_list[] =
647 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
649 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
650 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
651 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
652 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
653 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
654 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
655 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
656 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
657 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
659 { EL_SP_PORT_LEFT, MV_RIGHT },
660 { EL_SP_PORT_RIGHT, MV_LEFT },
661 { EL_SP_PORT_UP, MV_DOWN },
662 { EL_SP_PORT_DOWN, MV_UP },
663 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
664 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
665 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
666 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
667 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
668 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
669 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
670 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
671 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
672 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
673 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
674 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
675 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
676 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
677 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
679 { EL_UNDEFINED, MV_NO_MOVING }
682 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
684 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
685 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
686 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
687 IS_JUST_CHANGING(x, y))
689 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
692 void GetPlayerConfig()
694 if (!audio.sound_available)
695 setup.sound_simple = FALSE;
697 if (!audio.loops_available)
698 setup.sound_loops = FALSE;
700 if (!audio.music_available)
701 setup.sound_music = FALSE;
703 if (!video.fullscreen_available)
704 setup.fullscreen = FALSE;
706 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
708 SetAudioMode(setup.sound);
712 static int getBeltNrFromBeltElement(int element)
714 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
715 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
716 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
719 static int getBeltNrFromBeltActiveElement(int element)
721 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
722 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
723 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
726 static int getBeltNrFromBeltSwitchElement(int element)
728 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
729 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
730 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
733 static int getBeltDirNrFromBeltSwitchElement(int element)
735 static int belt_base_element[4] =
737 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
738 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
739 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
740 EL_CONVEYOR_BELT_4_SWITCH_LEFT
743 int belt_nr = getBeltNrFromBeltSwitchElement(element);
744 int belt_dir_nr = element - belt_base_element[belt_nr];
746 return (belt_dir_nr % 3);
749 static int getBeltDirFromBeltSwitchElement(int element)
751 static int belt_move_dir[3] =
758 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
760 return belt_move_dir[belt_dir_nr];
763 static void InitPlayerField(int x, int y, int element, boolean init_game)
765 if (element == EL_SP_MURPHY)
769 if (stored_player[0].present)
771 Feld[x][y] = EL_SP_MURPHY_CLONE;
777 stored_player[0].use_murphy_graphic = TRUE;
780 Feld[x][y] = EL_PLAYER_1;
786 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
787 int jx = player->jx, jy = player->jy;
789 player->present = TRUE;
791 player->block_last_field = (element == EL_SP_MURPHY ?
792 level.sp_block_last_field :
793 level.block_last_field);
795 #if USE_NEW_BLOCK_STYLE
798 /* ---------- initialize player's last field block delay --------------- */
800 /* always start with reliable default value (no adjustment needed) */
801 player->block_delay_adjustment = 0;
803 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
804 if (player->block_last_field && element == EL_SP_MURPHY)
805 player->block_delay_adjustment = 1;
807 /* special case 2: in game engines before 3.1.1, blocking was different */
808 if (game.use_block_last_field_bug)
809 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
812 /* blocking the last field when moving was corrected in version 3.1.1 */
813 if (game.use_block_last_field_bug)
815 /* even "not blocking" was blocking the last field for one frame */
816 level.block_delay = (level.block_last_field ? 7 : 1);
817 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
819 level.block_last_field = TRUE;
820 level.sp_block_last_field = TRUE;
824 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
825 level.block_delay = 8; /* when blocking, block 8 frames */
826 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
830 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
836 player->block_delay = (player->block_last_field ?
837 (element == EL_SP_MURPHY ?
838 level.sp_block_delay :
839 level.block_delay) : 0);
841 player->block_delay = (element == EL_SP_MURPHY ?
842 (player->block_last_field ? 7 : 1) :
843 (player->block_last_field ? 7 : 1));
849 printf("::: block_last_field == %d, block_delay = %d\n",
850 player->block_last_field, player->block_delay);
854 if (!options.network || player->connected)
856 player->active = TRUE;
858 /* remove potentially duplicate players */
859 if (StorePlayer[jx][jy] == Feld[x][y])
860 StorePlayer[jx][jy] = 0;
862 StorePlayer[x][y] = Feld[x][y];
866 printf("Player %d activated.\n", player->element_nr);
867 printf("[Local player is %d and currently %s.]\n",
868 local_player->element_nr,
869 local_player->active ? "active" : "not active");
873 Feld[x][y] = EL_EMPTY;
875 player->jx = player->last_jx = x;
876 player->jy = player->last_jy = y;
880 static void InitField(int x, int y, boolean init_game)
882 int element = Feld[x][y];
891 InitPlayerField(x, y, element, init_game);
894 case EL_SOKOBAN_FIELD_PLAYER:
895 element = Feld[x][y] = EL_PLAYER_1;
896 InitField(x, y, init_game);
898 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
899 InitField(x, y, init_game);
902 case EL_SOKOBAN_FIELD_EMPTY:
903 local_player->sokobanfields_still_needed++;
907 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
908 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
909 else if (x > 0 && Feld[x-1][y] == EL_ACID)
910 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
911 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
912 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
913 else if (y > 0 && Feld[x][y-1] == EL_ACID)
914 Feld[x][y] = EL_ACID_POOL_BOTTOM;
915 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
916 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
924 case EL_SPACESHIP_RIGHT:
925 case EL_SPACESHIP_UP:
926 case EL_SPACESHIP_LEFT:
927 case EL_SPACESHIP_DOWN:
929 case EL_BD_BUTTERFLY_RIGHT:
930 case EL_BD_BUTTERFLY_UP:
931 case EL_BD_BUTTERFLY_LEFT:
932 case EL_BD_BUTTERFLY_DOWN:
933 case EL_BD_BUTTERFLY:
934 case EL_BD_FIREFLY_RIGHT:
935 case EL_BD_FIREFLY_UP:
936 case EL_BD_FIREFLY_LEFT:
937 case EL_BD_FIREFLY_DOWN:
939 case EL_PACMAN_RIGHT:
963 if (y == lev_fieldy - 1)
965 Feld[x][y] = EL_AMOEBA_GROWING;
966 Store[x][y] = EL_AMOEBA_WET;
970 case EL_DYNAMITE_ACTIVE:
971 case EL_SP_DISK_RED_ACTIVE:
972 case EL_DYNABOMB_PLAYER_1_ACTIVE:
973 case EL_DYNABOMB_PLAYER_2_ACTIVE:
974 case EL_DYNABOMB_PLAYER_3_ACTIVE:
975 case EL_DYNABOMB_PLAYER_4_ACTIVE:
980 local_player->lights_still_needed++;
984 local_player->friends_still_needed++;
989 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
994 Feld[x][y] = EL_EMPTY;
999 case EL_EM_KEY_1_FILE:
1000 Feld[x][y] = EL_EM_KEY_1;
1002 case EL_EM_KEY_2_FILE:
1003 Feld[x][y] = EL_EM_KEY_2;
1005 case EL_EM_KEY_3_FILE:
1006 Feld[x][y] = EL_EM_KEY_3;
1008 case EL_EM_KEY_4_FILE:
1009 Feld[x][y] = EL_EM_KEY_4;
1013 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1014 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1015 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1016 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1017 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1018 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1019 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1020 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1021 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1022 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1023 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1024 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1027 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1028 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1029 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1031 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1033 game.belt_dir[belt_nr] = belt_dir;
1034 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1036 else /* more than one switch -- set it like the first switch */
1038 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1043 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1045 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1048 case EL_LIGHT_SWITCH_ACTIVE:
1050 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1054 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1056 else if (IS_GROUP_ELEMENT(element))
1058 struct ElementGroupInfo *group = element_info[element].group;
1059 int last_anim_random_frame = gfx.anim_random_frame;
1062 if (group->choice_mode == ANIM_RANDOM)
1063 gfx.anim_random_frame = RND(group->num_elements_resolved);
1065 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1066 group->choice_mode, 0,
1069 if (group->choice_mode == ANIM_RANDOM)
1070 gfx.anim_random_frame = last_anim_random_frame;
1072 group->choice_pos++;
1074 Feld[x][y] = group->element_resolved[element_pos];
1076 InitField(x, y, init_game);
1082 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1084 InitField(x, y, init_game);
1086 /* not needed to call InitMovDir() -- already done by InitField()! */
1087 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1088 CAN_MOVE(Feld[x][y]))
1092 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1094 int old_element = Feld[x][y];
1096 InitField(x, y, init_game);
1098 /* not needed to call InitMovDir() -- already done by InitField()! */
1099 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1100 CAN_MOVE(old_element) &&
1101 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1104 /* this case is in fact a combination of not less than three bugs:
1105 first, it calls InitMovDir() for elements that can move, although this is
1106 already done by InitField(); then, it checks the element that was at this
1107 field _before_ the call to InitField() (which can change it); lastly, it
1108 was not called for "mole with direction" elements, which were treated as
1109 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1113 inline void DrawGameValue_Emeralds(int value)
1115 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1118 inline void DrawGameValue_Dynamite(int value)
1120 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1123 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1127 /* currently only 4 of 8 possible keys are displayed */
1128 for (i = 0; i < STD_NUM_KEYS; i++)
1130 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1131 el2edimg(EL_KEY_1 + i));
1134 inline void DrawGameValue_Score(int value)
1136 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1139 inline void DrawGameValue_Time(int value)
1142 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1144 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1147 inline void DrawGameValue_Level(int value)
1150 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1153 /* misuse area for displaying emeralds to draw bigger level number */
1154 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1155 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1157 /* now copy it to the area for displaying level number */
1158 BlitBitmap(drawto, drawto,
1159 DX_EMERALDS, DY_EMERALDS + 1,
1160 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1161 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1162 DX_LEVEL - 1, DY_LEVEL + 1);
1164 /* restore the area for displaying emeralds */
1165 DrawGameValue_Emeralds(local_player->gems_still_needed);
1167 /* yes, this is all really ugly :-) */
1171 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1174 int key[MAX_NUM_KEYS];
1177 for (i = 0; i < MAX_NUM_KEYS; i++)
1178 key[i] = key_bits & (1 << i);
1180 DrawGameValue_Level(level_nr);
1182 DrawGameValue_Emeralds(emeralds);
1183 DrawGameValue_Dynamite(dynamite);
1184 DrawGameValue_Score(score);
1185 DrawGameValue_Time(time);
1187 DrawGameValue_Keys(key);
1190 void DrawGameDoorValues()
1194 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1196 DrawGameDoorValues_EM();
1201 DrawGameValue_Level(level_nr);
1203 DrawGameValue_Emeralds(local_player->gems_still_needed);
1204 DrawGameValue_Dynamite(local_player->inventory_size);
1205 DrawGameValue_Score(local_player->score);
1206 DrawGameValue_Time(TimeLeft);
1208 for (i = 0; i < MAX_PLAYERS; i++)
1209 DrawGameValue_Keys(stored_player[i].key);
1212 static void resolve_group_element(int group_element, int recursion_depth)
1214 static int group_nr;
1215 static struct ElementGroupInfo *group;
1216 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1219 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1221 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1222 group_element - EL_GROUP_START + 1);
1224 /* replace element which caused too deep recursion by question mark */
1225 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1230 if (recursion_depth == 0) /* initialization */
1232 group = element_info[group_element].group;
1233 group_nr = group_element - EL_GROUP_START;
1235 group->num_elements_resolved = 0;
1236 group->choice_pos = 0;
1239 for (i = 0; i < actual_group->num_elements; i++)
1241 int element = actual_group->element[i];
1243 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1246 if (IS_GROUP_ELEMENT(element))
1247 resolve_group_element(element, recursion_depth + 1);
1250 group->element_resolved[group->num_elements_resolved++] = element;
1251 element_info[element].in_group[group_nr] = TRUE;
1256 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1258 printf("::: group %d: %d resolved elements\n",
1259 group_element - EL_GROUP_START, group->num_elements_resolved);
1260 for (i = 0; i < group->num_elements_resolved; i++)
1261 printf("::: - %d ['%s']\n", group->element_resolved[i],
1262 element_info[group->element_resolved[i]].token_name);
1269 =============================================================================
1271 -----------------------------------------------------------------------------
1272 initialize game engine due to level / tape version number
1273 =============================================================================
1276 static void InitGameEngine()
1280 /* set game engine from tape file when re-playing, else from level file */
1281 game.engine_version = (tape.playing ? tape.engine_version :
1282 level.game_version);
1284 /* ---------------------------------------------------------------------- */
1285 /* set flags for bugs and changes according to active game engine version */
1286 /* ---------------------------------------------------------------------- */
1289 Summary of bugfix/change:
1290 Fixed handling for custom elements that change when pushed by the player.
1292 Fixed/changed in version:
1296 Before 3.1.0, custom elements that "change when pushing" changed directly
1297 after the player started pushing them (until then handled in "DigField()").
1298 Since 3.1.0, these custom elements are not changed until the "pushing"
1299 move of the element is finished (now handled in "ContinueMoving()").
1301 Affected levels/tapes:
1302 The first condition is generally needed for all levels/tapes before version
1303 3.1.0, which might use the old behaviour before it was changed; known tapes
1304 that are affected are some tapes from the level set "Walpurgis Gardens" by
1306 The second condition is an exception from the above case and is needed for
1307 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1308 above (including some development versions of 3.1.0), but before it was
1309 known that this change would break tapes like the above and was fixed in
1310 3.1.1, so that the changed behaviour was active although the engine version
1311 while recording maybe was before 3.1.0. There is at least one tape that is
1312 affected by this exception, which is the tape for the one-level set "Bug
1313 Machine" by Juergen Bonhagen.
1316 game.use_change_when_pushing_bug =
1317 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1319 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1320 tape.game_version < VERSION_IDENT(3,1,1,0)));
1323 Summary of bugfix/change:
1324 Fixed handling for blocking the field the player leaves when moving.
1326 Fixed/changed in version:
1330 Before 3.1.1, when "block last field when moving" was enabled, the field
1331 the player is leaving when moving was blocked for the time of the move,
1332 and was directly unblocked afterwards. This resulted in the last field
1333 being blocked for exactly one less than the number of frames of one player
1334 move. Additionally, even when blocking was disabled, the last field was
1335 blocked for exactly one frame.
1336 Since 3.1.1, due to changes in player movement handling, the last field
1337 is not blocked at all when blocking is disabled. When blocking is enabled,
1338 the last field is blocked for exactly the number of frames of one player
1339 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1340 last field is blocked for exactly one more than the number of frames of
1343 Affected levels/tapes:
1344 (!!! yet to be determined -- probably many !!!)
1347 game.use_block_last_field_bug =
1348 (game.engine_version < VERSION_IDENT(3,1,1,0));
1350 /* ---------------------------------------------------------------------- */
1352 /* dynamically adjust element properties according to game engine version */
1353 InitElementPropertiesEngine(game.engine_version);
1356 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1357 printf(" tape version == %06d [%s] [file: %06d]\n",
1358 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1360 printf(" => game.engine_version == %06d\n", game.engine_version);
1363 /* ---------- recursively resolve group elements ------------------------- */
1365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1366 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1367 element_info[i].in_group[j] = FALSE;
1369 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1370 resolve_group_element(EL_GROUP_START + i, 0);
1372 /* ---------- initialize player's initial move delay --------------------- */
1374 #if USE_NEW_MOVE_DELAY
1375 /* dynamically adjust player properties according to level information */
1376 game.initial_move_delay_value =
1377 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1379 /* dynamically adjust player properties according to game engine version */
1380 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1381 game.initial_move_delay_value : 0);
1383 /* dynamically adjust player properties according to game engine version */
1384 game.initial_move_delay =
1385 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1386 INITIAL_MOVE_DELAY_OFF);
1388 /* dynamically adjust player properties according to level information */
1389 game.initial_move_delay_value =
1390 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1393 /* ---------- initialize player's initial push delay --------------------- */
1395 /* dynamically adjust player properties according to game engine version */
1396 game.initial_push_delay_value =
1397 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1399 /* ---------- initialize changing elements ------------------------------- */
1401 /* initialize changing elements information */
1402 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1404 struct ElementInfo *ei = &element_info[i];
1406 /* this pointer might have been changed in the level editor */
1407 ei->change = &ei->change_page[0];
1409 if (!IS_CUSTOM_ELEMENT(i))
1411 ei->change->target_element = EL_EMPTY_SPACE;
1412 ei->change->delay_fixed = 0;
1413 ei->change->delay_random = 0;
1414 ei->change->delay_frames = 1;
1417 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1419 ei->has_change_event[j] = FALSE;
1421 ei->event_page_nr[j] = 0;
1422 ei->event_page[j] = &ei->change_page[0];
1426 /* add changing elements from pre-defined list */
1427 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1429 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1430 struct ElementInfo *ei = &element_info[ch_delay->element];
1432 ei->change->target_element = ch_delay->target_element;
1433 ei->change->delay_fixed = ch_delay->change_delay;
1435 ei->change->pre_change_function = ch_delay->pre_change_function;
1436 ei->change->change_function = ch_delay->change_function;
1437 ei->change->post_change_function = ch_delay->post_change_function;
1439 ei->has_change_event[CE_DELAY] = TRUE;
1442 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1447 /* add change events from custom element configuration */
1448 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1450 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1452 for (j = 0; j < ei->num_change_pages; j++)
1454 if (!ei->change_page[j].can_change)
1457 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1459 /* only add event page for the first page found with this event */
1460 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1462 ei->has_change_event[k] = TRUE;
1464 ei->event_page_nr[k] = j;
1465 ei->event_page[k] = &ei->change_page[j];
1473 /* add change events from custom element configuration */
1474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1476 int element = EL_CUSTOM_START + i;
1478 /* only add custom elements that change after fixed/random frame delay */
1479 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1480 element_info[element].has_change_event[CE_DELAY] = TRUE;
1484 /* ---------- initialize run-time trigger player and element ------------- */
1486 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1488 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1490 for (j = 0; j < ei->num_change_pages; j++)
1492 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1493 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1497 /* ---------- initialize trigger events ---------------------------------- */
1499 /* initialize trigger events information */
1500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1501 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1502 trigger_events[i][j] = FALSE;
1505 /* add trigger events from element change event properties */
1506 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1508 struct ElementInfo *ei = &element_info[i];
1510 for (j = 0; j < ei->num_change_pages; j++)
1512 if (!ei->change_page[j].can_change)
1515 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1517 int trigger_element = ei->change_page[j].trigger_element;
1519 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1521 if (ei->change_page[j].has_event[k])
1523 if (IS_GROUP_ELEMENT(trigger_element))
1525 struct ElementGroupInfo *group =
1526 element_info[trigger_element].group;
1528 for (l = 0; l < group->num_elements_resolved; l++)
1529 trigger_events[group->element_resolved[l]][k] = TRUE;
1532 trigger_events[trigger_element][k] = TRUE;
1539 /* add trigger events from element change event properties */
1540 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1541 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1542 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1543 if (element_info[i].change->has_event[j])
1544 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1547 /* ---------- initialize push delay -------------------------------------- */
1549 /* initialize push delay values to default */
1550 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1552 if (!IS_CUSTOM_ELEMENT(i))
1554 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1555 element_info[i].push_delay_random = game.default_push_delay_random;
1559 /* set push delay value for certain elements from pre-defined list */
1560 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1562 int e = push_delay_list[i].element;
1564 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1565 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1568 /* set push delay value for Supaplex elements for newer engine versions */
1569 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1571 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1573 if (IS_SP_ELEMENT(i))
1575 #if USE_NEW_MOVE_STYLE
1576 /* set SP push delay to just enough to push under a falling zonk */
1577 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1579 element_info[i].push_delay_fixed = delay;
1580 element_info[i].push_delay_random = 0;
1582 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1583 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1589 /* ---------- initialize move stepsize ----------------------------------- */
1591 /* initialize move stepsize values to default */
1592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1593 if (!IS_CUSTOM_ELEMENT(i))
1594 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1596 /* set move stepsize value for certain elements from pre-defined list */
1597 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1599 int e = move_stepsize_list[i].element;
1601 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1605 /* ---------- initialize move dig/leave ---------------------------------- */
1607 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1609 element_info[i].can_leave_element = FALSE;
1610 element_info[i].can_leave_element_last = FALSE;
1614 /* ---------- initialize gem count --------------------------------------- */
1616 /* initialize gem count values for each element */
1617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1618 if (!IS_CUSTOM_ELEMENT(i))
1619 element_info[i].collect_count = 0;
1621 /* add gem count values for all elements from pre-defined list */
1622 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1623 element_info[collect_count_list[i].element].collect_count =
1624 collect_count_list[i].count;
1626 /* ---------- initialize access direction -------------------------------- */
1628 /* initialize access direction values to default (access from every side) */
1629 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1630 if (!IS_CUSTOM_ELEMENT(i))
1631 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1633 /* set access direction value for certain elements from pre-defined list */
1634 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1635 element_info[access_direction_list[i].element].access_direction =
1636 access_direction_list[i].direction;
1641 =============================================================================
1643 -----------------------------------------------------------------------------
1644 initialize and start new game
1645 =============================================================================
1650 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1651 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1652 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1659 #if USE_NEW_AMOEBA_CODE
1660 printf("Using new amoeba code.\n");
1662 printf("Using old amoeba code.\n");
1667 /* don't play tapes over network */
1668 network_playing = (options.network && !tape.playing);
1670 for (i = 0; i < MAX_PLAYERS; i++)
1672 struct PlayerInfo *player = &stored_player[i];
1674 player->index_nr = i;
1675 player->index_bit = (1 << i);
1676 player->element_nr = EL_PLAYER_1 + i;
1678 player->present = FALSE;
1679 player->active = FALSE;
1682 player->effective_action = 0;
1683 player->programmed_action = 0;
1686 player->gems_still_needed = level.gems_needed;
1687 player->sokobanfields_still_needed = 0;
1688 player->lights_still_needed = 0;
1689 player->friends_still_needed = 0;
1691 for (j = 0; j < MAX_NUM_KEYS; j++)
1692 player->key[j] = FALSE;
1694 player->dynabomb_count = 0;
1695 player->dynabomb_size = 1;
1696 player->dynabombs_left = 0;
1697 player->dynabomb_xl = FALSE;
1699 player->MovDir = MV_NO_MOVING;
1702 player->GfxDir = MV_NO_MOVING;
1703 player->GfxAction = ACTION_DEFAULT;
1705 player->StepFrame = 0;
1707 player->use_murphy_graphic = FALSE;
1709 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1710 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1712 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1714 player->actual_frame_counter = 0;
1716 player->step_counter = 0;
1718 player->last_move_dir = MV_NO_MOVING;
1720 player->is_waiting = FALSE;
1721 player->is_moving = FALSE;
1722 player->is_auto_moving = FALSE;
1723 player->is_digging = FALSE;
1724 player->is_snapping = FALSE;
1725 player->is_collecting = FALSE;
1726 player->is_pushing = FALSE;
1727 player->is_switching = FALSE;
1728 player->is_dropping = FALSE;
1730 player->is_bored = FALSE;
1731 player->is_sleeping = FALSE;
1733 player->frame_counter_bored = -1;
1734 player->frame_counter_sleeping = -1;
1736 player->anim_delay_counter = 0;
1737 player->post_delay_counter = 0;
1739 player->action_waiting = ACTION_DEFAULT;
1740 player->last_action_waiting = ACTION_DEFAULT;
1741 player->special_action_bored = ACTION_DEFAULT;
1742 player->special_action_sleeping = ACTION_DEFAULT;
1744 player->num_special_action_bored = 0;
1745 player->num_special_action_sleeping = 0;
1747 /* determine number of special actions for bored and sleeping animation */
1748 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1750 boolean found = FALSE;
1752 for (k = 0; k < NUM_DIRECTIONS; k++)
1753 if (el_act_dir2img(player->element_nr, j, k) !=
1754 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1758 player->num_special_action_bored++;
1762 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1764 boolean found = FALSE;
1766 for (k = 0; k < NUM_DIRECTIONS; k++)
1767 if (el_act_dir2img(player->element_nr, j, k) !=
1768 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1772 player->num_special_action_sleeping++;
1777 player->switch_x = -1;
1778 player->switch_y = -1;
1781 player->drop_x = -1;
1782 player->drop_y = -1;
1785 player->show_envelope = 0;
1787 player->move_delay = game.initial_move_delay;
1788 player->move_delay_value = game.initial_move_delay_value;
1790 player->move_delay_reset_counter = 0;
1792 #if USE_NEW_PUSH_DELAY
1793 player->push_delay = -1; /* initialized when pushing starts */
1794 player->push_delay_value = game.initial_push_delay_value;
1796 player->push_delay = 0;
1797 player->push_delay_value = game.initial_push_delay_value;
1800 player->drop_delay = 0;
1802 player->last_jx = player->last_jy = 0;
1803 player->jx = player->jy = 0;
1805 player->shield_normal_time_left = 0;
1806 player->shield_deadly_time_left = 0;
1808 player->inventory_infinite_element = EL_UNDEFINED;
1809 player->inventory_size = 0;
1811 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1812 SnapField(player, 0, 0);
1814 player->LevelSolved = FALSE;
1815 player->GameOver = FALSE;
1818 network_player_action_received = FALSE;
1820 #if defined(NETWORK_AVALIABLE)
1821 /* initial null action */
1822 if (network_playing)
1823 SendToServer_MovePlayer(MV_NO_MOVING);
1832 TimeLeft = level.time;
1835 ScreenMovDir = MV_NO_MOVING;
1839 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1841 AllPlayersGone = FALSE;
1843 game.yamyam_content_nr = 0;
1844 game.magic_wall_active = FALSE;
1845 game.magic_wall_time_left = 0;
1846 game.light_time_left = 0;
1847 game.timegate_time_left = 0;
1848 game.switchgate_pos = 0;
1849 game.balloon_dir = MV_NO_MOVING;
1850 game.gravity = level.initial_gravity;
1851 game.explosions_delayed = TRUE;
1853 game.envelope_active = FALSE;
1855 for (i = 0; i < NUM_BELTS; i++)
1857 game.belt_dir[i] = MV_NO_MOVING;
1858 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1861 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1862 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1864 for (x = 0; x < lev_fieldx; x++)
1866 for (y = 0; y < lev_fieldy; y++)
1868 Feld[x][y] = level.field[x][y];
1869 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1870 ChangeDelay[x][y] = 0;
1871 ChangePage[x][y] = -1;
1872 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1874 WasJustMoving[x][y] = 0;
1875 WasJustFalling[x][y] = 0;
1876 CheckCollision[x][y] = 0;
1878 Pushed[x][y] = FALSE;
1880 Changed[x][y] = FALSE;
1881 ChangeEvent[x][y] = -1;
1883 ExplodePhase[x][y] = 0;
1884 ExplodeDelay[x][y] = 0;
1885 ExplodeField[x][y] = EX_TYPE_NONE;
1887 RunnerVisit[x][y] = 0;
1888 PlayerVisit[x][y] = 0;
1891 GfxRandom[x][y] = INIT_GFX_RANDOM();
1892 GfxElement[x][y] = EL_UNDEFINED;
1893 GfxAction[x][y] = ACTION_DEFAULT;
1894 GfxDir[x][y] = MV_NO_MOVING;
1898 for (y = 0; y < lev_fieldy; y++)
1900 for (x = 0; x < lev_fieldx; x++)
1902 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1904 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1906 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1909 InitField(x, y, TRUE);
1915 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1916 emulate_sb ? EMU_SOKOBAN :
1917 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1919 /* initialize explosion and ignition delay */
1920 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1922 if (!IS_CUSTOM_ELEMENT(i))
1925 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1926 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1927 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1928 int last_phase = (num_phase + 1) * delay;
1929 int half_phase = (num_phase / 2) * delay;
1931 element_info[i].explosion_delay = last_phase - 1;
1932 element_info[i].ignition_delay = half_phase;
1935 if (i == EL_BLACK_ORB)
1936 element_info[i].ignition_delay = 0;
1938 if (i == EL_BLACK_ORB)
1939 element_info[i].ignition_delay = 1;
1944 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1945 element_info[i].explosion_delay = 1;
1947 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1948 element_info[i].ignition_delay = 1;
1952 /* correct non-moving belts to start moving left */
1953 for (i = 0; i < NUM_BELTS; i++)
1954 if (game.belt_dir[i] == MV_NO_MOVING)
1955 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1957 /* check if any connected player was not found in playfield */
1958 for (i = 0; i < MAX_PLAYERS; i++)
1960 struct PlayerInfo *player = &stored_player[i];
1962 if (player->connected && !player->present)
1964 for (j = 0; j < MAX_PLAYERS; j++)
1966 struct PlayerInfo *some_player = &stored_player[j];
1967 int jx = some_player->jx, jy = some_player->jy;
1969 /* assign first free player found that is present in the playfield */
1970 if (some_player->present && !some_player->connected)
1972 player->present = TRUE;
1973 player->active = TRUE;
1975 some_player->present = FALSE;
1976 some_player->active = FALSE;
1979 player->element_nr = some_player->element_nr;
1982 #if USE_NEW_BLOCK_STYLE
1983 player->block_last_field = some_player->block_last_field;
1984 player->block_delay_adjustment = some_player->block_delay_adjustment;
1987 StorePlayer[jx][jy] = player->element_nr;
1988 player->jx = player->last_jx = jx;
1989 player->jy = player->last_jy = jy;
1999 /* when playing a tape, eliminate all players which do not participate */
2001 for (i = 0; i < MAX_PLAYERS; i++)
2003 if (stored_player[i].active && !tape.player_participates[i])
2005 struct PlayerInfo *player = &stored_player[i];
2006 int jx = player->jx, jy = player->jy;
2008 player->active = FALSE;
2009 StorePlayer[jx][jy] = 0;
2010 Feld[jx][jy] = EL_EMPTY;
2014 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2016 /* when in single player mode, eliminate all but the first active player */
2018 for (i = 0; i < MAX_PLAYERS; i++)
2020 if (stored_player[i].active)
2022 for (j = i + 1; j < MAX_PLAYERS; j++)
2024 if (stored_player[j].active)
2026 struct PlayerInfo *player = &stored_player[j];
2027 int jx = player->jx, jy = player->jy;
2029 player->active = FALSE;
2030 player->present = FALSE;
2032 StorePlayer[jx][jy] = 0;
2033 Feld[jx][jy] = EL_EMPTY;
2040 /* when recording the game, store which players take part in the game */
2043 for (i = 0; i < MAX_PLAYERS; i++)
2044 if (stored_player[i].active)
2045 tape.player_participates[i] = TRUE;
2050 for (i = 0; i < MAX_PLAYERS; i++)
2052 struct PlayerInfo *player = &stored_player[i];
2054 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2059 if (local_player == player)
2060 printf("Player %d is local player.\n", i+1);
2064 if (BorderElement == EL_EMPTY)
2067 SBX_Right = lev_fieldx - SCR_FIELDX;
2069 SBY_Lower = lev_fieldy - SCR_FIELDY;
2074 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2076 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2079 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2080 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2082 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2083 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2085 /* if local player not found, look for custom element that might create
2086 the player (make some assumptions about the right custom element) */
2087 if (!local_player->present)
2089 int start_x = 0, start_y = 0;
2090 int found_rating = 0;
2091 int found_element = EL_UNDEFINED;
2093 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2095 int element = Feld[x][y];
2100 if (!IS_CUSTOM_ELEMENT(element))
2103 if (CAN_CHANGE(element))
2105 for (i = 0; i < element_info[element].num_change_pages; i++)
2107 content = element_info[element].change_page[i].target_element;
2108 is_player = ELEM_IS_PLAYER(content);
2110 if (is_player && (found_rating < 3 || element < found_element))
2116 found_element = element;
2121 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2123 content = element_info[element].content[xx][yy];
2124 is_player = ELEM_IS_PLAYER(content);
2126 if (is_player && (found_rating < 2 || element < found_element))
2128 start_x = x + xx - 1;
2129 start_y = y + yy - 1;
2132 found_element = element;
2135 if (!CAN_CHANGE(element))
2138 for (i = 0; i < element_info[element].num_change_pages; i++)
2140 content= element_info[element].change_page[i].target_content[xx][yy];
2141 is_player = ELEM_IS_PLAYER(content);
2143 if (is_player && (found_rating < 1 || element < found_element))
2145 start_x = x + xx - 1;
2146 start_y = y + yy - 1;
2149 found_element = element;
2155 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2156 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2159 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2160 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2166 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2167 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2168 local_player->jx - MIDPOSX);
2170 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2171 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2172 local_player->jy - MIDPOSY);
2174 scroll_x = SBX_Left;
2175 scroll_y = SBY_Upper;
2176 if (local_player->jx >= SBX_Left + MIDPOSX)
2177 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2178 local_player->jx - MIDPOSX :
2180 if (local_player->jy >= SBY_Upper + MIDPOSY)
2181 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2182 local_player->jy - MIDPOSY :
2187 CloseDoor(DOOR_CLOSE_1);
2189 /* !!! FIX THIS (START) !!! */
2190 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2192 InitGameEngine_EM();
2199 /* after drawing the level, correct some elements */
2200 if (game.timegate_time_left == 0)
2201 CloseAllOpenTimegates();
2203 if (setup.soft_scrolling)
2204 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2206 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2209 /* !!! FIX THIS (END) !!! */
2211 /* copy default game door content to main double buffer */
2212 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2213 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2215 DrawGameDoorValues();
2219 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2220 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2221 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2225 /* copy actual game door content to door double buffer for OpenDoor() */
2226 BlitBitmap(drawto, bitmap_db_door,
2227 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2229 OpenDoor(DOOR_OPEN_ALL);
2231 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2233 if (setup.sound_music)
2236 KeyboardAutoRepeatOffUnlessAutoplay();
2240 for (i = 0; i < MAX_PLAYERS; i++)
2241 printf("Player %d %sactive.\n",
2242 i + 1, (stored_player[i].active ? "" : "not "));
2246 printf("::: starting game [%d]\n", FrameCounter);
2250 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2252 /* this is used for non-R'n'D game engines to update certain engine values */
2254 /* needed to determine if sounds are played within the visible screen area */
2255 scroll_x = actual_scroll_x;
2256 scroll_y = actual_scroll_y;
2259 void InitMovDir(int x, int y)
2261 int i, element = Feld[x][y];
2262 static int xy[4][2] =
2269 static int direction[3][4] =
2271 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2272 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2273 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2282 Feld[x][y] = EL_BUG;
2283 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2286 case EL_SPACESHIP_RIGHT:
2287 case EL_SPACESHIP_UP:
2288 case EL_SPACESHIP_LEFT:
2289 case EL_SPACESHIP_DOWN:
2290 Feld[x][y] = EL_SPACESHIP;
2291 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2294 case EL_BD_BUTTERFLY_RIGHT:
2295 case EL_BD_BUTTERFLY_UP:
2296 case EL_BD_BUTTERFLY_LEFT:
2297 case EL_BD_BUTTERFLY_DOWN:
2298 Feld[x][y] = EL_BD_BUTTERFLY;
2299 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2302 case EL_BD_FIREFLY_RIGHT:
2303 case EL_BD_FIREFLY_UP:
2304 case EL_BD_FIREFLY_LEFT:
2305 case EL_BD_FIREFLY_DOWN:
2306 Feld[x][y] = EL_BD_FIREFLY;
2307 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2310 case EL_PACMAN_RIGHT:
2312 case EL_PACMAN_LEFT:
2313 case EL_PACMAN_DOWN:
2314 Feld[x][y] = EL_PACMAN;
2315 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2318 case EL_SP_SNIKSNAK:
2319 MovDir[x][y] = MV_UP;
2322 case EL_SP_ELECTRON:
2323 MovDir[x][y] = MV_LEFT;
2330 Feld[x][y] = EL_MOLE;
2331 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2335 if (IS_CUSTOM_ELEMENT(element))
2337 struct ElementInfo *ei = &element_info[element];
2338 int move_direction_initial = ei->move_direction_initial;
2339 int move_pattern = ei->move_pattern;
2341 if (move_direction_initial == MV_START_PREVIOUS)
2343 if (MovDir[x][y] != MV_NO_MOVING)
2346 move_direction_initial = MV_START_AUTOMATIC;
2349 if (move_direction_initial == MV_START_RANDOM)
2350 MovDir[x][y] = 1 << RND(4);
2351 else if (move_direction_initial & MV_ANY_DIRECTION)
2352 MovDir[x][y] = move_direction_initial;
2353 else if (move_pattern == MV_ALL_DIRECTIONS ||
2354 move_pattern == MV_TURNING_LEFT ||
2355 move_pattern == MV_TURNING_RIGHT ||
2356 move_pattern == MV_TURNING_LEFT_RIGHT ||
2357 move_pattern == MV_TURNING_RIGHT_LEFT ||
2358 move_pattern == MV_TURNING_RANDOM)
2359 MovDir[x][y] = 1 << RND(4);
2360 else if (move_pattern == MV_HORIZONTAL)
2361 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2362 else if (move_pattern == MV_VERTICAL)
2363 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2364 else if (move_pattern & MV_ANY_DIRECTION)
2365 MovDir[x][y] = element_info[element].move_pattern;
2366 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2367 move_pattern == MV_ALONG_RIGHT_SIDE)
2370 /* use random direction as default start direction */
2371 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2372 MovDir[x][y] = 1 << RND(4);
2375 for (i = 0; i < NUM_DIRECTIONS; i++)
2377 int x1 = x + xy[i][0];
2378 int y1 = y + xy[i][1];
2380 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2382 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2383 MovDir[x][y] = direction[0][i];
2385 MovDir[x][y] = direction[1][i];
2394 MovDir[x][y] = 1 << RND(4);
2396 if (element != EL_BUG &&
2397 element != EL_SPACESHIP &&
2398 element != EL_BD_BUTTERFLY &&
2399 element != EL_BD_FIREFLY)
2402 for (i = 0; i < NUM_DIRECTIONS; i++)
2404 int x1 = x + xy[i][0];
2405 int y1 = y + xy[i][1];
2407 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2409 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2411 MovDir[x][y] = direction[0][i];
2414 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2415 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2417 MovDir[x][y] = direction[1][i];
2426 GfxDir[x][y] = MovDir[x][y];
2429 void InitAmoebaNr(int x, int y)
2432 int group_nr = AmoebeNachbarNr(x, y);
2436 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2438 if (AmoebaCnt[i] == 0)
2446 AmoebaNr[x][y] = group_nr;
2447 AmoebaCnt[group_nr]++;
2448 AmoebaCnt2[group_nr]++;
2454 boolean raise_level = FALSE;
2456 if (local_player->MovPos)
2460 if (tape.auto_play) /* tape might already be stopped here */
2461 tape.auto_play_level_solved = TRUE;
2463 if (tape.playing && tape.auto_play)
2464 tape.auto_play_level_solved = TRUE;
2467 local_player->LevelSolved = FALSE;
2469 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2473 if (!tape.playing && setup.sound_loops)
2474 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2475 SND_CTRL_PLAY_LOOP);
2477 while (TimeLeft > 0)
2479 if (!tape.playing && !setup.sound_loops)
2480 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2481 if (TimeLeft > 0 && !(TimeLeft % 10))
2482 RaiseScore(level.score[SC_TIME_BONUS]);
2483 if (TimeLeft > 100 && !(TimeLeft % 10))
2488 DrawGameValue_Time(TimeLeft);
2496 if (!tape.playing && setup.sound_loops)
2497 StopSound(SND_GAME_LEVELTIME_BONUS);
2499 else if (level.time == 0) /* level without time limit */
2501 if (!tape.playing && setup.sound_loops)
2502 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2503 SND_CTRL_PLAY_LOOP);
2505 while (TimePlayed < 999)
2507 if (!tape.playing && !setup.sound_loops)
2508 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2509 if (TimePlayed < 999 && !(TimePlayed % 10))
2510 RaiseScore(level.score[SC_TIME_BONUS]);
2511 if (TimePlayed < 900 && !(TimePlayed % 10))
2516 DrawGameValue_Time(TimePlayed);
2524 if (!tape.playing && setup.sound_loops)
2525 StopSound(SND_GAME_LEVELTIME_BONUS);
2528 /* close exit door after last player */
2529 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2530 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2531 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2533 int element = Feld[ExitX][ExitY];
2535 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2536 EL_SP_EXIT_CLOSING);
2538 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2541 /* Hero disappears */
2542 if (ExitX >= 0 && ExitY >= 0)
2543 DrawLevelField(ExitX, ExitY);
2550 CloseDoor(DOOR_CLOSE_1);
2555 SaveTape(tape.level_nr); /* Ask to save tape */
2558 if (level_nr == leveldir_current->handicap_level)
2560 leveldir_current->handicap_level++;
2561 SaveLevelSetup_SeriesInfo();
2564 if (level_editor_test_game)
2565 local_player->score = -1; /* no highscore when playing from editor */
2566 else if (level_nr < leveldir_current->last_level)
2567 raise_level = TRUE; /* advance to next level */
2569 if ((hi_pos = NewHiScore()) >= 0)
2571 game_status = GAME_MODE_SCORES;
2572 DrawHallOfFame(hi_pos);
2581 game_status = GAME_MODE_MAIN;
2598 LoadScore(level_nr);
2600 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2601 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2604 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2606 if (local_player->score > highscore[k].Score)
2608 /* player has made it to the hall of fame */
2610 if (k < MAX_SCORE_ENTRIES - 1)
2612 int m = MAX_SCORE_ENTRIES - 1;
2615 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2616 if (!strcmp(setup.player_name, highscore[l].Name))
2618 if (m == k) /* player's new highscore overwrites his old one */
2622 for (l = m; l > k; l--)
2624 strcpy(highscore[l].Name, highscore[l - 1].Name);
2625 highscore[l].Score = highscore[l - 1].Score;
2632 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2633 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2634 highscore[k].Score = local_player->score;
2640 else if (!strncmp(setup.player_name, highscore[k].Name,
2641 MAX_PLAYER_NAME_LEN))
2642 break; /* player already there with a higher score */
2648 SaveScore(level_nr);
2653 inline static int getElementMoveStepsize(int x, int y)
2655 int element = Feld[x][y];
2656 int direction = MovDir[x][y];
2657 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2658 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2659 int horiz_move = (dx != 0);
2660 int sign = (horiz_move ? dx : dy);
2661 int step = sign * element_info[element].move_stepsize;
2663 /* special values for move stepsize for spring and things on conveyor belt */
2667 if (element == EL_SPRING)
2668 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2669 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2670 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2671 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2673 if (CAN_FALL(element) &&
2674 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2675 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2676 else if (element == EL_SPRING)
2677 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2684 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2686 if (player->GfxAction != action || player->GfxDir != dir)
2689 printf("Player frame reset! (%d => %d, %d => %d)\n",
2690 player->GfxAction, action, player->GfxDir, dir);
2693 player->GfxAction = action;
2694 player->GfxDir = dir;
2696 player->StepFrame = 0;
2700 static void ResetRandomAnimationValue(int x, int y)
2702 GfxRandom[x][y] = INIT_GFX_RANDOM();
2705 static void ResetGfxAnimation(int x, int y)
2708 GfxAction[x][y] = ACTION_DEFAULT;
2709 GfxDir[x][y] = MovDir[x][y];
2712 void InitMovingField(int x, int y, int direction)
2714 int element = Feld[x][y];
2715 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2716 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2720 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2721 ResetGfxAnimation(x, y);
2723 #if USE_CAN_MOVE_NOT_MOVING
2725 MovDir[x][y] = direction;
2726 GfxDir[x][y] = direction;
2727 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2728 ACTION_FALLING : ACTION_MOVING);
2730 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2732 if (Feld[newx][newy] == EL_EMPTY)
2733 Feld[newx][newy] = EL_BLOCKED;
2735 MovDir[newx][newy] = MovDir[x][y];
2736 GfxFrame[newx][newy] = GfxFrame[x][y];
2737 GfxRandom[newx][newy] = GfxRandom[x][y];
2738 GfxAction[newx][newy] = GfxAction[x][y];
2739 GfxDir[newx][newy] = GfxDir[x][y];
2744 MovDir[newx][newy] = MovDir[x][y] = direction;
2745 GfxDir[x][y] = direction;
2747 if (Feld[newx][newy] == EL_EMPTY)
2748 Feld[newx][newy] = EL_BLOCKED;
2750 if (direction == MV_DOWN && CAN_FALL(element))
2751 GfxAction[x][y] = ACTION_FALLING;
2753 GfxAction[x][y] = ACTION_MOVING;
2755 GfxFrame[newx][newy] = GfxFrame[x][y];
2756 GfxRandom[newx][newy] = GfxRandom[x][y];
2757 GfxAction[newx][newy] = GfxAction[x][y];
2758 GfxDir[newx][newy] = GfxDir[x][y];
2762 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2764 int direction = MovDir[x][y];
2765 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2766 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2772 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2774 int oldx = x, oldy = y;
2775 int direction = MovDir[x][y];
2777 if (direction == MV_LEFT)
2779 else if (direction == MV_RIGHT)
2781 else if (direction == MV_UP)
2783 else if (direction == MV_DOWN)
2786 *comes_from_x = oldx;
2787 *comes_from_y = oldy;
2790 int MovingOrBlocked2Element(int x, int y)
2792 int element = Feld[x][y];
2794 if (element == EL_BLOCKED)
2798 Blocked2Moving(x, y, &oldx, &oldy);
2799 return Feld[oldx][oldy];
2805 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2807 /* like MovingOrBlocked2Element(), but if element is moving
2808 and (x,y) is the field the moving element is just leaving,
2809 return EL_BLOCKED instead of the element value */
2810 int element = Feld[x][y];
2812 if (IS_MOVING(x, y))
2814 if (element == EL_BLOCKED)
2818 Blocked2Moving(x, y, &oldx, &oldy);
2819 return Feld[oldx][oldy];
2828 static void RemoveField(int x, int y)
2830 Feld[x][y] = EL_EMPTY;
2837 ChangeDelay[x][y] = 0;
2838 ChangePage[x][y] = -1;
2839 Pushed[x][y] = FALSE;
2842 ExplodeField[x][y] = EX_TYPE_NONE;
2845 GfxElement[x][y] = EL_UNDEFINED;
2846 GfxAction[x][y] = ACTION_DEFAULT;
2847 GfxDir[x][y] = MV_NO_MOVING;
2850 void RemoveMovingField(int x, int y)
2852 int oldx = x, oldy = y, newx = x, newy = y;
2853 int element = Feld[x][y];
2854 int next_element = EL_UNDEFINED;
2856 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2859 if (IS_MOVING(x, y))
2861 Moving2Blocked(x, y, &newx, &newy);
2863 if (Feld[newx][newy] != EL_BLOCKED)
2866 if (Feld[newx][newy] != EL_BLOCKED)
2868 /* element is moving, but target field is not free (blocked), but
2869 already occupied by something different (example: acid pool);
2870 in this case, only remove the moving field, but not the target */
2872 RemoveField(oldx, oldy);
2874 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2876 DrawLevelField(oldx, oldy);
2882 else if (element == EL_BLOCKED)
2884 Blocked2Moving(x, y, &oldx, &oldy);
2885 if (!IS_MOVING(oldx, oldy))
2889 if (element == EL_BLOCKED &&
2890 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2891 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2892 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2893 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2894 next_element = get_next_element(Feld[oldx][oldy]);
2896 RemoveField(oldx, oldy);
2897 RemoveField(newx, newy);
2899 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2901 if (next_element != EL_UNDEFINED)
2902 Feld[oldx][oldy] = next_element;
2904 DrawLevelField(oldx, oldy);
2905 DrawLevelField(newx, newy);
2908 void DrawDynamite(int x, int y)
2910 int sx = SCREENX(x), sy = SCREENY(y);
2911 int graphic = el2img(Feld[x][y]);
2914 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2917 if (IS_WALKABLE_INSIDE(Back[x][y]))
2921 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2922 else if (Store[x][y])
2923 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2925 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2928 if (Back[x][y] || Store[x][y])
2929 DrawGraphicThruMask(sx, sy, graphic, frame);
2931 DrawGraphic(sx, sy, graphic, frame);
2933 if (game.emulation == EMU_SUPAPLEX)
2934 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2935 else if (Store[x][y])
2936 DrawGraphicThruMask(sx, sy, graphic, frame);
2938 DrawGraphic(sx, sy, graphic, frame);
2942 void CheckDynamite(int x, int y)
2944 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2948 if (MovDelay[x][y] != 0)
2951 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2958 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2960 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2961 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2962 StopSound(SND_DYNAMITE_ACTIVE);
2964 StopSound(SND_DYNABOMB_ACTIVE);
2970 void DrawRelocatePlayer(struct PlayerInfo *player)
2972 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2973 boolean no_delay = (tape.warp_forward);
2974 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2975 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2976 int jx = player->jx;
2977 int jy = player->jy;
2979 if (level.instant_relocation)
2982 int offset = (setup.scroll_delay ? 3 : 0);
2984 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2986 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2987 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2988 local_player->jx - MIDPOSX);
2990 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2991 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2992 local_player->jy - MIDPOSY);
2996 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2997 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2998 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3000 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3001 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3002 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3004 /* don't scroll over playfield boundaries */
3005 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3006 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3008 /* don't scroll over playfield boundaries */
3009 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3010 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3013 scroll_x += (local_player->jx - old_jx);
3014 scroll_y += (local_player->jy - old_jy);
3016 /* don't scroll over playfield boundaries */
3017 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3018 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3020 /* don't scroll over playfield boundaries */
3021 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3022 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3025 RedrawPlayfield(TRUE, 0,0,0,0);
3031 int offset = (setup.scroll_delay ? 3 : 0);
3033 int scroll_xx = -999, scroll_yy = -999;
3035 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3037 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3040 int fx = FX, fy = FY;
3042 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3043 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3044 local_player->jx - MIDPOSX);
3046 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3047 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3048 local_player->jy - MIDPOSY);
3050 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3051 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3054 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3057 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3064 fx += dx * TILEX / 2;
3065 fy += dy * TILEY / 2;
3067 ScrollLevel(dx, dy);
3070 /* scroll in two steps of half tile size to make things smoother */
3071 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3073 Delay(wait_delay_value);
3075 /* scroll second step to align at full tile size */
3077 Delay(wait_delay_value);
3080 int scroll_xx = -999, scroll_yy = -999;
3082 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3084 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3087 int fx = FX, fy = FY;
3089 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3090 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3091 local_player->jx - MIDPOSX);
3093 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3094 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3095 local_player->jy - MIDPOSY);
3097 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3098 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3101 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3104 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3111 fx += dx * TILEX / 2;
3112 fy += dy * TILEY / 2;
3114 ScrollLevel(dx, dy);
3117 /* scroll in two steps of half tile size to make things smoother */
3118 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3120 Delay(wait_delay_value);
3122 /* scroll second step to align at full tile size */
3124 Delay(wait_delay_value);
3130 Delay(wait_delay_value);
3134 void RelocatePlayer(int jx, int jy, int el_player_raw)
3137 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3139 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3141 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3142 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3143 boolean no_delay = (tape.warp_forward);
3144 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3145 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3146 int old_jx = player->jx;
3147 int old_jy = player->jy;
3148 int old_element = Feld[old_jx][old_jy];
3149 int element = Feld[jx][jy];
3150 boolean player_relocated = (old_jx != jx || old_jy != jy);
3152 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3153 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3155 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3156 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3157 int leave_side_horiz = move_dir_horiz;
3158 int leave_side_vert = move_dir_vert;
3160 static int trigger_sides[4][2] =
3162 /* enter side leave side */
3163 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3164 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3165 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3166 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3168 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3169 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3170 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3171 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3173 int enter_side = enter_side_horiz | enter_side_vert;
3174 int leave_side = leave_side_horiz | leave_side_vert;
3176 if (player->GameOver) /* do not reanimate dead player */
3179 if (!player_relocated) /* no need to relocate the player */
3182 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3184 RemoveField(jx, jy); /* temporarily remove newly placed player */
3185 DrawLevelField(jx, jy);
3188 if (player->present)
3190 while (player->MovPos)
3192 ScrollPlayer(player, SCROLL_GO_ON);
3193 ScrollScreen(NULL, SCROLL_GO_ON);
3195 #if USE_NEW_MOVE_DELAY
3196 AdvanceFrameAndPlayerCounters(player->index_nr);
3204 Delay(wait_delay_value);
3207 DrawPlayer(player); /* needed here only to cleanup last field */
3208 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3210 player->is_moving = FALSE;
3214 if (IS_CUSTOM_ELEMENT(old_element))
3215 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3217 player->index_bit, leave_side);
3219 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3221 player->index_bit, leave_side);
3224 Feld[jx][jy] = el_player;
3225 InitPlayerField(jx, jy, el_player, TRUE);
3227 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3229 Feld[jx][jy] = element;
3230 InitField(jx, jy, FALSE);
3234 if (player == local_player) /* only visually relocate local player */
3235 DrawRelocatePlayer(player);
3239 TestIfHeroTouchesBadThing(jx, jy);
3240 TestIfPlayerTouchesCustomElement(jx, jy);
3244 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3249 /* needed to allow change of walkable custom element by entering player */
3250 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3251 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3253 /* needed to allow change of walkable custom element by entering player */
3254 Changed[jx][jy] = 0; /* allow another change */
3259 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3260 enter_side == MV_LEFT ? "left" :
3261 enter_side == MV_RIGHT ? "right" :
3262 enter_side == MV_UP ? "top" :
3263 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3267 if (IS_CUSTOM_ELEMENT(element))
3268 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3269 player->index_bit, enter_side);
3271 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3273 player->index_bit, enter_side);
3277 void Explode(int ex, int ey, int phase, int mode)
3284 /* !!! eliminate this variable !!! */
3285 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3290 int last_phase = num_phase * delay;
3291 int half_phase = (num_phase / 2) * delay;
3292 int first_phase_after_start = EX_PHASE_START + 1;
3296 if (game.explosions_delayed)
3298 ExplodeField[ex][ey] = mode;
3302 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3304 int center_element = Feld[ex][ey];
3307 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3311 /* --- This is only really needed (and now handled) in "Impact()". --- */
3312 /* do not explode moving elements that left the explode field in time */
3313 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3314 center_element == EL_EMPTY &&
3315 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3320 if (mode == EX_TYPE_NORMAL ||
3321 mode == EX_TYPE_CENTER ||
3322 mode == EX_TYPE_CROSS)
3323 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3325 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3326 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3329 /* remove things displayed in background while burning dynamite */
3330 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3335 /* put moving element to center field (and let it explode there) */
3336 center_element = MovingOrBlocked2Element(ex, ey);
3337 RemoveMovingField(ex, ey);
3338 Feld[ex][ey] = center_element;
3344 last_phase = element_info[center_element].explosion_delay + 1;
3346 last_phase = element_info[center_element].explosion_delay;
3350 printf("::: %d -> %d\n", center_element, last_phase);
3354 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3356 int xx = x - ex + 1;
3357 int yy = y - ey + 1;
3362 if (!IN_LEV_FIELD(x, y) ||
3363 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3364 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3367 if (!IN_LEV_FIELD(x, y) ||
3368 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3372 if (!IN_LEV_FIELD(x, y) ||
3373 ((mode != EX_TYPE_NORMAL ||
3374 center_element == EL_AMOEBA_TO_DIAMOND) &&
3375 (x != ex || y != ey)))
3379 element = Feld[x][y];
3381 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3383 element = MovingOrBlocked2Element(x, y);
3385 if (!IS_EXPLOSION_PROOF(element))
3386 RemoveMovingField(x, y);
3392 if (IS_EXPLOSION_PROOF(element))
3395 /* indestructible elements can only explode in center (but not flames) */
3397 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3398 mode == EX_TYPE_BORDER)) ||
3399 element == EL_FLAMES)
3402 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3403 element == EL_FLAMES)
3409 if ((IS_INDESTRUCTIBLE(element) &&
3410 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3411 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3412 element == EL_FLAMES)
3416 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3417 behaviour, for example when touching a yamyam that explodes to rocks
3418 with active deadly shield, a rock is created under the player !!! */
3419 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3421 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3422 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3423 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3425 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3428 if (IS_ACTIVE_BOMB(element))
3430 /* re-activate things under the bomb like gate or penguin */
3432 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3435 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3440 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3441 element_info[Feld[x][y]].token_name,
3442 Store[x][y], Store2[x][y]);
3449 /* save walkable background elements while explosion on same tile */
3451 if (IS_INDESTRUCTIBLE(element))
3452 Back[x][y] = element;
3456 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3457 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3458 Back[x][y] = element;
3460 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3461 (x != ex || y != ey))
3462 Back[x][y] = element;
3465 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3466 Back[x][y] = element;
3470 /* ignite explodable elements reached by other explosion */
3471 if (element == EL_EXPLOSION)
3472 element = Store2[x][y];
3475 if (AmoebaNr[x][y] &&
3476 (element == EL_AMOEBA_FULL ||
3477 element == EL_BD_AMOEBA ||
3478 element == EL_AMOEBA_GROWING))
3480 AmoebaCnt[AmoebaNr[x][y]]--;
3481 AmoebaCnt2[AmoebaNr[x][y]]--;
3487 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3489 switch(StorePlayer[ex][ey])
3492 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3495 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3498 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3502 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3507 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3508 Store[x][y] = EL_EMPTY;
3510 if (game.emulation == EMU_SUPAPLEX)
3511 Store[x][y] = EL_EMPTY;
3514 else if (center_element == EL_MOLE)
3515 Store[x][y] = EL_EMERALD_RED;
3516 else if (center_element == EL_PENGUIN)
3517 Store[x][y] = EL_EMERALD_PURPLE;
3518 else if (center_element == EL_BUG)
3519 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3520 else if (center_element == EL_BD_BUTTERFLY)
3521 Store[x][y] = EL_BD_DIAMOND;
3522 else if (center_element == EL_SP_ELECTRON)
3523 Store[x][y] = EL_SP_INFOTRON;
3524 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3525 Store[x][y] = level.amoeba_content;
3526 else if (center_element == EL_YAMYAM)
3527 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3528 else if (IS_CUSTOM_ELEMENT(center_element) &&
3529 element_info[center_element].content[xx][yy] != EL_EMPTY)
3530 Store[x][y] = element_info[center_element].content[xx][yy];
3531 else if (element == EL_WALL_EMERALD)
3532 Store[x][y] = EL_EMERALD;
3533 else if (element == EL_WALL_DIAMOND)
3534 Store[x][y] = EL_DIAMOND;
3535 else if (element == EL_WALL_BD_DIAMOND)
3536 Store[x][y] = EL_BD_DIAMOND;
3537 else if (element == EL_WALL_EMERALD_YELLOW)
3538 Store[x][y] = EL_EMERALD_YELLOW;
3539 else if (element == EL_WALL_EMERALD_RED)
3540 Store[x][y] = EL_EMERALD_RED;
3541 else if (element == EL_WALL_EMERALD_PURPLE)
3542 Store[x][y] = EL_EMERALD_PURPLE;
3543 else if (element == EL_WALL_PEARL)
3544 Store[x][y] = EL_PEARL;
3545 else if (element == EL_WALL_CRYSTAL)
3546 Store[x][y] = EL_CRYSTAL;
3547 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3548 Store[x][y] = element_info[element].content[1][1];
3550 Store[x][y] = EL_EMPTY;
3552 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3553 center_element == EL_AMOEBA_TO_DIAMOND)
3554 Store2[x][y] = element;
3557 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3558 element_info[Store2[x][y]].token_name);
3562 if (AmoebaNr[x][y] &&
3563 (element == EL_AMOEBA_FULL ||
3564 element == EL_BD_AMOEBA ||
3565 element == EL_AMOEBA_GROWING))
3567 AmoebaCnt[AmoebaNr[x][y]]--;
3568 AmoebaCnt2[AmoebaNr[x][y]]--;
3574 MovDir[x][y] = MovPos[x][y] = 0;
3575 GfxDir[x][y] = MovDir[x][y];
3580 Feld[x][y] = EL_EXPLOSION;
3582 GfxElement[x][y] = center_element;
3584 GfxElement[x][y] = EL_UNDEFINED;
3587 ExplodePhase[x][y] = 1;
3589 ExplodeDelay[x][y] = last_phase;
3594 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3596 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3603 if (center_element == EL_YAMYAM)
3604 game.yamyam_content_nr =
3605 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3608 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3609 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3623 GfxFrame[x][y] = 0; /* restart explosion animation */
3627 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3631 last_phase = ExplodeDelay[x][y];
3634 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3638 /* activate this even in non-DEBUG version until cause for crash in
3639 getGraphicAnimationFrame() (see below) is found and eliminated */
3643 if (GfxElement[x][y] == EL_UNDEFINED)
3646 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3647 printf("Explode(): This should never happen!\n");
3650 GfxElement[x][y] = EL_EMPTY;
3656 border_element = Store2[x][y];
3658 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3659 border_element = StorePlayer[x][y];
3661 if (IS_PLAYER(x, y))
3662 border_element = StorePlayer[x][y];
3666 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3667 element_info[border_element].token_name, Store2[x][y]);
3671 printf("::: phase == %d\n", phase);
3674 if (phase == element_info[border_element].ignition_delay ||
3675 phase == last_phase)
3677 boolean border_explosion = FALSE;
3681 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3682 !PLAYER_EXPLOSION_PROTECTED(x, y))
3684 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3687 if (IS_PLAYER(x, y))
3690 KillHeroUnlessExplosionProtected(x, y);
3691 border_explosion = TRUE;
3694 if (phase == last_phase)
3695 printf("::: IS_PLAYER\n");
3698 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3701 printf("::: %d,%d: %d %s\n", x, y, border_element,
3702 element_info[border_element].token_name);
3705 Feld[x][y] = Store2[x][y];
3708 border_explosion = TRUE;
3711 if (phase == last_phase)
3712 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3715 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3717 AmoebeUmwandeln(x, y);
3719 border_explosion = TRUE;
3722 if (phase == last_phase)
3723 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3724 element_info[border_element].explosion_delay,
3725 element_info[border_element].ignition_delay,
3731 /* if an element just explodes due to another explosion (chain-reaction),
3732 do not immediately end the new explosion when it was the last frame of
3733 the explosion (as it would be done in the following "if"-statement!) */
3734 if (border_explosion && phase == last_phase)
3741 if (phase == first_phase_after_start)
3743 int element = Store2[x][y];
3745 if (element == EL_BLACK_ORB)
3747 Feld[x][y] = Store2[x][y];
3752 else if (phase == half_phase)
3754 int element = Store2[x][y];
3756 if (IS_PLAYER(x, y))
3757 KillHeroUnlessExplosionProtected(x, y);
3758 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3760 Feld[x][y] = Store2[x][y];
3764 else if (element == EL_AMOEBA_TO_DIAMOND)
3765 AmoebeUmwandeln(x, y);
3769 if (phase == last_phase)
3774 printf("::: done: phase == %d\n", phase);
3778 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3781 element = Feld[x][y] = Store[x][y];
3782 Store[x][y] = Store2[x][y] = 0;
3783 GfxElement[x][y] = EL_UNDEFINED;
3785 /* player can escape from explosions and might therefore be still alive */
3786 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3787 element <= EL_PLAYER_IS_EXPLODING_4)
3788 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3790 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3791 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3792 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3795 /* restore probably existing indestructible background element */
3796 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3797 element = Feld[x][y] = Back[x][y];
3800 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3801 GfxDir[x][y] = MV_NO_MOVING;
3802 ChangeDelay[x][y] = 0;
3803 ChangePage[x][y] = -1;
3806 InitField_WithBug2(x, y, FALSE);
3808 InitField(x, y, FALSE);
3810 /* !!! not needed !!! */
3812 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3813 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3816 if (CAN_MOVE(element))
3821 DrawLevelField(x, y);
3823 TestIfElementTouchesCustomElement(x, y);
3825 if (GFX_CRUMBLED(element))
3826 DrawLevelFieldCrumbledSandNeighbours(x, y);
3828 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3829 StorePlayer[x][y] = 0;
3831 if (ELEM_IS_PLAYER(element))
3832 RelocatePlayer(x, y, element);
3835 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3837 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3841 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3843 int stored = Store[x][y];
3844 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3845 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3849 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3851 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3855 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3859 printf("::: %d / %d [%d - %d]\n",
3860 GfxFrame[x][y], phase - delay, phase, delay);
3864 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3865 element_info[GfxElement[x][y]].token_name,
3870 DrawLevelFieldCrumbledSand(x, y);
3872 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3874 DrawLevelElement(x, y, Back[x][y]);
3875 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3877 else if (IS_WALKABLE_UNDER(Back[x][y]))
3879 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3880 DrawLevelElementThruMask(x, y, Back[x][y]);
3882 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3883 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3887 void DynaExplode(int ex, int ey)
3890 int dynabomb_element = Feld[ex][ey];
3891 int dynabomb_size = 1;
3892 boolean dynabomb_xl = FALSE;
3893 struct PlayerInfo *player;
3894 static int xy[4][2] =
3902 if (IS_ACTIVE_BOMB(dynabomb_element))
3904 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3905 dynabomb_size = player->dynabomb_size;
3906 dynabomb_xl = player->dynabomb_xl;
3907 player->dynabombs_left++;
3910 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3912 for (i = 0; i < NUM_DIRECTIONS; i++)
3914 for (j = 1; j <= dynabomb_size; j++)
3916 int x = ex + j * xy[i][0];
3917 int y = ey + j * xy[i][1];
3920 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3923 element = Feld[x][y];
3925 /* do not restart explosions of fields with active bombs */
3926 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3929 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3933 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3934 !IS_DIGGABLE(element) && !dynabomb_xl)
3937 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3938 !CAN_GROW_INTO(element) && !dynabomb_xl)
3942 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3943 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3944 element != EL_SAND && !dynabomb_xl)
3951 void Bang(int x, int y)
3954 int element = MovingOrBlocked2Element(x, y);
3956 int element = Feld[x][y];
3960 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3962 if (IS_PLAYER(x, y))
3965 struct PlayerInfo *player = PLAYERINFO(x, y);
3967 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3968 player->element_nr);
3973 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3975 if (game.emulation == EMU_SUPAPLEX)
3976 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3978 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3983 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3991 case EL_BD_BUTTERFLY:
3994 case EL_DARK_YAMYAM:
3998 RaiseScoreElement(element);
3999 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4001 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4002 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4003 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4004 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4005 case EL_DYNABOMB_INCREASE_NUMBER:
4006 case EL_DYNABOMB_INCREASE_SIZE:
4007 case EL_DYNABOMB_INCREASE_POWER:
4012 case EL_LAMP_ACTIVE:
4014 case EL_AMOEBA_TO_DIAMOND:
4016 if (IS_PLAYER(x, y))
4017 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4019 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4023 if (element_info[element].explosion_type == EXPLODES_CROSS)
4025 if (CAN_EXPLODE_CROSS(element))
4028 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4033 else if (element_info[element].explosion_type == EXPLODES_1X1)
4035 else if (CAN_EXPLODE_1X1(element))
4037 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4039 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4043 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4046 void SplashAcid(int x, int y)
4049 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4050 (!IN_LEV_FIELD(x - 1, y - 2) ||
4051 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4052 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4054 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4055 (!IN_LEV_FIELD(x + 1, y - 2) ||
4056 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4057 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4059 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4061 /* input: position of element entering acid (obsolete) */
4063 int element = Feld[x][y];
4065 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4068 if (element != EL_ACID_SPLASH_LEFT &&
4069 element != EL_ACID_SPLASH_RIGHT)
4071 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4073 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4074 (!IN_LEV_FIELD(x - 1, y - 1) ||
4075 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4076 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4078 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4079 (!IN_LEV_FIELD(x + 1, y - 1) ||
4080 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4081 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4086 static void InitBeltMovement()
4088 static int belt_base_element[4] =
4090 EL_CONVEYOR_BELT_1_LEFT,
4091 EL_CONVEYOR_BELT_2_LEFT,
4092 EL_CONVEYOR_BELT_3_LEFT,
4093 EL_CONVEYOR_BELT_4_LEFT
4095 static int belt_base_active_element[4] =
4097 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4098 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4099 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4100 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4105 /* set frame order for belt animation graphic according to belt direction */
4106 for (i = 0; i < NUM_BELTS; i++)
4110 for (j = 0; j < NUM_BELT_PARTS; j++)
4112 int element = belt_base_active_element[belt_nr] + j;
4113 int graphic = el2img(element);
4115 if (game.belt_dir[i] == MV_LEFT)
4116 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4118 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4122 for (y = 0; y < lev_fieldy; y++)
4124 for (x = 0; x < lev_fieldx; x++)
4126 int element = Feld[x][y];
4128 for (i = 0; i < NUM_BELTS; i++)
4130 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4132 int e_belt_nr = getBeltNrFromBeltElement(element);
4135 if (e_belt_nr == belt_nr)
4137 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4139 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4147 static void ToggleBeltSwitch(int x, int y)
4149 static int belt_base_element[4] =
4151 EL_CONVEYOR_BELT_1_LEFT,
4152 EL_CONVEYOR_BELT_2_LEFT,
4153 EL_CONVEYOR_BELT_3_LEFT,
4154 EL_CONVEYOR_BELT_4_LEFT
4156 static int belt_base_active_element[4] =
4158 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4159 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4160 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4161 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4163 static int belt_base_switch_element[4] =
4165 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4166 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4167 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4168 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4170 static int belt_move_dir[4] =
4178 int element = Feld[x][y];
4179 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4180 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4181 int belt_dir = belt_move_dir[belt_dir_nr];
4184 if (!IS_BELT_SWITCH(element))
4187 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4188 game.belt_dir[belt_nr] = belt_dir;
4190 if (belt_dir_nr == 3)
4193 /* set frame order for belt animation graphic according to belt direction */
4194 for (i = 0; i < NUM_BELT_PARTS; i++)
4196 int element = belt_base_active_element[belt_nr] + i;
4197 int graphic = el2img(element);
4199 if (belt_dir == MV_LEFT)
4200 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4202 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4205 for (yy = 0; yy < lev_fieldy; yy++)
4207 for (xx = 0; xx < lev_fieldx; xx++)
4209 int element = Feld[xx][yy];
4211 if (IS_BELT_SWITCH(element))
4213 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4215 if (e_belt_nr == belt_nr)
4217 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4218 DrawLevelField(xx, yy);
4221 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4223 int e_belt_nr = getBeltNrFromBeltElement(element);
4225 if (e_belt_nr == belt_nr)
4227 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4229 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4230 DrawLevelField(xx, yy);
4233 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4235 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4237 if (e_belt_nr == belt_nr)
4239 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4241 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4242 DrawLevelField(xx, yy);
4249 static void ToggleSwitchgateSwitch(int x, int y)
4253 game.switchgate_pos = !game.switchgate_pos;
4255 for (yy = 0; yy < lev_fieldy; yy++)
4257 for (xx = 0; xx < lev_fieldx; xx++)
4259 int element = Feld[xx][yy];
4261 if (element == EL_SWITCHGATE_SWITCH_UP ||
4262 element == EL_SWITCHGATE_SWITCH_DOWN)
4264 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4265 DrawLevelField(xx, yy);
4267 else if (element == EL_SWITCHGATE_OPEN ||
4268 element == EL_SWITCHGATE_OPENING)
4270 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4272 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4274 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4277 else if (element == EL_SWITCHGATE_CLOSED ||
4278 element == EL_SWITCHGATE_CLOSING)
4280 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4282 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4284 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4291 static int getInvisibleActiveFromInvisibleElement(int element)
4293 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4294 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4295 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4299 static int getInvisibleFromInvisibleActiveElement(int element)
4301 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4302 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4303 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4307 static void RedrawAllLightSwitchesAndInvisibleElements()
4311 for (y = 0; y < lev_fieldy; y++)
4313 for (x = 0; x < lev_fieldx; x++)
4315 int element = Feld[x][y];
4317 if (element == EL_LIGHT_SWITCH &&
4318 game.light_time_left > 0)
4320 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4321 DrawLevelField(x, y);
4323 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4324 game.light_time_left == 0)
4326 Feld[x][y] = EL_LIGHT_SWITCH;
4327 DrawLevelField(x, y);
4329 else if (element == EL_INVISIBLE_STEELWALL ||
4330 element == EL_INVISIBLE_WALL ||
4331 element == EL_INVISIBLE_SAND)
4333 if (game.light_time_left > 0)
4334 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4336 DrawLevelField(x, y);
4338 /* uncrumble neighbour fields, if needed */
4339 if (element == EL_INVISIBLE_SAND)
4340 DrawLevelFieldCrumbledSandNeighbours(x, y);
4342 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4343 element == EL_INVISIBLE_WALL_ACTIVE ||
4344 element == EL_INVISIBLE_SAND_ACTIVE)
4346 if (game.light_time_left == 0)
4347 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4349 DrawLevelField(x, y);
4351 /* re-crumble neighbour fields, if needed */
4352 if (element == EL_INVISIBLE_SAND)
4353 DrawLevelFieldCrumbledSandNeighbours(x, y);
4359 static void ToggleLightSwitch(int x, int y)
4361 int element = Feld[x][y];
4363 game.light_time_left =
4364 (element == EL_LIGHT_SWITCH ?
4365 level.time_light * FRAMES_PER_SECOND : 0);
4367 RedrawAllLightSwitchesAndInvisibleElements();
4370 static void ActivateTimegateSwitch(int x, int y)
4374 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4376 for (yy = 0; yy < lev_fieldy; yy++)
4378 for (xx = 0; xx < lev_fieldx; xx++)
4380 int element = Feld[xx][yy];
4382 if (element == EL_TIMEGATE_CLOSED ||
4383 element == EL_TIMEGATE_CLOSING)
4385 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4386 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4390 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4392 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4393 DrawLevelField(xx, yy);
4400 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4403 void Impact(int x, int y)
4405 boolean lastline = (y == lev_fieldy-1);
4406 boolean object_hit = FALSE;
4407 boolean impact = (lastline || object_hit);
4408 int element = Feld[x][y];
4409 int smashed = EL_STEELWALL;
4412 printf("IMPACT!\n");
4415 if (!lastline) /* check if element below was hit */
4417 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4420 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4421 MovDir[x][y + 1] != MV_DOWN ||
4422 MovPos[x][y + 1] <= TILEY / 2));
4425 object_hit = !IS_FREE(x, y + 1);
4428 /* do not smash moving elements that left the smashed field in time */
4429 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4430 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4434 smashed = MovingOrBlocked2Element(x, y + 1);
4436 impact = (lastline || object_hit);
4439 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4441 SplashAcid(x, y + 1);
4445 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4446 /* only reset graphic animation if graphic really changes after impact */
4448 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4450 ResetGfxAnimation(x, y);
4451 DrawLevelField(x, y);
4454 if (impact && CAN_EXPLODE_IMPACT(element))
4459 else if (impact && element == EL_PEARL)
4461 ResetGfxAnimation(x, y);
4463 Feld[x][y] = EL_PEARL_BREAKING;
4464 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4467 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4469 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4474 if (impact && element == EL_AMOEBA_DROP)
4476 if (object_hit && IS_PLAYER(x, y + 1))
4477 KillHeroUnlessEnemyProtected(x, y + 1);
4478 else if (object_hit && smashed == EL_PENGUIN)
4482 Feld[x][y] = EL_AMOEBA_GROWING;
4483 Store[x][y] = EL_AMOEBA_WET;
4485 ResetRandomAnimationValue(x, y);
4490 if (object_hit) /* check which object was hit */
4492 if (CAN_PASS_MAGIC_WALL(element) &&
4493 (smashed == EL_MAGIC_WALL ||
4494 smashed == EL_BD_MAGIC_WALL))
4497 int activated_magic_wall =
4498 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4499 EL_BD_MAGIC_WALL_ACTIVE);
4501 /* activate magic wall / mill */
4502 for (yy = 0; yy < lev_fieldy; yy++)
4503 for (xx = 0; xx < lev_fieldx; xx++)
4504 if (Feld[xx][yy] == smashed)
4505 Feld[xx][yy] = activated_magic_wall;
4507 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4508 game.magic_wall_active = TRUE;
4510 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4511 SND_MAGIC_WALL_ACTIVATING :
4512 SND_BD_MAGIC_WALL_ACTIVATING));
4515 if (IS_PLAYER(x, y + 1))
4517 if (CAN_SMASH_PLAYER(element))
4519 KillHeroUnlessEnemyProtected(x, y + 1);
4523 else if (smashed == EL_PENGUIN)
4525 if (CAN_SMASH_PLAYER(element))
4531 else if (element == EL_BD_DIAMOND)
4533 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4539 else if (((element == EL_SP_INFOTRON ||
4540 element == EL_SP_ZONK) &&
4541 (smashed == EL_SP_SNIKSNAK ||
4542 smashed == EL_SP_ELECTRON ||
4543 smashed == EL_SP_DISK_ORANGE)) ||
4544 (element == EL_SP_INFOTRON &&
4545 smashed == EL_SP_DISK_YELLOW))
4551 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4557 else if (CAN_SMASH_EVERYTHING(element))
4559 if (IS_CLASSIC_ENEMY(smashed) ||
4560 CAN_EXPLODE_SMASHED(smashed))
4565 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4567 if (smashed == EL_LAMP ||
4568 smashed == EL_LAMP_ACTIVE)
4573 else if (smashed == EL_NUT)
4575 Feld[x][y + 1] = EL_NUT_BREAKING;
4576 PlayLevelSound(x, y, SND_NUT_BREAKING);
4577 RaiseScoreElement(EL_NUT);
4580 else if (smashed == EL_PEARL)
4582 ResetGfxAnimation(x, y);
4584 Feld[x][y + 1] = EL_PEARL_BREAKING;
4585 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4588 else if (smashed == EL_DIAMOND)
4590 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4591 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4594 else if (IS_BELT_SWITCH(smashed))
4596 ToggleBeltSwitch(x, y + 1);
4598 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4599 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4601 ToggleSwitchgateSwitch(x, y + 1);
4603 else if (smashed == EL_LIGHT_SWITCH ||
4604 smashed == EL_LIGHT_SWITCH_ACTIVE)
4606 ToggleLightSwitch(x, y + 1);
4611 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4614 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4617 /* !!! TEST ONLY !!! */
4618 CheckElementChangeBySide(x, y + 1, smashed, element,
4619 CE_SWITCHED, CH_SIDE_TOP);
4620 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4621 CE_SWITCH_OF_X, CH_SIDE_TOP);
4623 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4624 CE_SWITCH_OF_X, CH_SIDE_TOP);
4625 CheckElementChangeBySide(x, y + 1, smashed, element,
4626 CE_SWITCHED, CH_SIDE_TOP);
4632 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4637 /* play sound of magic wall / mill */
4639 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4640 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4642 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4643 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4644 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4645 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4650 /* play sound of object that hits the ground */
4651 if (lastline || object_hit)
4652 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4655 inline static void TurnRoundExt(int x, int y)
4667 { 0, 0 }, { 0, 0 }, { 0, 0 },
4672 int left, right, back;
4676 { MV_DOWN, MV_UP, MV_RIGHT },
4677 { MV_UP, MV_DOWN, MV_LEFT },
4679 { MV_LEFT, MV_RIGHT, MV_DOWN },
4683 { MV_RIGHT, MV_LEFT, MV_UP }
4686 int element = Feld[x][y];
4687 int move_pattern = element_info[element].move_pattern;
4689 int old_move_dir = MovDir[x][y];
4690 int left_dir = turn[old_move_dir].left;
4691 int right_dir = turn[old_move_dir].right;
4692 int back_dir = turn[old_move_dir].back;
4694 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4695 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4696 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4697 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4699 int left_x = x + left_dx, left_y = y + left_dy;
4700 int right_x = x + right_dx, right_y = y + right_dy;
4701 int move_x = x + move_dx, move_y = y + move_dy;
4705 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4707 TestIfBadThingTouchesOtherBadThing(x, y);
4709 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4710 MovDir[x][y] = right_dir;
4711 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4712 MovDir[x][y] = left_dir;
4714 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4716 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4720 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4721 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4723 TestIfBadThingTouchesOtherBadThing(x, y);
4725 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4726 MovDir[x][y] = left_dir;
4727 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4728 MovDir[x][y] = right_dir;
4730 if ((element == EL_SPACESHIP ||
4731 element == EL_SP_SNIKSNAK ||
4732 element == EL_SP_ELECTRON)
4733 && MovDir[x][y] != old_move_dir)
4735 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4739 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4741 TestIfBadThingTouchesOtherBadThing(x, y);
4743 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4744 MovDir[x][y] = left_dir;
4745 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4746 MovDir[x][y] = right_dir;
4748 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4750 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4753 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4755 TestIfBadThingTouchesOtherBadThing(x, y);
4757 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4758 MovDir[x][y] = left_dir;
4759 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4760 MovDir[x][y] = right_dir;
4762 if (MovDir[x][y] != old_move_dir)
4766 else if (element == EL_YAMYAM)
4768 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4769 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4771 if (can_turn_left && can_turn_right)
4772 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4773 else if (can_turn_left)
4774 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4775 else if (can_turn_right)
4776 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4778 MovDir[x][y] = back_dir;
4780 MovDelay[x][y] = 16 + 16 * RND(3);
4782 else if (element == EL_DARK_YAMYAM)
4784 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4786 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4789 if (can_turn_left && can_turn_right)
4790 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4791 else if (can_turn_left)
4792 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4793 else if (can_turn_right)
4794 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4796 MovDir[x][y] = back_dir;
4798 MovDelay[x][y] = 16 + 16 * RND(3);
4800 else if (element == EL_PACMAN)
4802 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4803 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4805 if (can_turn_left && can_turn_right)
4806 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4807 else if (can_turn_left)
4808 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4809 else if (can_turn_right)
4810 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4812 MovDir[x][y] = back_dir;
4814 MovDelay[x][y] = 6 + RND(40);
4816 else if (element == EL_PIG)
4818 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4819 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4820 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4821 boolean should_turn_left, should_turn_right, should_move_on;
4823 int rnd = RND(rnd_value);
4825 should_turn_left = (can_turn_left &&
4827 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4828 y + back_dy + left_dy)));
4829 should_turn_right = (can_turn_right &&
4831 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4832 y + back_dy + right_dy)));
4833 should_move_on = (can_move_on &&
4836 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4837 y + move_dy + left_dy) ||
4838 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4839 y + move_dy + right_dy)));
4841 if (should_turn_left || should_turn_right || should_move_on)
4843 if (should_turn_left && should_turn_right && should_move_on)
4844 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4845 rnd < 2 * rnd_value / 3 ? right_dir :
4847 else if (should_turn_left && should_turn_right)
4848 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4849 else if (should_turn_left && should_move_on)
4850 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4851 else if (should_turn_right && should_move_on)
4852 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4853 else if (should_turn_left)
4854 MovDir[x][y] = left_dir;
4855 else if (should_turn_right)
4856 MovDir[x][y] = right_dir;
4857 else if (should_move_on)
4858 MovDir[x][y] = old_move_dir;
4860 else if (can_move_on && rnd > rnd_value / 8)
4861 MovDir[x][y] = old_move_dir;
4862 else if (can_turn_left && can_turn_right)
4863 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4864 else if (can_turn_left && rnd > rnd_value / 8)
4865 MovDir[x][y] = left_dir;
4866 else if (can_turn_right && rnd > rnd_value/8)
4867 MovDir[x][y] = right_dir;
4869 MovDir[x][y] = back_dir;
4871 xx = x + move_xy[MovDir[x][y]].x;
4872 yy = y + move_xy[MovDir[x][y]].y;
4875 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4876 if (!IN_LEV_FIELD(xx, yy) ||
4877 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4878 MovDir[x][y] = old_move_dir;
4880 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4881 MovDir[x][y] = old_move_dir;
4886 else if (element == EL_DRAGON)
4888 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4889 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4890 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4892 int rnd = RND(rnd_value);
4895 if (FrameCounter < 1 && x == 0 && y == 29)
4896 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4899 if (can_move_on && rnd > rnd_value / 8)
4900 MovDir[x][y] = old_move_dir;
4901 else if (can_turn_left && can_turn_right)
4902 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4903 else if (can_turn_left && rnd > rnd_value / 8)
4904 MovDir[x][y] = left_dir;
4905 else if (can_turn_right && rnd > rnd_value / 8)
4906 MovDir[x][y] = right_dir;
4908 MovDir[x][y] = back_dir;
4910 xx = x + move_xy[MovDir[x][y]].x;
4911 yy = y + move_xy[MovDir[x][y]].y;
4914 if (FrameCounter < 1 && x == 0 && y == 29)
4915 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4916 xx, yy, Feld[xx][yy],
4921 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4922 MovDir[x][y] = old_move_dir;
4924 if (!IS_FREE(xx, yy))
4925 MovDir[x][y] = old_move_dir;
4929 if (FrameCounter < 1 && x == 0 && y == 29)
4930 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4935 else if (element == EL_MOLE)
4937 boolean can_move_on =
4938 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4939 IS_AMOEBOID(Feld[move_x][move_y]) ||
4940 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4943 boolean can_turn_left =
4944 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4945 IS_AMOEBOID(Feld[left_x][left_y])));
4947 boolean can_turn_right =
4948 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4949 IS_AMOEBOID(Feld[right_x][right_y])));
4951 if (can_turn_left && can_turn_right)
4952 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4953 else if (can_turn_left)
4954 MovDir[x][y] = left_dir;
4956 MovDir[x][y] = right_dir;
4959 if (MovDir[x][y] != old_move_dir)
4962 else if (element == EL_BALLOON)
4964 MovDir[x][y] = game.balloon_dir;
4967 else if (element == EL_SPRING)
4970 if (MovDir[x][y] & MV_HORIZONTAL &&
4971 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4972 MovDir[x][y] = MV_NO_MOVING;
4974 if (MovDir[x][y] & MV_HORIZONTAL &&
4975 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4976 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4977 MovDir[x][y] = MV_NO_MOVING;
4982 else if (element == EL_ROBOT ||
4983 element == EL_SATELLITE ||
4984 element == EL_PENGUIN)
4986 int attr_x = -1, attr_y = -1;
4997 for (i = 0; i < MAX_PLAYERS; i++)
4999 struct PlayerInfo *player = &stored_player[i];
5000 int jx = player->jx, jy = player->jy;
5002 if (!player->active)
5006 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5015 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5016 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5017 game.engine_version < VERSION_IDENT(3,1,0,0)))
5019 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5026 if (element == EL_PENGUIN)
5029 static int xy[4][2] =
5037 for (i = 0; i < NUM_DIRECTIONS; i++)
5039 int ex = x + xy[i][0];
5040 int ey = y + xy[i][1];
5042 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5051 MovDir[x][y] = MV_NO_MOVING;
5053 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5054 else if (attr_x > x)
5055 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5057 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5058 else if (attr_y > y)
5059 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5061 if (element == EL_ROBOT)
5065 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5066 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5067 Moving2Blocked(x, y, &newx, &newy);
5069 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5070 MovDelay[x][y] = 8 + 8 * !RND(3);
5072 MovDelay[x][y] = 16;
5074 else if (element == EL_PENGUIN)
5080 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5082 boolean first_horiz = RND(2);
5083 int new_move_dir = MovDir[x][y];
5086 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5087 Moving2Blocked(x, y, &newx, &newy);
5089 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5093 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5094 Moving2Blocked(x, y, &newx, &newy);
5096 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5099 MovDir[x][y] = old_move_dir;
5103 else /* (element == EL_SATELLITE) */
5109 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5111 boolean first_horiz = RND(2);
5112 int new_move_dir = MovDir[x][y];
5115 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5116 Moving2Blocked(x, y, &newx, &newy);
5118 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5122 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5123 Moving2Blocked(x, y, &newx, &newy);
5125 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5128 MovDir[x][y] = old_move_dir;
5133 else if (move_pattern == MV_TURNING_LEFT ||
5134 move_pattern == MV_TURNING_RIGHT ||
5135 move_pattern == MV_TURNING_LEFT_RIGHT ||
5136 move_pattern == MV_TURNING_RIGHT_LEFT ||
5137 move_pattern == MV_TURNING_RANDOM ||
5138 move_pattern == MV_ALL_DIRECTIONS)
5140 boolean can_turn_left =
5141 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5142 boolean can_turn_right =
5143 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5145 #if USE_CAN_MOVE_NOT_MOVING
5146 if (element_info[element].move_stepsize == 0) /* not moving */
5150 if (move_pattern == MV_TURNING_LEFT)
5151 MovDir[x][y] = left_dir;
5152 else if (move_pattern == MV_TURNING_RIGHT)
5153 MovDir[x][y] = right_dir;
5154 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5155 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5156 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5157 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5158 else if (move_pattern == MV_TURNING_RANDOM)
5159 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5160 can_turn_right && !can_turn_left ? right_dir :
5161 RND(2) ? left_dir : right_dir);
5162 else if (can_turn_left && can_turn_right)
5163 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5164 else if (can_turn_left)
5165 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5166 else if (can_turn_right)
5167 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5169 MovDir[x][y] = back_dir;
5171 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5173 else if (move_pattern == MV_HORIZONTAL ||
5174 move_pattern == MV_VERTICAL)
5176 if (move_pattern & old_move_dir)
5177 MovDir[x][y] = back_dir;
5178 else if (move_pattern == MV_HORIZONTAL)
5179 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5180 else if (move_pattern == MV_VERTICAL)
5181 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5183 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5185 else if (move_pattern & MV_ANY_DIRECTION)
5187 MovDir[x][y] = move_pattern;
5188 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5190 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5192 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5193 MovDir[x][y] = left_dir;
5194 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5195 MovDir[x][y] = right_dir;
5197 if (MovDir[x][y] != old_move_dir)
5198 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5200 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5202 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5203 MovDir[x][y] = right_dir;
5204 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5205 MovDir[x][y] = left_dir;
5207 if (MovDir[x][y] != old_move_dir)
5208 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5210 else if (move_pattern == MV_TOWARDS_PLAYER ||
5211 move_pattern == MV_AWAY_FROM_PLAYER)
5213 int attr_x = -1, attr_y = -1;
5215 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5226 for (i = 0; i < MAX_PLAYERS; i++)
5228 struct PlayerInfo *player = &stored_player[i];
5229 int jx = player->jx, jy = player->jy;
5231 if (!player->active)
5235 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5243 MovDir[x][y] = MV_NO_MOVING;
5245 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5246 else if (attr_x > x)
5247 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5249 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5250 else if (attr_y > y)
5251 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5253 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5255 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5257 boolean first_horiz = RND(2);
5258 int new_move_dir = MovDir[x][y];
5260 #if USE_CAN_MOVE_NOT_MOVING
5261 if (element_info[element].move_stepsize == 0) /* not moving */
5263 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5264 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5271 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5272 Moving2Blocked(x, y, &newx, &newy);
5274 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5278 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5279 Moving2Blocked(x, y, &newx, &newy);
5281 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5284 MovDir[x][y] = old_move_dir;
5287 else if (move_pattern == MV_WHEN_PUSHED ||
5288 move_pattern == MV_WHEN_DROPPED)
5290 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5291 MovDir[x][y] = MV_NO_MOVING;
5295 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5297 static int test_xy[7][2] =
5307 static int test_dir[7] =
5317 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5318 int move_preference = -1000000; /* start with very low preference */
5319 int new_move_dir = MV_NO_MOVING;
5320 int start_test = RND(4);
5323 for (i = 0; i < NUM_DIRECTIONS; i++)
5325 int move_dir = test_dir[start_test + i];
5326 int move_dir_preference;
5328 xx = x + test_xy[start_test + i][0];
5329 yy = y + test_xy[start_test + i][1];
5331 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5332 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5334 new_move_dir = move_dir;
5339 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5342 move_dir_preference = -1 * RunnerVisit[xx][yy];
5343 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5344 move_dir_preference = PlayerVisit[xx][yy];
5346 if (move_dir_preference > move_preference)
5348 /* prefer field that has not been visited for the longest time */
5349 move_preference = move_dir_preference;
5350 new_move_dir = move_dir;
5352 else if (move_dir_preference == move_preference &&
5353 move_dir == old_move_dir)
5355 /* prefer last direction when all directions are preferred equally */
5356 move_preference = move_dir_preference;
5357 new_move_dir = move_dir;
5361 MovDir[x][y] = new_move_dir;
5362 if (old_move_dir != new_move_dir)
5365 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5373 static void TurnRound(int x, int y)
5375 int direction = MovDir[x][y];
5378 GfxDir[x][y] = MovDir[x][y];
5384 GfxDir[x][y] = MovDir[x][y];
5387 if (direction != MovDir[x][y])
5392 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5395 GfxAction[x][y] = ACTION_WAITING;
5399 static boolean JustBeingPushed(int x, int y)
5403 for (i = 0; i < MAX_PLAYERS; i++)
5405 struct PlayerInfo *player = &stored_player[i];
5407 if (player->active && player->is_pushing && player->MovPos)
5409 int next_jx = player->jx + (player->jx - player->last_jx);
5410 int next_jy = player->jy + (player->jy - player->last_jy);
5412 if (x == next_jx && y == next_jy)
5420 void StartMoving(int x, int y)
5423 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5425 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5426 int element = Feld[x][y];
5432 if (MovDelay[x][y] == 0)
5433 GfxAction[x][y] = ACTION_DEFAULT;
5435 /* !!! this should be handled more generic (not only for mole) !!! */
5436 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5437 GfxAction[x][y] = ACTION_DEFAULT;
5440 if (CAN_FALL(element) && y < lev_fieldy - 1)
5442 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5443 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5444 if (JustBeingPushed(x, y))
5447 if (element == EL_QUICKSAND_FULL)
5449 if (IS_FREE(x, y + 1))
5451 InitMovingField(x, y, MV_DOWN);
5452 started_moving = TRUE;
5454 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5455 Store[x][y] = EL_ROCK;
5457 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5459 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5462 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5464 if (!MovDelay[x][y])
5465 MovDelay[x][y] = TILEY + 1;
5474 Feld[x][y] = EL_QUICKSAND_EMPTY;
5475 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5476 Store[x][y + 1] = Store[x][y];
5479 PlayLevelSoundAction(x, y, ACTION_FILLING);
5481 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5485 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5486 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5488 InitMovingField(x, y, MV_DOWN);
5489 started_moving = TRUE;
5491 Feld[x][y] = EL_QUICKSAND_FILLING;
5492 Store[x][y] = element;
5494 PlayLevelSoundAction(x, y, ACTION_FILLING);
5496 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5499 else if (element == EL_MAGIC_WALL_FULL)
5501 if (IS_FREE(x, y + 1))
5503 InitMovingField(x, y, MV_DOWN);
5504 started_moving = TRUE;
5506 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5507 Store[x][y] = EL_CHANGED(Store[x][y]);
5509 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5511 if (!MovDelay[x][y])
5512 MovDelay[x][y] = TILEY/4 + 1;
5521 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5522 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5523 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5527 else if (element == EL_BD_MAGIC_WALL_FULL)
5529 if (IS_FREE(x, y + 1))
5531 InitMovingField(x, y, MV_DOWN);
5532 started_moving = TRUE;
5534 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5535 Store[x][y] = EL_CHANGED2(Store[x][y]);
5537 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5539 if (!MovDelay[x][y])
5540 MovDelay[x][y] = TILEY/4 + 1;
5549 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5550 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5551 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5555 else if (CAN_PASS_MAGIC_WALL(element) &&
5556 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5557 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5559 InitMovingField(x, y, MV_DOWN);
5560 started_moving = TRUE;
5563 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5564 EL_BD_MAGIC_WALL_FILLING);
5565 Store[x][y] = element;
5568 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5570 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5573 SplashAcid(x, y + 1);
5575 InitMovingField(x, y, MV_DOWN);
5576 started_moving = TRUE;
5578 Store[x][y] = EL_ACID;
5580 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5581 GfxAction[x][y + 1] = ACTION_ACTIVE;
5585 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5586 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5588 #if USE_IMPACT_BUGFIX
5589 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5590 CAN_FALL(element) && WasJustFalling[x][y] &&
5591 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5593 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5594 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5595 (Feld[x][y + 1] == EL_BLOCKED)))
5597 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5598 CAN_SMASH(element) && WasJustFalling[x][y] &&
5599 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5601 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5602 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5603 (Feld[x][y + 1] == EL_BLOCKED)))
5608 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5609 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5610 WasJustMoving[x][y] && !Pushed[x][y + 1])
5612 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5613 WasJustMoving[x][y])
5618 /* this is needed for a special case not covered by calling "Impact()"
5619 from "ContinueMoving()": if an element moves to a tile directly below
5620 another element which was just falling on that tile (which was empty
5621 in the previous frame), the falling element above would just stop
5622 instead of smashing the element below (in previous version, the above
5623 element was just checked for "moving" instead of "falling", resulting
5624 in incorrect smashes caused by horizontal movement of the above
5625 element; also, the case of the player being the element to smash was
5626 simply not covered here... :-/ ) */
5629 WasJustMoving[x][y] = 0;
5630 WasJustFalling[x][y] = 0;
5633 CheckCollision[x][y] = 0;
5636 if (IS_PLAYER(x, y + 1))
5637 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5642 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5644 if (MovDir[x][y] == MV_NO_MOVING)
5646 InitMovingField(x, y, MV_DOWN);
5647 started_moving = TRUE;
5650 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5652 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5653 MovDir[x][y] = MV_DOWN;
5655 InitMovingField(x, y, MV_DOWN);
5656 started_moving = TRUE;
5658 else if (element == EL_AMOEBA_DROP)
5660 Feld[x][y] = EL_AMOEBA_GROWING;
5661 Store[x][y] = EL_AMOEBA_WET;
5663 /* Store[x][y + 1] must be zero, because:
5664 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5667 #if OLD_GAME_BEHAVIOUR
5668 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5670 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5671 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5672 element != EL_DX_SUPABOMB)
5675 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5676 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5677 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5678 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5681 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5682 (IS_FREE(x - 1, y + 1) ||
5683 Feld[x - 1][y + 1] == EL_ACID));
5684 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5685 (IS_FREE(x + 1, y + 1) ||
5686 Feld[x + 1][y + 1] == EL_ACID));
5687 boolean can_fall_any = (can_fall_left || can_fall_right);
5688 boolean can_fall_both = (can_fall_left && can_fall_right);
5690 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5692 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5694 if (slippery_type == SLIPPERY_ONLY_LEFT)
5695 can_fall_right = FALSE;
5696 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5697 can_fall_left = FALSE;
5698 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5699 can_fall_right = FALSE;
5700 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5701 can_fall_left = FALSE;
5703 can_fall_any = (can_fall_left || can_fall_right);
5704 can_fall_both = (can_fall_left && can_fall_right);
5707 #if USE_NEW_SP_SLIPPERY
5708 /* !!! better use the same properties as for custom elements here !!! */
5709 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5710 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5712 can_fall_right = FALSE; /* slip down on left side */
5713 can_fall_both = FALSE;
5720 if (game.emulation == EMU_BOULDERDASH ||
5721 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5722 can_fall_right = FALSE; /* slip down on left side */
5724 can_fall_left = !(can_fall_right = RND(2));
5726 can_fall_both = FALSE;
5733 if (can_fall_both &&
5734 (game.emulation != EMU_BOULDERDASH &&
5735 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5736 can_fall_left = !(can_fall_right = RND(2));
5739 /* if not determined otherwise, prefer left side for slipping down */
5740 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5741 started_moving = TRUE;
5745 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5747 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5750 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5751 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5752 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5753 int belt_dir = game.belt_dir[belt_nr];
5755 if ((belt_dir == MV_LEFT && left_is_free) ||
5756 (belt_dir == MV_RIGHT && right_is_free))
5759 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5762 InitMovingField(x, y, belt_dir);
5763 started_moving = TRUE;
5766 Pushed[x][y] = TRUE;
5767 Pushed[nextx][y] = TRUE;
5770 GfxAction[x][y] = ACTION_DEFAULT;
5774 MovDir[x][y] = 0; /* if element was moving, stop it */
5779 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5781 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5783 if (CAN_MOVE(element) && !started_moving)
5786 int move_pattern = element_info[element].move_pattern;
5791 if (MovDir[x][y] == MV_NO_MOVING)
5793 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5794 x, y, element, element_info[element].token_name);
5795 printf("StartMoving(): This should never happen!\n");
5800 Moving2Blocked(x, y, &newx, &newy);
5803 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5806 if ((element == EL_SATELLITE ||
5807 element == EL_BALLOON ||
5808 element == EL_SPRING)
5809 && JustBeingPushed(x, y))
5816 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5817 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5819 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5820 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5821 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5825 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5826 element, element_info[element].token_name,
5827 WasJustMoving[x][y],
5828 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5829 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5830 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5831 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5835 WasJustMoving[x][y] = 0;
5838 CheckCollision[x][y] = 0;
5840 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5843 if (Feld[x][y] != element) /* element has changed */
5845 element = Feld[x][y];
5846 move_pattern = element_info[element].move_pattern;
5848 if (!CAN_MOVE(element))
5852 if (Feld[x][y] != element) /* element has changed */
5860 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5861 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5863 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5865 Moving2Blocked(x, y, &newx, &newy);
5866 if (Feld[newx][newy] == EL_BLOCKED)
5867 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5873 if (FrameCounter < 1 && x == 0 && y == 29)
5874 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5877 if (!MovDelay[x][y]) /* start new movement phase */
5879 /* all objects that can change their move direction after each step
5880 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5882 if (element != EL_YAMYAM &&
5883 element != EL_DARK_YAMYAM &&
5884 element != EL_PACMAN &&
5885 !(move_pattern & MV_ANY_DIRECTION) &&
5886 move_pattern != MV_TURNING_LEFT &&
5887 move_pattern != MV_TURNING_RIGHT &&
5888 move_pattern != MV_TURNING_LEFT_RIGHT &&
5889 move_pattern != MV_TURNING_RIGHT_LEFT &&
5890 move_pattern != MV_TURNING_RANDOM)
5895 if (FrameCounter < 1 && x == 0 && y == 29)
5896 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5899 if (MovDelay[x][y] && (element == EL_BUG ||
5900 element == EL_SPACESHIP ||
5901 element == EL_SP_SNIKSNAK ||
5902 element == EL_SP_ELECTRON ||
5903 element == EL_MOLE))
5904 DrawLevelField(x, y);
5908 if (MovDelay[x][y]) /* wait some time before next movement */
5913 if (element == EL_YAMYAM)
5916 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5917 DrawLevelElementAnimation(x, y, element);
5921 if (MovDelay[x][y]) /* element still has to wait some time */
5924 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5925 ResetGfxAnimation(x, y);
5929 if (GfxAction[x][y] != ACTION_WAITING)
5930 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5932 GfxAction[x][y] = ACTION_WAITING;
5936 if (element == EL_ROBOT ||
5938 element == EL_PACMAN ||
5940 element == EL_YAMYAM ||
5941 element == EL_DARK_YAMYAM)
5944 DrawLevelElementAnimation(x, y, element);
5946 DrawLevelElementAnimationIfNeeded(x, y, element);
5948 PlayLevelSoundAction(x, y, ACTION_WAITING);
5950 else if (element == EL_SP_ELECTRON)
5951 DrawLevelElementAnimationIfNeeded(x, y, element);
5952 else if (element == EL_DRAGON)
5955 int dir = MovDir[x][y];
5956 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5957 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5958 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5959 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5960 dir == MV_UP ? IMG_FLAMES_1_UP :
5961 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5962 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5965 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5968 GfxAction[x][y] = ACTION_ATTACKING;
5970 if (IS_PLAYER(x, y))
5971 DrawPlayerField(x, y);
5973 DrawLevelField(x, y);
5975 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5977 for (i = 1; i <= 3; i++)
5979 int xx = x + i * dx;
5980 int yy = y + i * dy;
5981 int sx = SCREENX(xx);
5982 int sy = SCREENY(yy);
5983 int flame_graphic = graphic + (i - 1);
5985 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5990 int flamed = MovingOrBlocked2Element(xx, yy);
5994 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5996 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5997 RemoveMovingField(xx, yy);
5999 RemoveField(xx, yy);
6001 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6004 RemoveMovingField(xx, yy);
6008 if (ChangeDelay[xx][yy])
6009 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6010 Feld[xx][yy] == EL_BLOCKED));
6014 ChangeDelay[xx][yy] = 0;
6016 Feld[xx][yy] = EL_FLAMES;
6017 if (IN_SCR_FIELD(sx, sy))
6019 DrawLevelFieldCrumbledSand(xx, yy);
6020 DrawGraphic(sx, sy, flame_graphic, frame);
6025 if (Feld[xx][yy] == EL_FLAMES)
6026 Feld[xx][yy] = EL_EMPTY;
6027 DrawLevelField(xx, yy);
6032 if (MovDelay[x][y]) /* element still has to wait some time */
6034 PlayLevelSoundAction(x, y, ACTION_WAITING);
6040 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6041 for all other elements GfxAction will be set by InitMovingField() */
6042 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6043 GfxAction[x][y] = ACTION_MOVING;
6047 /* now make next step */
6049 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6051 if (DONT_COLLIDE_WITH(element) &&
6052 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6053 !PLAYER_ENEMY_PROTECTED(newx, newy))
6056 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6060 /* player killed by element which is deadly when colliding with */
6062 KillHero(PLAYERINFO(newx, newy));
6069 else if (CAN_MOVE_INTO_ACID(element) &&
6070 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6071 (MovDir[x][y] == MV_DOWN ||
6072 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6074 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6075 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6079 else if ((element == EL_PENGUIN ||
6080 element == EL_ROBOT ||
6081 element == EL_SATELLITE ||
6082 element == EL_BALLOON ||
6083 IS_CUSTOM_ELEMENT(element)) &&
6084 IN_LEV_FIELD(newx, newy) &&
6085 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6088 SplashAcid(newx, newy);
6089 Store[x][y] = EL_ACID;
6091 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6093 if (Feld[newx][newy] == EL_EXIT_OPEN)
6097 DrawLevelField(x, y);
6099 Feld[x][y] = EL_EMPTY;
6100 DrawLevelField(x, y);
6103 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6104 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6105 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6107 local_player->friends_still_needed--;
6108 if (!local_player->friends_still_needed &&
6109 !local_player->GameOver && AllPlayersGone)
6110 local_player->LevelSolved = local_player->GameOver = TRUE;
6114 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6116 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6117 DrawLevelField(newx, newy);
6119 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6121 else if (!IS_FREE(newx, newy))
6123 GfxAction[x][y] = ACTION_WAITING;
6125 if (IS_PLAYER(x, y))
6126 DrawPlayerField(x, y);
6128 DrawLevelField(x, y);
6133 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6135 if (IS_FOOD_PIG(Feld[newx][newy]))
6137 if (IS_MOVING(newx, newy))
6138 RemoveMovingField(newx, newy);
6141 Feld[newx][newy] = EL_EMPTY;
6142 DrawLevelField(newx, newy);
6145 PlayLevelSound(x, y, SND_PIG_DIGGING);
6147 else if (!IS_FREE(newx, newy))
6149 if (IS_PLAYER(x, y))
6150 DrawPlayerField(x, y);
6152 DrawLevelField(x, y);
6161 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6164 else if (IS_CUSTOM_ELEMENT(element) &&
6165 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6169 !IS_FREE(newx, newy)
6174 int new_element = Feld[newx][newy];
6177 printf("::: '%s' digs '%s' [%d]\n",
6178 element_info[element].token_name,
6179 element_info[Feld[newx][newy]].token_name,
6180 StorePlayer[newx][newy]);
6183 if (!IS_FREE(newx, newy))
6185 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6186 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6189 /* no element can dig solid indestructible elements */
6190 if (IS_INDESTRUCTIBLE(new_element) &&
6191 !IS_DIGGABLE(new_element) &&
6192 !IS_COLLECTIBLE(new_element))
6195 if (AmoebaNr[newx][newy] &&
6196 (new_element == EL_AMOEBA_FULL ||
6197 new_element == EL_BD_AMOEBA ||
6198 new_element == EL_AMOEBA_GROWING))
6200 AmoebaCnt[AmoebaNr[newx][newy]]--;
6201 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6204 if (IS_MOVING(newx, newy))
6205 RemoveMovingField(newx, newy);
6208 RemoveField(newx, newy);
6209 DrawLevelField(newx, newy);
6212 /* if digged element was about to explode, prevent the explosion */
6213 ExplodeField[newx][newy] = EX_TYPE_NONE;
6215 PlayLevelSoundAction(x, y, action);
6220 Store[newx][newy] = EL_EMPTY;
6221 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6222 Store[newx][newy] = element_info[element].move_leave_element;
6224 Store[newx][newy] = EL_EMPTY;
6225 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6226 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6227 Store[newx][newy] = element_info[element].move_leave_element;
6230 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6231 element_info[element].can_leave_element = TRUE;
6234 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6236 RunnerVisit[x][y] = FrameCounter;
6237 PlayerVisit[x][y] /= 8; /* expire player visit path */
6243 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6245 if (!IS_FREE(newx, newy))
6247 if (IS_PLAYER(x, y))
6248 DrawPlayerField(x, y);
6250 DrawLevelField(x, y);
6256 boolean wanna_flame = !RND(10);
6257 int dx = newx - x, dy = newy - y;
6258 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6259 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6260 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6261 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6262 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6263 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6266 IS_CLASSIC_ENEMY(element1) ||
6267 IS_CLASSIC_ENEMY(element2)) &&
6268 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6269 element1 != EL_FLAMES && element2 != EL_FLAMES)
6272 ResetGfxAnimation(x, y);
6273 GfxAction[x][y] = ACTION_ATTACKING;
6276 if (IS_PLAYER(x, y))
6277 DrawPlayerField(x, y);
6279 DrawLevelField(x, y);
6281 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6283 MovDelay[x][y] = 50;
6287 RemoveField(newx, newy);
6289 Feld[newx][newy] = EL_FLAMES;
6290 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6293 RemoveField(newx1, newy1);
6295 Feld[newx1][newy1] = EL_FLAMES;
6297 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6300 RemoveField(newx2, newy2);
6302 Feld[newx2][newy2] = EL_FLAMES;
6309 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6310 Feld[newx][newy] == EL_DIAMOND)
6312 if (IS_MOVING(newx, newy))
6313 RemoveMovingField(newx, newy);
6316 Feld[newx][newy] = EL_EMPTY;
6317 DrawLevelField(newx, newy);
6320 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6322 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6323 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6325 if (AmoebaNr[newx][newy])
6327 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6328 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6329 Feld[newx][newy] == EL_BD_AMOEBA)
6330 AmoebaCnt[AmoebaNr[newx][newy]]--;
6335 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6337 if (IS_MOVING(newx, newy))
6340 RemoveMovingField(newx, newy);
6344 Feld[newx][newy] = EL_EMPTY;
6345 DrawLevelField(newx, newy);
6348 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6350 else if ((element == EL_PACMAN || element == EL_MOLE)
6351 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6353 if (AmoebaNr[newx][newy])
6355 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6356 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6357 Feld[newx][newy] == EL_BD_AMOEBA)
6358 AmoebaCnt[AmoebaNr[newx][newy]]--;
6361 if (element == EL_MOLE)
6363 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6364 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6366 ResetGfxAnimation(x, y);
6367 GfxAction[x][y] = ACTION_DIGGING;
6368 DrawLevelField(x, y);
6370 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6372 return; /* wait for shrinking amoeba */
6374 else /* element == EL_PACMAN */
6376 Feld[newx][newy] = EL_EMPTY;
6377 DrawLevelField(newx, newy);
6378 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6381 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6382 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6383 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6385 /* wait for shrinking amoeba to completely disappear */
6388 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6390 /* object was running against a wall */
6395 if (move_pattern & MV_ANY_DIRECTION &&
6396 move_pattern == MovDir[x][y])
6398 int blocking_element =
6399 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6402 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6403 element_info[element].token_name,
6404 element_info[blocking_element].token_name,
6408 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6411 element = Feld[x][y]; /* element might have changed */
6416 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6417 DrawLevelElementAnimation(x, y, element);
6419 if (element == EL_BUG ||
6420 element == EL_SPACESHIP ||
6421 element == EL_SP_SNIKSNAK)
6422 DrawLevelField(x, y);
6423 else if (element == EL_MOLE)
6424 DrawLevelField(x, y);
6425 else if (element == EL_BD_BUTTERFLY ||
6426 element == EL_BD_FIREFLY)
6427 DrawLevelElementAnimationIfNeeded(x, y, element);
6428 else if (element == EL_SATELLITE)
6429 DrawLevelElementAnimationIfNeeded(x, y, element);
6430 else if (element == EL_SP_ELECTRON)
6431 DrawLevelElementAnimationIfNeeded(x, y, element);
6434 if (DONT_TOUCH(element))
6435 TestIfBadThingTouchesHero(x, y);
6438 PlayLevelSoundAction(x, y, ACTION_WAITING);
6444 InitMovingField(x, y, MovDir[x][y]);
6446 PlayLevelSoundAction(x, y, ACTION_MOVING);
6450 ContinueMoving(x, y);
6453 void ContinueMoving(int x, int y)
6455 int element = Feld[x][y];
6456 int stored = Store[x][y];
6457 struct ElementInfo *ei = &element_info[element];
6458 int direction = MovDir[x][y];
6459 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6460 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6461 int newx = x + dx, newy = y + dy;
6463 int nextx = newx + dx, nexty = newy + dy;
6466 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6467 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6469 boolean pushed_by_player = Pushed[x][y];
6472 MovPos[x][y] += getElementMoveStepsize(x, y);
6475 if (pushed_by_player && IS_PLAYER(x, y))
6477 /* special case: moving object pushed by player */
6478 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6481 if (pushed_by_player) /* special case: moving object pushed by player */
6482 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6485 if (ABS(MovPos[x][y]) < TILEX)
6487 DrawLevelField(x, y);
6489 return; /* element is still moving */
6492 /* element reached destination field */
6494 Feld[x][y] = EL_EMPTY;
6495 Feld[newx][newy] = element;
6496 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6499 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6501 element = Feld[newx][newy] = EL_ACID;
6504 else if (element == EL_MOLE)
6506 Feld[x][y] = EL_SAND;
6508 DrawLevelFieldCrumbledSandNeighbours(x, y);
6510 else if (element == EL_QUICKSAND_FILLING)
6512 element = Feld[newx][newy] = get_next_element(element);
6513 Store[newx][newy] = Store[x][y];
6515 else if (element == EL_QUICKSAND_EMPTYING)
6517 Feld[x][y] = get_next_element(element);
6518 element = Feld[newx][newy] = Store[x][y];
6520 else if (element == EL_MAGIC_WALL_FILLING)
6522 element = Feld[newx][newy] = get_next_element(element);
6523 if (!game.magic_wall_active)
6524 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6525 Store[newx][newy] = Store[x][y];
6527 else if (element == EL_MAGIC_WALL_EMPTYING)
6529 Feld[x][y] = get_next_element(element);
6530 if (!game.magic_wall_active)
6531 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6532 element = Feld[newx][newy] = Store[x][y];
6534 else if (element == EL_BD_MAGIC_WALL_FILLING)
6536 element = Feld[newx][newy] = get_next_element(element);
6537 if (!game.magic_wall_active)
6538 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6539 Store[newx][newy] = Store[x][y];
6541 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6543 Feld[x][y] = get_next_element(element);
6544 if (!game.magic_wall_active)
6545 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6546 element = Feld[newx][newy] = Store[x][y];
6548 else if (element == EL_AMOEBA_DROPPING)
6550 Feld[x][y] = get_next_element(element);
6551 element = Feld[newx][newy] = Store[x][y];
6553 else if (element == EL_SOKOBAN_OBJECT)
6556 Feld[x][y] = Back[x][y];
6558 if (Back[newx][newy])
6559 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6561 Back[x][y] = Back[newx][newy] = 0;
6564 else if (Store[x][y] == EL_ACID)
6566 element = Feld[newx][newy] = EL_ACID;
6570 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6571 ei->move_leave_element != EL_EMPTY &&
6572 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6573 Store[x][y] != EL_EMPTY))
6575 /* some elements can leave other elements behind after moving */
6577 Feld[x][y] = ei->move_leave_element;
6578 InitField(x, y, FALSE);
6580 if (GFX_CRUMBLED(Feld[x][y]))
6581 DrawLevelFieldCrumbledSandNeighbours(x, y);
6585 Store[x][y] = EL_EMPTY;
6589 MovDelay[newx][newy] = 0;
6591 if (CAN_CHANGE(element))
6593 /* copy element change control values to new field */
6594 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6595 ChangePage[newx][newy] = ChangePage[x][y];
6596 Changed[newx][newy] = Changed[x][y];
6597 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6600 ChangeDelay[x][y] = 0;
6601 ChangePage[x][y] = -1;
6602 Changed[x][y] = FALSE;
6603 ChangeEvent[x][y] = -1;
6605 /* copy animation control values to new field */
6606 GfxFrame[newx][newy] = GfxFrame[x][y];
6607 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6608 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6609 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6611 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6614 /* do this after checking for left-behind element */
6615 ResetGfxAnimation(x, y); /* reset animation values for old field */
6619 /* some elements can leave other elements behind after moving */
6621 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6622 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6623 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6625 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6626 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6630 int move_leave_element = ei->move_leave_element;
6632 Feld[x][y] = move_leave_element;
6634 #if USE_PREVIOUS_MOVE_DIR
6635 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6636 MovDir[x][y] = direction;
6639 InitField(x, y, FALSE);
6641 if (GFX_CRUMBLED(Feld[x][y]))
6642 DrawLevelFieldCrumbledSandNeighbours(x, y);
6644 if (ELEM_IS_PLAYER(move_leave_element))
6645 RelocatePlayer(x, y, move_leave_element);
6650 /* some elements can leave other elements behind after moving */
6651 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6652 ei->move_leave_element != EL_EMPTY &&
6653 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6654 ei->can_leave_element_last))
6656 Feld[x][y] = ei->move_leave_element;
6657 InitField(x, y, FALSE);
6659 if (GFX_CRUMBLED(Feld[x][y]))
6660 DrawLevelFieldCrumbledSandNeighbours(x, y);
6663 ei->can_leave_element_last = ei->can_leave_element;
6664 ei->can_leave_element = FALSE;
6668 /* do this after checking for left-behind element */
6669 ResetGfxAnimation(x, y); /* reset animation values for old field */
6673 /* 2.1.1 (does not work correctly for spring) */
6674 if (!CAN_MOVE(element))
6675 MovDir[newx][newy] = 0;
6679 /* (does not work for falling objects that slide horizontally) */
6680 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6681 MovDir[newx][newy] = 0;
6684 if (!CAN_MOVE(element) ||
6685 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6686 MovDir[newx][newy] = 0;
6690 if (!CAN_MOVE(element) ||
6691 (CAN_FALL(element) && direction == MV_DOWN))
6692 GfxDir[x][y] = MovDir[newx][newy] = 0;
6694 if (!CAN_MOVE(element) ||
6695 (CAN_FALL(element) && direction == MV_DOWN &&
6696 (element == EL_SPRING ||
6697 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6698 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6699 GfxDir[x][y] = MovDir[newx][newy] = 0;
6705 DrawLevelField(x, y);
6706 DrawLevelField(newx, newy);
6708 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6710 /* prevent pushed element from moving on in pushed direction */
6711 if (pushed_by_player && CAN_MOVE(element) &&
6712 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6713 !(element_info[element].move_pattern & direction))
6714 TurnRound(newx, newy);
6717 /* prevent elements on conveyor belt from moving on in last direction */
6718 if (pushed_by_conveyor && CAN_FALL(element) &&
6719 direction & MV_HORIZONTAL)
6722 if (CAN_MOVE(element))
6723 InitMovDir(newx, newy);
6725 MovDir[newx][newy] = 0;
6727 MovDir[newx][newy] = 0;
6732 if (!pushed_by_player)
6734 int nextx = newx + dx, nexty = newy + dy;
6735 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6737 WasJustMoving[newx][newy] = 3;
6739 if (CAN_FALL(element) && direction == MV_DOWN)
6740 WasJustFalling[newx][newy] = 3;
6742 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6743 CheckCollision[newx][newy] = 2;
6746 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6748 TestIfBadThingTouchesHero(newx, newy);
6749 TestIfBadThingTouchesFriend(newx, newy);
6751 if (!IS_CUSTOM_ELEMENT(element))
6752 TestIfBadThingTouchesOtherBadThing(newx, newy);
6754 else if (element == EL_PENGUIN)
6755 TestIfFriendTouchesBadThing(newx, newy);
6757 #if USE_NEW_MOVE_STYLE
6759 if (CAN_FALL(element) && direction == MV_DOWN &&
6760 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6761 IS_PLAYER(x, newy + 1))
6762 printf("::: we would now kill the player [%d]\n", FrameCounter);
6765 /* give the player one last chance (one more frame) to move away */
6766 if (CAN_FALL(element) && direction == MV_DOWN &&
6767 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6768 ((newy < lev_fieldy - 1 && !IS_PLAYER(x, newy + 1)) ||
6769 game.engine_version < VERSION_IDENT(3,1,1,0)))
6772 if (CAN_FALL(element) && direction == MV_DOWN &&
6773 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6781 if (pushed_by_player && !game.use_change_when_pushing_bug)
6783 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6786 if (pushed_by_player)
6791 int dig_side = MV_DIR_OPPOSITE(direction);
6793 static int trigger_sides[4] =
6795 CH_SIDE_RIGHT, /* moving left */
6796 CH_SIDE_LEFT, /* moving right */
6797 CH_SIDE_BOTTOM, /* moving up */
6798 CH_SIDE_TOP, /* moving down */
6800 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6802 struct PlayerInfo *player = PLAYERINFO(x, y);
6804 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6805 player->index_bit, dig_side);
6806 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6807 player->index_bit, dig_side);
6812 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6816 if (ChangePage[newx][newy] != -1) /* delayed change */
6817 ChangeElement(newx, newy, ChangePage[newx][newy]);
6822 TestIfElementHitsCustomElement(newx, newy, direction);
6826 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6828 int hitting_element = Feld[newx][newy];
6830 /* !!! fix side (direction) orientation here and elsewhere !!! */
6831 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6835 if (IN_LEV_FIELD(nextx, nexty))
6837 int opposite_direction = MV_DIR_OPPOSITE(direction);
6838 int hitting_side = direction;
6839 int touched_side = opposite_direction;
6840 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6841 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6842 MovDir[nextx][nexty] != direction ||
6843 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6849 CheckElementChangeBySide(nextx, nexty, touched_element,
6850 CE_HIT_BY_SOMETHING, opposite_direction);
6852 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6853 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6855 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6857 struct ElementChangeInfo *change =
6858 &element_info[hitting_element].change_page[i];
6860 if (change->can_change &&
6861 change->has_event[CE_HITTING_X] &&
6862 change->trigger_side & touched_side &&
6863 change->trigger_element == touched_element)
6865 CheckElementChangeByPage(newx, newy, hitting_element,
6866 touched_element, CE_HITTING_X, i);
6872 if (IS_CUSTOM_ELEMENT(touched_element) &&
6873 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6875 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6877 struct ElementChangeInfo *change =
6878 &element_info[touched_element].change_page[i];
6880 if (change->can_change &&
6881 change->has_event[CE_HIT_BY_X] &&
6882 change->trigger_side & hitting_side &&
6883 change->trigger_element == hitting_element)
6885 CheckElementChangeByPage(nextx, nexty, touched_element,
6886 hitting_element, CE_HIT_BY_X,i);
6897 TestIfPlayerTouchesCustomElement(newx, newy);
6898 TestIfElementTouchesCustomElement(newx, newy);
6901 int AmoebeNachbarNr(int ax, int ay)
6904 int element = Feld[ax][ay];
6906 static int xy[4][2] =
6914 for (i = 0; i < NUM_DIRECTIONS; i++)
6916 int x = ax + xy[i][0];
6917 int y = ay + xy[i][1];
6919 if (!IN_LEV_FIELD(x, y))
6922 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6923 group_nr = AmoebaNr[x][y];
6929 void AmoebenVereinigen(int ax, int ay)
6931 int i, x, y, xx, yy;
6932 int new_group_nr = AmoebaNr[ax][ay];
6933 static int xy[4][2] =
6941 if (new_group_nr == 0)
6944 for (i = 0; i < NUM_DIRECTIONS; i++)
6949 if (!IN_LEV_FIELD(x, y))
6952 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6953 Feld[x][y] == EL_BD_AMOEBA ||
6954 Feld[x][y] == EL_AMOEBA_DEAD) &&
6955 AmoebaNr[x][y] != new_group_nr)
6957 int old_group_nr = AmoebaNr[x][y];
6959 if (old_group_nr == 0)
6962 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6963 AmoebaCnt[old_group_nr] = 0;
6964 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6965 AmoebaCnt2[old_group_nr] = 0;
6967 for (yy = 0; yy < lev_fieldy; yy++)
6969 for (xx = 0; xx < lev_fieldx; xx++)
6971 if (AmoebaNr[xx][yy] == old_group_nr)
6972 AmoebaNr[xx][yy] = new_group_nr;
6979 void AmoebeUmwandeln(int ax, int ay)
6983 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6985 int group_nr = AmoebaNr[ax][ay];
6990 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6991 printf("AmoebeUmwandeln(): This should never happen!\n");
6996 for (y = 0; y < lev_fieldy; y++)
6998 for (x = 0; x < lev_fieldx; x++)
7000 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7003 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7007 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7008 SND_AMOEBA_TURNING_TO_GEM :
7009 SND_AMOEBA_TURNING_TO_ROCK));
7014 static int xy[4][2] =
7022 for (i = 0; i < NUM_DIRECTIONS; i++)
7027 if (!IN_LEV_FIELD(x, y))
7030 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7032 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7033 SND_AMOEBA_TURNING_TO_GEM :
7034 SND_AMOEBA_TURNING_TO_ROCK));
7041 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7044 int group_nr = AmoebaNr[ax][ay];
7045 boolean done = FALSE;
7050 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7051 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7056 for (y = 0; y < lev_fieldy; y++)
7058 for (x = 0; x < lev_fieldx; x++)
7060 if (AmoebaNr[x][y] == group_nr &&
7061 (Feld[x][y] == EL_AMOEBA_DEAD ||
7062 Feld[x][y] == EL_BD_AMOEBA ||
7063 Feld[x][y] == EL_AMOEBA_GROWING))
7066 Feld[x][y] = new_element;
7067 InitField(x, y, FALSE);
7068 DrawLevelField(x, y);
7075 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7076 SND_BD_AMOEBA_TURNING_TO_ROCK :
7077 SND_BD_AMOEBA_TURNING_TO_GEM));
7080 void AmoebeWaechst(int x, int y)
7082 static unsigned long sound_delay = 0;
7083 static unsigned long sound_delay_value = 0;
7085 if (!MovDelay[x][y]) /* start new growing cycle */
7089 if (DelayReached(&sound_delay, sound_delay_value))
7092 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7094 if (Store[x][y] == EL_BD_AMOEBA)
7095 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7097 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7099 sound_delay_value = 30;
7103 if (MovDelay[x][y]) /* wait some time before growing bigger */
7106 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7108 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7109 6 - MovDelay[x][y]);
7111 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7114 if (!MovDelay[x][y])
7116 Feld[x][y] = Store[x][y];
7118 DrawLevelField(x, y);
7123 void AmoebaDisappearing(int x, int y)
7125 static unsigned long sound_delay = 0;
7126 static unsigned long sound_delay_value = 0;
7128 if (!MovDelay[x][y]) /* start new shrinking cycle */
7132 if (DelayReached(&sound_delay, sound_delay_value))
7133 sound_delay_value = 30;
7136 if (MovDelay[x][y]) /* wait some time before shrinking */
7139 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7141 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7142 6 - MovDelay[x][y]);
7144 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7147 if (!MovDelay[x][y])
7149 Feld[x][y] = EL_EMPTY;
7150 DrawLevelField(x, y);
7152 /* don't let mole enter this field in this cycle;
7153 (give priority to objects falling to this field from above) */
7159 void AmoebeAbleger(int ax, int ay)
7162 int element = Feld[ax][ay];
7163 int graphic = el2img(element);
7164 int newax = ax, neway = ay;
7165 static int xy[4][2] =
7173 if (!level.amoeba_speed)
7175 Feld[ax][ay] = EL_AMOEBA_DEAD;
7176 DrawLevelField(ax, ay);
7180 if (IS_ANIMATED(graphic))
7181 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7183 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7184 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7186 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7189 if (MovDelay[ax][ay])
7193 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7196 int x = ax + xy[start][0];
7197 int y = ay + xy[start][1];
7199 if (!IN_LEV_FIELD(x, y))
7203 if (IS_FREE(x, y) ||
7204 CAN_GROW_INTO(Feld[x][y]) ||
7205 Feld[x][y] == EL_QUICKSAND_EMPTY)
7211 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7212 if (IS_FREE(x, y) ||
7213 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7220 if (newax == ax && neway == ay)
7223 else /* normal or "filled" (BD style) amoeba */
7226 boolean waiting_for_player = FALSE;
7228 for (i = 0; i < NUM_DIRECTIONS; i++)
7230 int j = (start + i) % 4;
7231 int x = ax + xy[j][0];
7232 int y = ay + xy[j][1];
7234 if (!IN_LEV_FIELD(x, y))
7238 if (IS_FREE(x, y) ||
7239 CAN_GROW_INTO(Feld[x][y]) ||
7240 Feld[x][y] == EL_QUICKSAND_EMPTY)
7247 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7248 if (IS_FREE(x, y) ||
7249 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7256 else if (IS_PLAYER(x, y))
7257 waiting_for_player = TRUE;
7260 if (newax == ax && neway == ay) /* amoeba cannot grow */
7263 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7265 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7268 Feld[ax][ay] = EL_AMOEBA_DEAD;
7269 DrawLevelField(ax, ay);
7270 AmoebaCnt[AmoebaNr[ax][ay]]--;
7272 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7274 if (element == EL_AMOEBA_FULL)
7275 AmoebeUmwandeln(ax, ay);
7276 else if (element == EL_BD_AMOEBA)
7277 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7282 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7284 /* amoeba gets larger by growing in some direction */
7286 int new_group_nr = AmoebaNr[ax][ay];
7289 if (new_group_nr == 0)
7291 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7292 printf("AmoebeAbleger(): This should never happen!\n");
7297 AmoebaNr[newax][neway] = new_group_nr;
7298 AmoebaCnt[new_group_nr]++;
7299 AmoebaCnt2[new_group_nr]++;
7301 /* if amoeba touches other amoeba(s) after growing, unify them */
7302 AmoebenVereinigen(newax, neway);
7304 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7306 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7312 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7313 (neway == lev_fieldy - 1 && newax != ax))
7315 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7316 Store[newax][neway] = element;
7318 else if (neway == ay)
7320 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7322 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7324 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7329 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7330 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7331 Store[ax][ay] = EL_AMOEBA_DROP;
7332 ContinueMoving(ax, ay);
7336 DrawLevelField(newax, neway);
7339 void Life(int ax, int ay)
7342 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7344 int element = Feld[ax][ay];
7345 int graphic = el2img(element);
7346 boolean changed = FALSE;
7348 if (IS_ANIMATED(graphic))
7349 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7354 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7355 MovDelay[ax][ay] = life_time;
7357 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7360 if (MovDelay[ax][ay])
7364 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7366 int xx = ax+x1, yy = ay+y1;
7369 if (!IN_LEV_FIELD(xx, yy))
7372 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7374 int x = xx+x2, y = yy+y2;
7376 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7379 if (((Feld[x][y] == element ||
7380 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7382 (IS_FREE(x, y) && Stop[x][y]))
7386 if (xx == ax && yy == ay) /* field in the middle */
7388 if (nachbarn < life[0] || nachbarn > life[1])
7390 Feld[xx][yy] = EL_EMPTY;
7392 DrawLevelField(xx, yy);
7393 Stop[xx][yy] = TRUE;
7398 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7399 { /* free border field */
7400 if (nachbarn >= life[2] && nachbarn <= life[3])
7402 Feld[xx][yy] = element;
7403 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7405 DrawLevelField(xx, yy);
7406 Stop[xx][yy] = TRUE;
7411 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7412 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7413 { /* free border field */
7414 if (nachbarn >= life[2] && nachbarn <= life[3])
7416 Feld[xx][yy] = element;
7417 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7419 DrawLevelField(xx, yy);
7420 Stop[xx][yy] = TRUE;
7428 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7429 SND_GAME_OF_LIFE_GROWING);
7432 static void InitRobotWheel(int x, int y)
7434 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7437 static void RunRobotWheel(int x, int y)
7439 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7442 static void StopRobotWheel(int x, int y)
7444 if (ZX == x && ZY == y)
7448 static void InitTimegateWheel(int x, int y)
7451 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7453 /* another brainless, "type style" bug ... :-( */
7454 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7458 static void RunTimegateWheel(int x, int y)
7460 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7463 void CheckExit(int x, int y)
7465 if (local_player->gems_still_needed > 0 ||
7466 local_player->sokobanfields_still_needed > 0 ||
7467 local_player->lights_still_needed > 0)
7469 int element = Feld[x][y];
7470 int graphic = el2img(element);
7472 if (IS_ANIMATED(graphic))
7473 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7478 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7481 Feld[x][y] = EL_EXIT_OPENING;
7483 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7486 void CheckExitSP(int x, int y)
7488 if (local_player->gems_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_SP_EXIT_OPENING;
7504 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7507 static void CloseAllOpenTimegates()
7511 for (y = 0; y < lev_fieldy; y++)
7513 for (x = 0; x < lev_fieldx; x++)
7515 int element = Feld[x][y];
7517 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7519 Feld[x][y] = EL_TIMEGATE_CLOSING;
7521 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7523 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7530 void EdelsteinFunkeln(int x, int y)
7532 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7535 if (Feld[x][y] == EL_BD_DIAMOND)
7538 if (MovDelay[x][y] == 0) /* next animation frame */
7539 MovDelay[x][y] = 11 * !SimpleRND(500);
7541 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7545 if (setup.direct_draw && MovDelay[x][y])
7546 SetDrawtoField(DRAW_BUFFERED);
7548 DrawLevelElementAnimation(x, y, Feld[x][y]);
7550 if (MovDelay[x][y] != 0)
7552 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7553 10 - MovDelay[x][y]);
7555 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7557 if (setup.direct_draw)
7561 dest_x = FX + SCREENX(x) * TILEX;
7562 dest_y = FY + SCREENY(y) * TILEY;
7564 BlitBitmap(drawto_field, window,
7565 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7566 SetDrawtoField(DRAW_DIRECT);
7572 void MauerWaechst(int x, int y)
7576 if (!MovDelay[x][y]) /* next animation frame */
7577 MovDelay[x][y] = 3 * delay;
7579 if (MovDelay[x][y]) /* wait some time before next frame */
7583 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7585 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7586 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7588 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7591 if (!MovDelay[x][y])
7593 if (MovDir[x][y] == MV_LEFT)
7595 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7596 DrawLevelField(x - 1, y);
7598 else if (MovDir[x][y] == MV_RIGHT)
7600 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7601 DrawLevelField(x + 1, y);
7603 else if (MovDir[x][y] == MV_UP)
7605 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7606 DrawLevelField(x, y - 1);
7610 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7611 DrawLevelField(x, y + 1);
7614 Feld[x][y] = Store[x][y];
7616 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7617 DrawLevelField(x, y);
7622 void MauerAbleger(int ax, int ay)
7624 int element = Feld[ax][ay];
7625 int graphic = el2img(element);
7626 boolean oben_frei = FALSE, unten_frei = FALSE;
7627 boolean links_frei = FALSE, rechts_frei = FALSE;
7628 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7629 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7630 boolean new_wall = FALSE;
7632 if (IS_ANIMATED(graphic))
7633 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7635 if (!MovDelay[ax][ay]) /* start building new wall */
7636 MovDelay[ax][ay] = 6;
7638 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7641 if (MovDelay[ax][ay])
7645 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7647 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7649 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7651 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7654 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7655 element == EL_EXPANDABLE_WALL_ANY)
7659 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7660 Store[ax][ay-1] = element;
7661 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7662 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7663 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7664 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7669 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7670 Store[ax][ay+1] = element;
7671 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7672 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7673 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7674 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7679 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7680 element == EL_EXPANDABLE_WALL_ANY ||
7681 element == EL_EXPANDABLE_WALL)
7685 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7686 Store[ax-1][ay] = element;
7687 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7688 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7689 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7690 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7696 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7697 Store[ax+1][ay] = element;
7698 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7699 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7700 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7701 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7706 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7707 DrawLevelField(ax, ay);
7709 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7711 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7712 unten_massiv = TRUE;
7713 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7714 links_massiv = TRUE;
7715 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7716 rechts_massiv = TRUE;
7718 if (((oben_massiv && unten_massiv) ||
7719 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7720 element == EL_EXPANDABLE_WALL) &&
7721 ((links_massiv && rechts_massiv) ||
7722 element == EL_EXPANDABLE_WALL_VERTICAL))
7723 Feld[ax][ay] = EL_WALL;
7727 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7729 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7733 void CheckForDragon(int x, int y)
7736 boolean dragon_found = FALSE;
7737 static int xy[4][2] =
7745 for (i = 0; i < NUM_DIRECTIONS; i++)
7747 for (j = 0; j < 4; j++)
7749 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7751 if (IN_LEV_FIELD(xx, yy) &&
7752 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7754 if (Feld[xx][yy] == EL_DRAGON)
7755 dragon_found = TRUE;
7764 for (i = 0; i < NUM_DIRECTIONS; i++)
7766 for (j = 0; j < 3; j++)
7768 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7770 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7772 Feld[xx][yy] = EL_EMPTY;
7773 DrawLevelField(xx, yy);
7782 static void InitBuggyBase(int x, int y)
7784 int element = Feld[x][y];
7785 int activating_delay = FRAMES_PER_SECOND / 4;
7788 (element == EL_SP_BUGGY_BASE ?
7789 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7790 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7792 element == EL_SP_BUGGY_BASE_ACTIVE ?
7793 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7796 static void WarnBuggyBase(int x, int y)
7799 static int xy[4][2] =
7807 for (i = 0; i < NUM_DIRECTIONS; i++)
7809 int xx = x + xy[i][0], yy = y + xy[i][1];
7811 if (IS_PLAYER(xx, yy))
7813 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7820 static void InitTrap(int x, int y)
7822 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7825 static void ActivateTrap(int x, int y)
7827 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7830 static void ChangeActiveTrap(int x, int y)
7832 int graphic = IMG_TRAP_ACTIVE;
7834 /* if new animation frame was drawn, correct crumbled sand border */
7835 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7836 DrawLevelFieldCrumbledSand(x, y);
7839 static void ChangeElementNowExt(int x, int y, int target_element)
7841 int previous_move_direction = MovDir[x][y];
7843 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7844 IS_WALKABLE(Feld[x][y]));
7846 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7847 IS_WALKABLE(Feld[x][y]) &&
7851 /* check if element under player changes from accessible to unaccessible
7852 (needed for special case of dropping element which then changes) */
7853 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7854 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7857 printf("::: BOOOM! [%d, '%s']\n", target_element,
7858 element_info[target_element].token_name);
7870 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7871 RemoveMovingField(x, y);
7875 Feld[x][y] = target_element;
7878 Feld[x][y] = target_element;
7881 ResetGfxAnimation(x, y);
7882 ResetRandomAnimationValue(x, y);
7884 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7885 MovDir[x][y] = previous_move_direction;
7888 InitField_WithBug1(x, y, FALSE);
7890 InitField(x, y, FALSE);
7891 if (CAN_MOVE(Feld[x][y]))
7895 DrawLevelField(x, y);
7897 if (GFX_CRUMBLED(Feld[x][y]))
7898 DrawLevelFieldCrumbledSandNeighbours(x, y);
7902 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7906 TestIfBadThingTouchesHero(x, y);
7907 TestIfPlayerTouchesCustomElement(x, y);
7908 TestIfElementTouchesCustomElement(x, y);
7911 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7912 if (ELEM_IS_PLAYER(target_element))
7913 RelocatePlayer(x, y, target_element);
7916 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7918 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7922 TestIfBadThingTouchesHero(x, y);
7923 TestIfPlayerTouchesCustomElement(x, y);
7924 TestIfElementTouchesCustomElement(x, y);
7928 static boolean ChangeElementNow(int x, int y, int element, int page)
7930 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7932 int old_element = Feld[x][y];
7934 /* always use default change event to prevent running into a loop */
7935 if (ChangeEvent[x][y] == -1)
7936 ChangeEvent[x][y] = CE_DELAY;
7938 if (ChangeEvent[x][y] == CE_DELAY)
7940 /* reset actual trigger element and player */
7941 change->actual_trigger_element = EL_EMPTY;
7942 change->actual_trigger_player = EL_PLAYER_1;
7946 /* do not change any elements that have already changed in this frame */
7950 /* do not change already changed elements with same change event */
7951 if (Changed[x][y] & ChangeEvent[x][y])
7956 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7958 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7962 /* !!! indirect change before direct change !!! */
7963 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
7966 if (change->explode)
7973 if (change->use_target_content)
7975 boolean complete_replace = TRUE;
7976 boolean can_replace[3][3];
7979 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7982 boolean is_walkable;
7983 boolean is_diggable;
7984 boolean is_collectible;
7985 boolean is_removable;
7986 boolean is_destructible;
7987 int ex = x + xx - 1;
7988 int ey = y + yy - 1;
7989 int content_element = change->target_content[xx][yy];
7992 can_replace[xx][yy] = TRUE;
7994 if (ex == x && ey == y) /* do not check changing element itself */
7997 if (content_element == EL_EMPTY_SPACE)
7999 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8004 if (!IN_LEV_FIELD(ex, ey))
8006 can_replace[xx][yy] = FALSE;
8007 complete_replace = FALSE;
8013 if (Changed[ex][ey]) /* do not change already changed elements */
8015 can_replace[xx][yy] = FALSE;
8016 complete_replace = FALSE;
8024 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8025 e = MovingOrBlocked2Element(ex, ey);
8030 is_empty = (IS_FREE(ex, ey) ||
8031 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8032 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8033 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8037 is_empty = (IS_FREE(ex, ey) ||
8038 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8040 is_empty = (IS_FREE(ex, ey) ||
8041 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8046 is_walkable = (is_empty || IS_WALKABLE(e));
8047 is_diggable = (is_empty || IS_DIGGABLE(e));
8048 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8049 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8050 is_removable = (is_diggable || is_collectible);
8052 can_replace[xx][yy] =
8053 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8054 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8055 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8056 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8057 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8058 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8059 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8061 if (!can_replace[xx][yy])
8062 complete_replace = FALSE;
8064 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8065 IS_WALKABLE(content_element)));
8067 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8069 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8072 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8073 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8074 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8076 can_replace[xx][yy] = FALSE;
8077 complete_replace = FALSE;
8082 if (!change->only_if_complete || complete_replace)
8084 boolean something_has_changed = FALSE;
8086 if (change->only_if_complete && change->use_random_replace &&
8087 RND(100) < change->random_percentage)
8090 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8092 int ex = x + xx - 1;
8093 int ey = y + yy - 1;
8094 int content_element;
8096 if (can_replace[xx][yy] && (!change->use_random_replace ||
8097 RND(100) < change->random_percentage))
8099 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8100 RemoveMovingField(ex, ey);
8102 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8104 content_element = change->target_content[xx][yy];
8105 target_element = GET_TARGET_ELEMENT(content_element, change);
8107 ChangeElementNowExt(ex, ey, target_element);
8109 something_has_changed = TRUE;
8111 /* for symmetry reasons, freeze newly created border elements */
8112 if (ex != x || ey != y)
8113 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8117 if (something_has_changed)
8118 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8123 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8125 ChangeElementNowExt(x, y, target_element);
8127 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8131 /* this uses direct change before indirect change */
8132 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8138 static void ChangeElement(int x, int y, int page)
8140 int element = MovingOrBlocked2Element(x, y);
8141 struct ElementInfo *ei = &element_info[element];
8142 struct ElementChangeInfo *change = &ei->change_page[page];
8145 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8148 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8149 x, y, element, element_info[element].token_name);
8150 printf("ChangeElement(): This should never happen!\n");
8155 /* this can happen with classic bombs on walkable, changing elements */
8156 if (!CAN_CHANGE(element))
8159 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8160 ChangeDelay[x][y] = 0;
8166 if (ChangeDelay[x][y] == 0) /* initialize element change */
8168 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8169 RND(change->delay_random * change->delay_frames)) + 1;
8171 ResetGfxAnimation(x, y);
8172 ResetRandomAnimationValue(x, y);
8174 if (change->pre_change_function)
8175 change->pre_change_function(x, y);
8178 ChangeDelay[x][y]--;
8180 if (ChangeDelay[x][y] != 0) /* continue element change */
8182 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8184 if (IS_ANIMATED(graphic))
8185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8187 if (change->change_function)
8188 change->change_function(x, y);
8190 else /* finish element change */
8192 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8194 page = ChangePage[x][y];
8195 ChangePage[x][y] = -1;
8197 change = &ei->change_page[page];
8201 if (IS_MOVING(x, y) && !change->explode)
8203 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8206 ChangeDelay[x][y] = 1; /* try change after next move step */
8207 ChangePage[x][y] = page; /* remember page to use for change */
8212 if (ChangeElementNow(x, y, element, page))
8214 if (change->post_change_function)
8215 change->post_change_function(x, y);
8220 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8221 int trigger_element,
8228 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8230 if (!(trigger_events[trigger_element][trigger_event]))
8233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8235 int element = EL_CUSTOM_START + i;
8237 boolean change_element = FALSE;
8240 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8243 for (j = 0; j < element_info[element].num_change_pages; j++)
8245 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8247 if (change->can_change &&
8248 change->has_event[trigger_event] &&
8249 change->trigger_side & trigger_side &&
8250 change->trigger_player & trigger_player &&
8251 change->trigger_page & trigger_page_bits &&
8252 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8255 if (!(change->has_event[trigger_event]))
8256 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8257 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8260 change_element = TRUE;
8263 change->actual_trigger_element = trigger_element;
8264 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8270 if (!change_element)
8273 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8276 if (x == lx && y == ly) /* do not change trigger element itself */
8280 if (Feld[x][y] == element)
8282 ChangeDelay[x][y] = 1;
8283 ChangeEvent[x][y] = trigger_event;
8284 ChangeElement(x, y, page);
8292 static boolean CheckElementChangeExt(int x, int y,
8294 int trigger_element,
8300 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8303 if (Feld[x][y] == EL_BLOCKED)
8305 Blocked2Moving(x, y, &x, &y);
8306 element = Feld[x][y];
8310 if (Feld[x][y] != element) /* check if element has already changed */
8313 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8314 Feld[x][y], element_info[Feld[x][y]].token_name,
8315 element, element_info[element].token_name,
8324 if (trigger_page < 0)
8326 boolean change_element = FALSE;
8329 for (i = 0; i < element_info[element].num_change_pages; i++)
8331 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8333 if (change->can_change &&
8334 change->has_event[trigger_event] &&
8335 change->trigger_side & trigger_side &&
8336 change->trigger_player & trigger_player)
8338 change_element = TRUE;
8341 change->actual_trigger_element = trigger_element;
8342 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8348 if (!change_element)
8353 struct ElementInfo *ei = &element_info[element];
8354 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8356 change->actual_trigger_element = trigger_element;
8357 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8362 /* !!! this check misses pages with same event, but different side !!! */
8364 if (trigger_page < 0)
8365 trigger_page = element_info[element].event_page_nr[trigger_event];
8367 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8371 ChangeDelay[x][y] = 1;
8372 ChangeEvent[x][y] = trigger_event;
8373 ChangeElement(x, y, trigger_page);
8378 static void PlayPlayerSound(struct PlayerInfo *player)
8380 int jx = player->jx, jy = player->jy;
8381 int element = player->element_nr;
8382 int last_action = player->last_action_waiting;
8383 int action = player->action_waiting;
8385 if (player->is_waiting)
8387 if (action != last_action)
8388 PlayLevelSoundElementAction(jx, jy, element, action);
8390 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8394 if (action != last_action)
8395 StopSound(element_info[element].sound[last_action]);
8397 if (last_action == ACTION_SLEEPING)
8398 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8402 static void PlayAllPlayersSound()
8406 for (i = 0; i < MAX_PLAYERS; i++)
8407 if (stored_player[i].active)
8408 PlayPlayerSound(&stored_player[i]);
8411 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8413 boolean last_waiting = player->is_waiting;
8414 int move_dir = player->MovDir;
8416 player->last_action_waiting = player->action_waiting;
8420 if (!last_waiting) /* not waiting -> waiting */
8422 player->is_waiting = TRUE;
8424 player->frame_counter_bored =
8426 game.player_boring_delay_fixed +
8427 SimpleRND(game.player_boring_delay_random);
8428 player->frame_counter_sleeping =
8430 game.player_sleeping_delay_fixed +
8431 SimpleRND(game.player_sleeping_delay_random);
8433 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8436 if (game.player_sleeping_delay_fixed +
8437 game.player_sleeping_delay_random > 0 &&
8438 player->anim_delay_counter == 0 &&
8439 player->post_delay_counter == 0 &&
8440 FrameCounter >= player->frame_counter_sleeping)
8441 player->is_sleeping = TRUE;
8442 else if (game.player_boring_delay_fixed +
8443 game.player_boring_delay_random > 0 &&
8444 FrameCounter >= player->frame_counter_bored)
8445 player->is_bored = TRUE;
8447 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8448 player->is_bored ? ACTION_BORING :
8451 if (player->is_sleeping)
8453 if (player->num_special_action_sleeping > 0)
8455 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8457 int last_special_action = player->special_action_sleeping;
8458 int num_special_action = player->num_special_action_sleeping;
8459 int special_action =
8460 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8461 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8462 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8463 last_special_action + 1 : ACTION_SLEEPING);
8464 int special_graphic =
8465 el_act_dir2img(player->element_nr, special_action, move_dir);
8467 player->anim_delay_counter =
8468 graphic_info[special_graphic].anim_delay_fixed +
8469 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8470 player->post_delay_counter =
8471 graphic_info[special_graphic].post_delay_fixed +
8472 SimpleRND(graphic_info[special_graphic].post_delay_random);
8474 player->special_action_sleeping = special_action;
8477 if (player->anim_delay_counter > 0)
8479 player->action_waiting = player->special_action_sleeping;
8480 player->anim_delay_counter--;
8482 else if (player->post_delay_counter > 0)
8484 player->post_delay_counter--;
8488 else if (player->is_bored)
8490 if (player->num_special_action_bored > 0)
8492 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8494 int special_action =
8495 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8496 int special_graphic =
8497 el_act_dir2img(player->element_nr, special_action, move_dir);
8499 player->anim_delay_counter =
8500 graphic_info[special_graphic].anim_delay_fixed +
8501 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8502 player->post_delay_counter =
8503 graphic_info[special_graphic].post_delay_fixed +
8504 SimpleRND(graphic_info[special_graphic].post_delay_random);
8506 player->special_action_bored = special_action;
8509 if (player->anim_delay_counter > 0)
8511 player->action_waiting = player->special_action_bored;
8512 player->anim_delay_counter--;
8514 else if (player->post_delay_counter > 0)
8516 player->post_delay_counter--;
8521 else if (last_waiting) /* waiting -> not waiting */
8523 player->is_waiting = FALSE;
8524 player->is_bored = FALSE;
8525 player->is_sleeping = FALSE;
8527 player->frame_counter_bored = -1;
8528 player->frame_counter_sleeping = -1;
8530 player->anim_delay_counter = 0;
8531 player->post_delay_counter = 0;
8533 player->action_waiting = ACTION_DEFAULT;
8535 player->special_action_bored = ACTION_DEFAULT;
8536 player->special_action_sleeping = ACTION_DEFAULT;
8541 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8544 static byte stored_player_action[MAX_PLAYERS];
8545 static int num_stored_actions = 0;
8547 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8548 int left = player_action & JOY_LEFT;
8549 int right = player_action & JOY_RIGHT;
8550 int up = player_action & JOY_UP;
8551 int down = player_action & JOY_DOWN;
8552 int button1 = player_action & JOY_BUTTON_1;
8553 int button2 = player_action & JOY_BUTTON_2;
8554 int dx = (left ? -1 : right ? 1 : 0);
8555 int dy = (up ? -1 : down ? 1 : 0);
8558 stored_player_action[player->index_nr] = 0;
8559 num_stored_actions++;
8563 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8566 if (!player->active || tape.pausing)
8570 printf("::: [%d %d %d %d] [%d %d]\n",
8571 left, right, up, down, button1, button2);
8577 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8582 if (player->MovPos == 0)
8583 CheckGravityMovement(player);
8586 snapped = SnapField(player, dx, dy);
8590 dropped = DropElement(player);
8592 moved = MovePlayer(player, dx, dy);
8595 if (tape.single_step && tape.recording && !tape.pausing)
8597 if (button1 || (dropped && !moved))
8599 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8600 SnapField(player, 0, 0); /* stop snapping */
8604 SetPlayerWaiting(player, FALSE);
8607 return player_action;
8609 stored_player_action[player->index_nr] = player_action;
8615 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8618 /* no actions for this player (no input at player's configured device) */
8620 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8621 SnapField(player, 0, 0);
8622 CheckGravityMovementWhenNotMoving(player);
8624 if (player->MovPos == 0)
8625 SetPlayerWaiting(player, TRUE);
8627 if (player->MovPos == 0) /* needed for tape.playing */
8628 player->is_moving = FALSE;
8630 player->is_dropping = FALSE;
8636 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8638 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8640 TapeRecordAction(stored_player_action);
8641 num_stored_actions = 0;
8648 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8650 static byte stored_player_action[MAX_PLAYERS];
8651 static int num_stored_actions = 0;
8652 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8653 int left = player_action & JOY_LEFT;
8654 int right = player_action & JOY_RIGHT;
8655 int up = player_action & JOY_UP;
8656 int down = player_action & JOY_DOWN;
8657 int button1 = player_action & JOY_BUTTON_1;
8658 int button2 = player_action & JOY_BUTTON_2;
8659 int dx = (left ? -1 : right ? 1 : 0);
8660 int dy = (up ? -1 : down ? 1 : 0);
8662 stored_player_action[player->index_nr] = 0;
8663 num_stored_actions++;
8665 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8667 if (!player->active || tape.pausing)
8672 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8675 snapped = SnapField(player, dx, dy);
8679 dropped = DropElement(player);
8681 moved = MovePlayer(player, dx, dy);
8684 if (tape.single_step && tape.recording && !tape.pausing)
8686 if (button1 || (dropped && !moved))
8688 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8689 SnapField(player, 0, 0); /* stop snapping */
8693 stored_player_action[player->index_nr] = player_action;
8697 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8699 /* no actions for this player (no input at player's configured device) */
8701 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8702 SnapField(player, 0, 0);
8703 CheckGravityMovementWhenNotMoving(player);
8705 if (player->MovPos == 0)
8706 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8708 if (player->MovPos == 0) /* needed for tape.playing */
8709 player->is_moving = FALSE;
8712 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8714 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8716 TapeRecordAction(stored_player_action);
8717 num_stored_actions = 0;
8722 void AdvanceFrameAndPlayerCounters(int player_nr)
8726 /* advance frame counters (global frame counter and time frame counter) */
8730 /* advance player counters (counters for move delay, move animation etc.) */
8731 for (i = 0; i < MAX_PLAYERS; i++)
8733 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8735 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8737 if (!advance_player_counters) /* not all players may be affected */
8740 stored_player[i].Frame += move_frames;
8742 if (stored_player[i].MovPos != 0)
8743 stored_player[i].StepFrame += move_frames;
8745 #if USE_NEW_MOVE_DELAY
8746 if (stored_player[i].move_delay > 0)
8747 stored_player[i].move_delay--;
8750 #if USE_NEW_PUSH_DELAY
8751 /* due to bugs in previous versions, counter must count up, not down */
8752 if (stored_player[i].push_delay != -1)
8753 stored_player[i].push_delay++;
8756 if (stored_player[i].drop_delay > 0)
8757 stored_player[i].drop_delay--;
8763 static unsigned long game_frame_delay = 0;
8764 unsigned long game_frame_delay_value;
8765 int magic_wall_x = 0, magic_wall_y = 0;
8766 int i, x, y, element, graphic;
8767 byte *recorded_player_action;
8768 byte summarized_player_action = 0;
8770 byte tape_action[MAX_PLAYERS];
8773 if (game_status != GAME_MODE_PLAYING)
8776 game_frame_delay_value =
8777 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8779 if (tape.playing && tape.warp_forward && !tape.pausing)
8780 game_frame_delay_value = 0;
8782 /* ---------- main game synchronization point ---------- */
8784 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8786 if (network_playing && !network_player_action_received)
8790 printf("DEBUG: try to get network player actions in time\n");
8794 #if defined(NETWORK_AVALIABLE)
8795 /* last chance to get network player actions without main loop delay */
8799 if (game_status != GAME_MODE_PLAYING)
8802 if (!network_player_action_received)
8806 printf("DEBUG: failed to get network player actions in time\n");
8817 printf("::: getting new tape action [%d]\n", FrameCounter);
8820 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8823 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8824 if (recorded_player_action == NULL && tape.pausing)
8829 printf("::: %d\n", stored_player[0].action);
8833 if (recorded_player_action != NULL)
8834 for (i = 0; i < MAX_PLAYERS; i++)
8835 stored_player[i].action = recorded_player_action[i];
8838 for (i = 0; i < MAX_PLAYERS; i++)
8840 summarized_player_action |= stored_player[i].action;
8842 if (!network_playing)
8843 stored_player[i].effective_action = stored_player[i].action;
8846 #if defined(NETWORK_AVALIABLE)
8847 if (network_playing)
8848 SendToServer_MovePlayer(summarized_player_action);
8851 if (!options.network && !setup.team_mode)
8852 local_player->effective_action = summarized_player_action;
8855 if (recorded_player_action != NULL)
8856 for (i = 0; i < MAX_PLAYERS; i++)
8857 stored_player[i].effective_action = recorded_player_action[i];
8861 for (i = 0; i < MAX_PLAYERS; i++)
8863 tape_action[i] = stored_player[i].effective_action;
8865 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8866 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8869 /* only save actions from input devices, but not programmed actions */
8871 TapeRecordAction(tape_action);
8874 for (i = 0; i < MAX_PLAYERS; i++)
8876 int actual_player_action = stored_player[i].effective_action;
8879 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8880 - rnd_equinox_tetrachloride 048
8881 - rnd_equinox_tetrachloride_ii 096
8882 - rnd_emanuel_schmieg 002
8883 - doctor_sloan_ww 001, 020
8885 if (stored_player[i].MovPos == 0)
8886 CheckGravityMovement(&stored_player[i]);
8890 /* overwrite programmed action with tape action */
8891 if (stored_player[i].programmed_action)
8892 actual_player_action = stored_player[i].programmed_action;
8896 if (stored_player[i].programmed_action)
8897 printf("::: %d\n", stored_player[i].programmed_action);
8900 if (recorded_player_action)
8903 if (stored_player[i].programmed_action &&
8904 stored_player[i].programmed_action != recorded_player_action[i])
8905 printf("::: %d: %d <-> %d\n", i,
8906 stored_player[i].programmed_action, recorded_player_action[i]);
8910 actual_player_action = recorded_player_action[i];
8915 /* overwrite tape action with programmed action */
8916 if (stored_player[i].programmed_action)
8917 actual_player_action = stored_player[i].programmed_action;
8922 printf("::: action: %d: %x [%d]\n",
8923 stored_player[i].MovPos, actual_player_action, FrameCounter);
8927 PlayerActions(&stored_player[i], actual_player_action);
8929 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8931 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8932 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8935 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8940 TapeRecordAction(tape_action);
8943 network_player_action_received = FALSE;
8945 ScrollScreen(NULL, SCROLL_GO_ON);
8951 for (i = 0; i < MAX_PLAYERS; i++)
8952 stored_player[i].Frame++;
8956 /* for backwards compatibility, the following code emulates a fixed bug that
8957 occured when pushing elements (causing elements that just made their last
8958 pushing step to already (if possible) make their first falling step in the
8959 same game frame, which is bad); this code is also needed to use the famous
8960 "spring push bug" which is used in older levels and might be wanted to be
8961 used also in newer levels, but in this case the buggy pushing code is only
8962 affecting the "spring" element and no other elements */
8965 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8967 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8970 for (i = 0; i < MAX_PLAYERS; i++)
8972 struct PlayerInfo *player = &stored_player[i];
8977 if (player->active && player->is_pushing && player->is_moving &&
8979 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8980 Feld[x][y] == EL_SPRING))
8982 if (player->active && player->is_pushing && player->is_moving &&
8986 ContinueMoving(x, y);
8988 /* continue moving after pushing (this is actually a bug) */
8989 if (!IS_MOVING(x, y))
8998 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9000 Changed[x][y] = FALSE;
9001 ChangeEvent[x][y] = -1;
9003 #if USE_NEW_BLOCK_STYLE
9004 /* this must be handled before main playfield loop */
9005 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9008 if (MovDelay[x][y] <= 0)
9014 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9016 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9017 printf("GameActions(): This should never happen!\n");
9019 ChangePage[x][y] = -1;
9024 if (WasJustMoving[x][y] > 0)
9025 WasJustMoving[x][y]--;
9026 if (WasJustFalling[x][y] > 0)
9027 WasJustFalling[x][y]--;
9028 if (CheckCollision[x][y] > 0)
9029 CheckCollision[x][y]--;
9034 /* reset finished pushing action (not done in ContinueMoving() to allow
9035 continous pushing animation for elements with zero push delay) */
9036 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9038 ResetGfxAnimation(x, y);
9039 DrawLevelField(x, y);
9044 if (IS_BLOCKED(x, y))
9048 Blocked2Moving(x, y, &oldx, &oldy);
9049 if (!IS_MOVING(oldx, oldy))
9051 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9052 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9053 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9054 printf("GameActions(): This should never happen!\n");
9060 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9062 element = Feld[x][y];
9064 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9066 graphic = el2img(element);
9072 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9074 element = graphic = 0;
9078 if (graphic_info[graphic].anim_global_sync)
9079 GfxFrame[x][y] = FrameCounter;
9081 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9082 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9083 ResetRandomAnimationValue(x, y);
9085 SetRandomAnimationValue(x, y);
9088 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9091 if (IS_INACTIVE(element))
9093 if (IS_ANIMATED(graphic))
9094 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9100 /* this may take place after moving, so 'element' may have changed */
9102 if (IS_CHANGING(x, y))
9104 if (IS_CHANGING(x, y) &&
9105 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9109 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9110 element_info[element].event_page_nr[CE_DELAY]);
9112 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9115 element = Feld[x][y];
9116 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9120 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9125 element = Feld[x][y];
9126 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9128 if (element == EL_MOLE)
9129 printf("::: %d, %d, %d [%d]\n",
9130 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9134 if (element == EL_YAMYAM)
9135 printf("::: %d, %d, %d\n",
9136 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9140 if (IS_ANIMATED(graphic) &&
9144 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9147 if (element == EL_BUG)
9148 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9152 if (element == EL_MOLE)
9153 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9157 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9158 EdelsteinFunkeln(x, y);
9160 else if ((element == EL_ACID ||
9161 element == EL_EXIT_OPEN ||
9162 element == EL_SP_EXIT_OPEN ||
9163 element == EL_SP_TERMINAL ||
9164 element == EL_SP_TERMINAL_ACTIVE ||
9165 element == EL_EXTRA_TIME ||
9166 element == EL_SHIELD_NORMAL ||
9167 element == EL_SHIELD_DEADLY) &&
9168 IS_ANIMATED(graphic))
9169 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9170 else if (IS_MOVING(x, y))
9171 ContinueMoving(x, y);
9172 else if (IS_ACTIVE_BOMB(element))
9173 CheckDynamite(x, y);
9175 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9176 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9178 else if (element == EL_AMOEBA_GROWING)
9179 AmoebeWaechst(x, y);
9180 else if (element == EL_AMOEBA_SHRINKING)
9181 AmoebaDisappearing(x, y);
9183 #if !USE_NEW_AMOEBA_CODE
9184 else if (IS_AMOEBALIVE(element))
9185 AmoebeAbleger(x, y);
9188 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9190 else if (element == EL_EXIT_CLOSED)
9192 else if (element == EL_SP_EXIT_CLOSED)
9194 else if (element == EL_EXPANDABLE_WALL_GROWING)
9196 else if (element == EL_EXPANDABLE_WALL ||
9197 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9198 element == EL_EXPANDABLE_WALL_VERTICAL ||
9199 element == EL_EXPANDABLE_WALL_ANY)
9201 else if (element == EL_FLAMES)
9202 CheckForDragon(x, y);
9204 else if (IS_AUTO_CHANGING(element))
9205 ChangeElement(x, y);
9207 else if (element == EL_EXPLOSION)
9208 ; /* drawing of correct explosion animation is handled separately */
9209 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9210 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9213 /* this may take place after moving, so 'element' may have changed */
9214 if (IS_AUTO_CHANGING(Feld[x][y]))
9215 ChangeElement(x, y);
9218 if (IS_BELT_ACTIVE(element))
9219 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9221 if (game.magic_wall_active)
9223 int jx = local_player->jx, jy = local_player->jy;
9225 /* play the element sound at the position nearest to the player */
9226 if ((element == EL_MAGIC_WALL_FULL ||
9227 element == EL_MAGIC_WALL_ACTIVE ||
9228 element == EL_MAGIC_WALL_EMPTYING ||
9229 element == EL_BD_MAGIC_WALL_FULL ||
9230 element == EL_BD_MAGIC_WALL_ACTIVE ||
9231 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9232 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9240 #if USE_NEW_AMOEBA_CODE
9241 /* new experimental amoeba growth stuff */
9243 if (!(FrameCounter % 8))
9246 static unsigned long random = 1684108901;
9248 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9251 x = (random >> 10) % lev_fieldx;
9252 y = (random >> 20) % lev_fieldy;
9254 x = RND(lev_fieldx);
9255 y = RND(lev_fieldy);
9257 element = Feld[x][y];
9260 if (!IS_PLAYER(x,y) &&
9261 (element == EL_EMPTY ||
9262 CAN_GROW_INTO(element) ||
9263 element == EL_QUICKSAND_EMPTY ||
9264 element == EL_ACID_SPLASH_LEFT ||
9265 element == EL_ACID_SPLASH_RIGHT))
9267 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9268 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9269 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9270 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9271 Feld[x][y] = EL_AMOEBA_DROP;
9274 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9275 if (!IS_PLAYER(x,y) &&
9276 (element == EL_EMPTY ||
9277 element == EL_SAND ||
9278 element == EL_QUICKSAND_EMPTY ||
9279 element == EL_ACID_SPLASH_LEFT ||
9280 element == EL_ACID_SPLASH_RIGHT))
9282 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9283 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9284 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9285 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9286 Feld[x][y] = EL_AMOEBA_DROP;
9290 random = random * 129 + 1;
9296 if (game.explosions_delayed)
9299 game.explosions_delayed = FALSE;
9301 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9303 element = Feld[x][y];
9305 if (ExplodeField[x][y])
9306 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9307 else if (element == EL_EXPLOSION)
9308 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9310 ExplodeField[x][y] = EX_TYPE_NONE;
9313 game.explosions_delayed = TRUE;
9316 if (game.magic_wall_active)
9318 if (!(game.magic_wall_time_left % 4))
9320 int element = Feld[magic_wall_x][magic_wall_y];
9322 if (element == EL_BD_MAGIC_WALL_FULL ||
9323 element == EL_BD_MAGIC_WALL_ACTIVE ||
9324 element == EL_BD_MAGIC_WALL_EMPTYING)
9325 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9327 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9330 if (game.magic_wall_time_left > 0)
9332 game.magic_wall_time_left--;
9333 if (!game.magic_wall_time_left)
9335 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9337 element = Feld[x][y];
9339 if (element == EL_MAGIC_WALL_ACTIVE ||
9340 element == EL_MAGIC_WALL_FULL)
9342 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9343 DrawLevelField(x, y);
9345 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9346 element == EL_BD_MAGIC_WALL_FULL)
9348 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9349 DrawLevelField(x, y);
9353 game.magic_wall_active = FALSE;
9358 if (game.light_time_left > 0)
9360 game.light_time_left--;
9362 if (game.light_time_left == 0)
9363 RedrawAllLightSwitchesAndInvisibleElements();
9366 if (game.timegate_time_left > 0)
9368 game.timegate_time_left--;
9370 if (game.timegate_time_left == 0)
9371 CloseAllOpenTimegates();
9374 for (i = 0; i < MAX_PLAYERS; i++)
9376 struct PlayerInfo *player = &stored_player[i];
9378 if (SHIELD_ON(player))
9380 if (player->shield_deadly_time_left)
9381 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9382 else if (player->shield_normal_time_left)
9383 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9387 if (TimeFrames >= FRAMES_PER_SECOND)
9392 for (i = 0; i < MAX_PLAYERS; i++)
9394 struct PlayerInfo *player = &stored_player[i];
9396 if (SHIELD_ON(player))
9398 player->shield_normal_time_left--;
9400 if (player->shield_deadly_time_left > 0)
9401 player->shield_deadly_time_left--;
9405 if (!level.use_step_counter)
9413 if (TimeLeft <= 10 && setup.time_limit)
9414 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9416 DrawGameValue_Time(TimeLeft);
9418 if (!TimeLeft && setup.time_limit)
9419 for (i = 0; i < MAX_PLAYERS; i++)
9420 KillHero(&stored_player[i]);
9422 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9423 DrawGameValue_Time(TimePlayed);
9426 if (tape.recording || tape.playing)
9427 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9431 PlayAllPlayersSound();
9433 if (options.debug) /* calculate frames per second */
9435 static unsigned long fps_counter = 0;
9436 static int fps_frames = 0;
9437 unsigned long fps_delay_ms = Counter() - fps_counter;
9441 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9443 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9446 fps_counter = Counter();
9449 redraw_mask |= REDRAW_FPS;
9453 if (stored_player[0].jx != stored_player[0].last_jx ||
9454 stored_player[0].jy != stored_player[0].last_jy)
9455 printf("::: %d, %d, %d, %d, %d\n",
9456 stored_player[0].MovDir,
9457 stored_player[0].MovPos,
9458 stored_player[0].GfxPos,
9459 stored_player[0].Frame,
9460 stored_player[0].StepFrame);
9463 #if USE_NEW_MOVE_DELAY
9464 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9469 for (i = 0; i < MAX_PLAYERS; i++)
9472 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9474 stored_player[i].Frame += move_frames;
9476 if (stored_player[i].MovPos != 0)
9477 stored_player[i].StepFrame += move_frames;
9479 #if USE_NEW_MOVE_DELAY
9480 if (stored_player[i].move_delay > 0)
9481 stored_player[i].move_delay--;
9484 if (stored_player[i].drop_delay > 0)
9485 stored_player[i].drop_delay--;
9490 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9492 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9494 local_player->show_envelope = 0;
9498 #if USE_NEW_RANDOMIZE
9499 /* use random number generator in every frame to make it less predictable */
9500 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9505 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9507 int min_x = x, min_y = y, max_x = x, max_y = y;
9510 for (i = 0; i < MAX_PLAYERS; i++)
9512 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9514 if (!stored_player[i].active || &stored_player[i] == player)
9517 min_x = MIN(min_x, jx);
9518 min_y = MIN(min_y, jy);
9519 max_x = MAX(max_x, jx);
9520 max_y = MAX(max_y, jy);
9523 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9526 static boolean AllPlayersInVisibleScreen()
9530 for (i = 0; i < MAX_PLAYERS; i++)
9532 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9534 if (!stored_player[i].active)
9537 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9544 void ScrollLevel(int dx, int dy)
9546 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9549 BlitBitmap(drawto_field, drawto_field,
9550 FX + TILEX * (dx == -1) - softscroll_offset,
9551 FY + TILEY * (dy == -1) - softscroll_offset,
9552 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9553 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9554 FX + TILEX * (dx == 1) - softscroll_offset,
9555 FY + TILEY * (dy == 1) - softscroll_offset);
9559 x = (dx == 1 ? BX1 : BX2);
9560 for (y = BY1; y <= BY2; y++)
9561 DrawScreenField(x, y);
9566 y = (dy == 1 ? BY1 : BY2);
9567 for (x = BX1; x <= BX2; x++)
9568 DrawScreenField(x, y);
9571 redraw_mask |= REDRAW_FIELD;
9575 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9577 int nextx = x + dx, nexty = y + dy;
9578 int element = Feld[x][y];
9581 element != EL_SP_PORT_LEFT &&
9582 element != EL_SP_GRAVITY_PORT_LEFT &&
9583 element != EL_SP_PORT_HORIZONTAL &&
9584 element != EL_SP_PORT_ANY) ||
9586 element != EL_SP_PORT_RIGHT &&
9587 element != EL_SP_GRAVITY_PORT_RIGHT &&
9588 element != EL_SP_PORT_HORIZONTAL &&
9589 element != EL_SP_PORT_ANY) ||
9591 element != EL_SP_PORT_UP &&
9592 element != EL_SP_GRAVITY_PORT_UP &&
9593 element != EL_SP_PORT_VERTICAL &&
9594 element != EL_SP_PORT_ANY) ||
9596 element != EL_SP_PORT_DOWN &&
9597 element != EL_SP_GRAVITY_PORT_DOWN &&
9598 element != EL_SP_PORT_VERTICAL &&
9599 element != EL_SP_PORT_ANY) ||
9600 !IN_LEV_FIELD(nextx, nexty) ||
9601 !IS_FREE(nextx, nexty))
9608 static boolean canFallDown(struct PlayerInfo *player)
9610 int jx = player->jx, jy = player->jy;
9612 return (IN_LEV_FIELD(jx, jy + 1) &&
9613 (IS_FREE(jx, jy + 1) ||
9614 #if USE_NEW_BLOCK_STYLE
9615 #if USE_GRAVITY_BUGFIX_OLD
9616 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9619 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9620 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9621 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9624 static boolean canPassField(int x, int y, int move_dir)
9626 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9627 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9628 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9631 int element = Feld[x][y];
9633 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9634 !CAN_MOVE(element) &&
9635 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9636 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9637 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9640 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9642 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9643 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9644 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9648 int nextx = newx + dx;
9649 int nexty = newy + dy;
9653 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9654 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9656 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9658 (IS_DIGGABLE(Feld[newx][newy]) ||
9659 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9660 canPassField(newx, newy, move_dir)));
9663 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9664 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9665 (IS_DIGGABLE(Feld[newx][newy]) ||
9666 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9667 canPassField(newx, newy, move_dir)));
9670 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9671 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9672 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9673 canPassField(newx, newy, move_dir)));
9675 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9676 (IS_DIGGABLE(Feld[newx][newy]) ||
9677 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9678 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9679 !CAN_MOVE(Feld[newx][newy]) &&
9680 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9681 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9682 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9688 static void CheckGravityMovement(struct PlayerInfo *player)
9690 if (game.gravity && !player->programmed_action)
9693 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9694 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9696 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9697 int move_dir_vertical = player->action & MV_VERTICAL;
9701 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9703 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9706 int jx = player->jx, jy = player->jy;
9708 boolean player_is_moving_to_valid_field =
9709 (!player_is_snapping &&
9710 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9711 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9715 (player->last_move_dir & MV_HORIZONTAL ?
9716 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9717 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9721 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9722 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9723 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9724 int new_jx = jx + dx, new_jy = jy + dy;
9725 int nextx = new_jx + dx, nexty = new_jy + dy;
9731 boolean player_can_fall_down = canFallDown(player);
9733 boolean player_can_fall_down =
9734 (IN_LEV_FIELD(jx, jy + 1) &&
9735 (IS_FREE(jx, jy + 1) ||
9736 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9740 boolean player_can_fall_down =
9741 (IN_LEV_FIELD(jx, jy + 1) &&
9742 (IS_FREE(jx, jy + 1)));
9746 boolean player_is_moving_to_valid_field =
9749 !player_is_snapping &&
9753 IN_LEV_FIELD(new_jx, new_jy) &&
9754 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9755 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9756 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9757 IN_LEV_FIELD(nextx, nexty) &&
9758 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9760 IN_LEV_FIELD(new_jx, new_jy) &&
9761 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9762 Feld[new_jx][new_jy] == EL_SAND ||
9763 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9764 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9765 /* !!! extend EL_SAND to anything diggable !!! */
9771 boolean player_is_standing_on_valid_field =
9772 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9773 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9777 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9778 player_can_fall_down,
9779 player_is_standing_on_valid_field,
9780 player_is_moving_to_valid_field,
9781 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9782 player->effective_action,
9783 player->can_fall_into_acid);
9786 if (player_can_fall_down &&
9788 !player_is_standing_on_valid_field &&
9790 !player_is_moving_to_valid_field)
9793 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9794 jx, jy, FrameCounter);
9797 player->programmed_action = MV_DOWN;
9802 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9805 return CheckGravityMovement(player);
9808 if (game.gravity && !player->programmed_action)
9810 int jx = player->jx, jy = player->jy;
9811 boolean field_under_player_is_free =
9812 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9813 boolean player_is_standing_on_valid_field =
9814 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9815 (IS_WALKABLE(Feld[jx][jy]) &&
9816 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9818 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9819 player->programmed_action = MV_DOWN;
9825 -----------------------------------------------------------------------------
9826 dx, dy: direction (non-diagonal) to try to move the player to
9827 real_dx, real_dy: direction as read from input device (can be diagonal)
9830 boolean MovePlayerOneStep(struct PlayerInfo *player,
9831 int dx, int dy, int real_dx, int real_dy)
9834 static int trigger_sides[4][2] =
9836 /* enter side leave side */
9837 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9838 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9839 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9840 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9842 int move_direction = (dx == -1 ? MV_LEFT :
9843 dx == +1 ? MV_RIGHT :
9845 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9846 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9847 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9849 int jx = player->jx, jy = player->jy;
9850 int new_jx = jx + dx, new_jy = jy + dy;
9854 if (!player->active || (!dx && !dy))
9855 return MF_NO_ACTION;
9857 player->MovDir = (dx < 0 ? MV_LEFT :
9860 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9862 if (!IN_LEV_FIELD(new_jx, new_jy))
9863 return MF_NO_ACTION;
9865 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9866 return MF_NO_ACTION;
9869 element = MovingOrBlocked2Element(new_jx, new_jy);
9871 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9874 if (DONT_RUN_INTO(element))
9876 if (element == EL_ACID && dx == 0 && dy == 1)
9878 SplashAcid(new_jx, new_jy);
9879 Feld[jx][jy] = EL_PLAYER_1;
9880 InitMovingField(jx, jy, MV_DOWN);
9881 Store[jx][jy] = EL_ACID;
9882 ContinueMoving(jx, jy);
9886 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9891 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9892 if (can_move != MF_MOVING)
9895 /* check if DigField() has caused relocation of the player */
9896 if (player->jx != jx || player->jy != jy)
9897 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9899 StorePlayer[jx][jy] = 0;
9900 player->last_jx = jx;
9901 player->last_jy = jy;
9902 player->jx = new_jx;
9903 player->jy = new_jy;
9904 StorePlayer[new_jx][new_jy] = player->element_nr;
9907 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9909 player->step_counter++;
9912 player->drop_delay = 0;
9915 PlayerVisit[jx][jy] = FrameCounter;
9917 ScrollPlayer(player, SCROLL_INIT);
9920 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9922 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9924 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9927 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9929 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9930 CE_PLAYER_ENTERS_X, enter_side);
9931 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9932 CE_ENTERED_BY_PLAYER, enter_side);
9939 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9941 int jx = player->jx, jy = player->jy;
9942 int old_jx = jx, old_jy = jy;
9943 int moved = MF_NO_ACTION;
9946 if (!player->active)
9951 if (player->MovPos == 0)
9953 player->is_moving = FALSE;
9954 player->is_digging = FALSE;
9955 player->is_collecting = FALSE;
9956 player->is_snapping = FALSE;
9957 player->is_pushing = FALSE;
9963 if (!player->active || (!dx && !dy))
9968 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9976 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9977 player->move_delay + player->move_delay_value);
9980 #if USE_NEW_MOVE_DELAY
9981 if (player->move_delay > 0)
9983 if (!FrameReached(&player->move_delay, player->move_delay_value))
9987 printf("::: can NOT move\n");
9993 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9994 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10001 printf("::: COULD move now\n");
10004 #if USE_NEW_MOVE_DELAY
10005 player->move_delay = -1; /* set to "uninitialized" value */
10008 /* store if player is automatically moved to next field */
10009 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10011 /* remove the last programmed player action */
10012 player->programmed_action = 0;
10014 if (player->MovPos)
10016 /* should only happen if pre-1.2 tape recordings are played */
10017 /* this is only for backward compatibility */
10019 int original_move_delay_value = player->move_delay_value;
10022 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10026 /* scroll remaining steps with finest movement resolution */
10027 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10029 while (player->MovPos)
10031 ScrollPlayer(player, SCROLL_GO_ON);
10032 ScrollScreen(NULL, SCROLL_GO_ON);
10034 #if USE_NEW_MOVE_DELAY
10035 AdvanceFrameAndPlayerCounters(player->index_nr);
10044 player->move_delay_value = original_move_delay_value;
10047 if (player->last_move_dir & MV_HORIZONTAL)
10049 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10050 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10054 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10055 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10061 if (moved & MF_MOVING && !ScreenMovPos &&
10062 (player == local_player || !options.network))
10064 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10065 int offset = (setup.scroll_delay ? 3 : 0);
10067 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10069 /* actual player has left the screen -- scroll in that direction */
10070 if (jx != old_jx) /* player has moved horizontally */
10071 scroll_x += (jx - old_jx);
10072 else /* player has moved vertically */
10073 scroll_y += (jy - old_jy);
10077 if (jx != old_jx) /* player has moved horizontally */
10079 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10080 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10081 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10083 /* don't scroll over playfield boundaries */
10084 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10085 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10087 /* don't scroll more than one field at a time */
10088 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10090 /* don't scroll against the player's moving direction */
10091 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10092 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10093 scroll_x = old_scroll_x;
10095 else /* player has moved vertically */
10097 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10098 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10099 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10101 /* don't scroll over playfield boundaries */
10102 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10103 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10105 /* don't scroll more than one field at a time */
10106 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10108 /* don't scroll against the player's moving direction */
10109 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10110 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10111 scroll_y = old_scroll_y;
10115 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10117 if (!options.network && !AllPlayersInVisibleScreen())
10119 scroll_x = old_scroll_x;
10120 scroll_y = old_scroll_y;
10124 ScrollScreen(player, SCROLL_INIT);
10125 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10132 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10134 if (!(moved & MF_MOVING) && !player->is_pushing)
10139 player->StepFrame = 0;
10141 if (moved & MF_MOVING)
10144 printf("::: REALLY moves now\n");
10147 if (old_jx != jx && old_jy == jy)
10148 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10149 else if (old_jx == jx && old_jy != jy)
10150 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10152 DrawLevelField(jx, jy); /* for "crumbled sand" */
10154 player->last_move_dir = player->MovDir;
10155 player->is_moving = TRUE;
10157 player->is_snapping = FALSE;
10161 player->is_switching = FALSE;
10164 player->is_dropping = FALSE;
10168 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10171 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10174 int move_direction = player->MovDir;
10176 int enter_side = MV_DIR_OPPOSITE(move_direction);
10177 int leave_side = move_direction;
10179 static int trigger_sides[4][2] =
10181 /* enter side leave side */
10182 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10183 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10184 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10185 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10187 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10188 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10190 int old_element = Feld[old_jx][old_jy];
10191 int new_element = Feld[jx][jy];
10194 /* !!! TEST ONLY !!! */
10195 if (IS_CUSTOM_ELEMENT(old_element))
10196 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10198 player->index_bit, leave_side);
10200 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10201 CE_PLAYER_LEAVES_X,
10202 player->index_bit, leave_side);
10204 if (IS_CUSTOM_ELEMENT(new_element))
10205 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10206 player->index_bit, enter_side);
10208 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10209 CE_PLAYER_ENTERS_X,
10210 player->index_bit, enter_side);
10220 CheckGravityMovementWhenNotMoving(player);
10223 player->last_move_dir = MV_NO_MOVING;
10225 player->is_moving = FALSE;
10227 #if USE_NEW_MOVE_STYLE
10228 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10229 /* ensure that the player is also allowed to move in the next frame */
10230 /* (currently, the player is forced to wait eight frames before he can try
10233 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10234 player->move_delay = 0; /* allow direct movement in the next frame */
10238 #if USE_NEW_MOVE_DELAY
10239 if (player->move_delay == -1) /* not yet initialized by DigField() */
10240 player->move_delay = player->move_delay_value;
10243 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10245 TestIfHeroTouchesBadThing(jx, jy);
10246 TestIfPlayerTouchesCustomElement(jx, jy);
10249 if (!player->active)
10250 RemoveHero(player);
10255 void ScrollPlayer(struct PlayerInfo *player, int mode)
10257 int jx = player->jx, jy = player->jy;
10258 int last_jx = player->last_jx, last_jy = player->last_jy;
10259 int move_stepsize = TILEX / player->move_delay_value;
10261 if (!player->active || !player->MovPos)
10264 if (mode == SCROLL_INIT)
10266 player->actual_frame_counter = FrameCounter;
10267 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10270 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10272 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10273 player->block_delay);
10276 #if USE_NEW_BLOCK_STYLE
10279 if (player->block_delay <= 0)
10280 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10283 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10284 Feld[last_jx][last_jy] == EL_EMPTY)
10286 int last_field_block_delay = 0; /* start with no blocking at all */
10287 int block_delay_adjustment = player->block_delay_adjustment;
10289 /* if player blocks last field, add delay for exactly one move */
10290 if (player->block_last_field)
10292 last_field_block_delay += player->move_delay_value;
10294 #if USE_GRAVITY_BUGFIX_NEW
10295 /* when blocking enabled, prevent moving up despite gravity */
10296 if (game.gravity && player->MovDir == MV_UP)
10297 block_delay_adjustment = -1;
10301 /* add block delay adjustment (also possible when not blocking) */
10302 last_field_block_delay += block_delay_adjustment;
10305 #if USE_BLOCK_DELAY_BUGFIX
10306 /* when blocking enabled, correct block delay for fast movement */
10307 if (player->block_last_field &&
10308 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10309 last_field_block_delay =
10310 player->move_delay_value + player->block_delay_adjustment;
10315 #if USE_GRAVITY_BUGFIX_NEW
10316 /* when blocking enabled, correct block delay for gravity movement */
10317 if (player->block_last_field &&
10318 game.gravity && player->MovDir == MV_UP)
10319 last_field_block_delay = player->move_delay_value - 1;
10323 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10324 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10327 #if USE_NEW_MOVE_STYLE
10328 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10329 player->block_last_field) &&
10330 Feld[last_jx][last_jy] == EL_EMPTY)
10331 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10333 if (Feld[last_jx][last_jy] == EL_EMPTY)
10334 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10339 DrawPlayer(player);
10344 else if (!FrameReached(&player->actual_frame_counter, 1))
10347 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10348 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10350 #if USE_NEW_BLOCK_STYLE
10352 if (!player->block_last_field &&
10353 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10355 RemoveField(last_jx, last_jy);
10357 Feld[last_jx][last_jy] = EL_EMPTY;
10361 /* before DrawPlayer() to draw correct player graphic for this case */
10362 if (player->MovPos == 0)
10363 CheckGravityMovement(player);
10366 DrawPlayer(player); /* needed here only to cleanup last field */
10369 if (player->MovPos == 0) /* player reached destination field */
10372 if (player->move_delay_reset_counter > 0)
10374 player->move_delay_reset_counter--;
10376 if (player->move_delay_reset_counter == 0)
10378 /* continue with normal speed after quickly moving through gate */
10379 HALVE_PLAYER_SPEED(player);
10381 /* be able to make the next move without delay */
10382 player->move_delay = 0;
10386 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10388 /* continue with normal speed after quickly moving through gate */
10389 HALVE_PLAYER_SPEED(player);
10391 /* be able to make the next move without delay */
10392 player->move_delay = 0;
10396 #if USE_NEW_BLOCK_STYLE
10398 if (player->block_last_field &&
10399 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10401 RemoveField(last_jx, last_jy);
10403 Feld[last_jx][last_jy] = EL_EMPTY;
10407 player->last_jx = jx;
10408 player->last_jy = jy;
10410 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10411 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10412 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10414 DrawPlayer(player); /* needed here only to cleanup last field */
10415 RemoveHero(player);
10417 if (local_player->friends_still_needed == 0 ||
10418 IS_SP_ELEMENT(Feld[jx][jy]))
10419 player->LevelSolved = player->GameOver = TRUE;
10423 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10424 /* this breaks one level: "machine", level 000 */
10426 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10429 int move_direction = player->MovDir;
10431 int enter_side = MV_DIR_OPPOSITE(move_direction);
10432 int leave_side = move_direction;
10434 static int trigger_sides[4][2] =
10436 /* enter side leave side */
10437 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10438 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10439 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10440 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10442 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10443 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10445 int old_jx = last_jx;
10446 int old_jy = last_jy;
10447 int old_element = Feld[old_jx][old_jy];
10448 int new_element = Feld[jx][jy];
10451 /* !!! TEST ONLY !!! */
10452 if (IS_CUSTOM_ELEMENT(old_element))
10453 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10455 player->index_bit, leave_side);
10457 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10458 CE_PLAYER_LEAVES_X,
10459 player->index_bit, leave_side);
10461 if (IS_CUSTOM_ELEMENT(new_element))
10462 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10463 player->index_bit, enter_side);
10465 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10466 CE_PLAYER_ENTERS_X,
10467 player->index_bit, enter_side);
10473 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10475 TestIfHeroTouchesBadThing(jx, jy);
10476 TestIfPlayerTouchesCustomElement(jx, jy);
10479 /* needed because pushed element has not yet reached its destination,
10480 so it would trigger a change event at its previous field location */
10481 if (!player->is_pushing)
10483 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10486 if (!player->active)
10487 RemoveHero(player);
10490 if (level.use_step_counter)
10500 if (TimeLeft <= 10 && setup.time_limit)
10501 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10503 DrawGameValue_Time(TimeLeft);
10505 if (!TimeLeft && setup.time_limit)
10506 for (i = 0; i < MAX_PLAYERS; i++)
10507 KillHero(&stored_player[i]);
10509 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10510 DrawGameValue_Time(TimePlayed);
10513 if (tape.single_step && tape.recording && !tape.pausing &&
10514 !player->programmed_action)
10515 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10519 void ScrollScreen(struct PlayerInfo *player, int mode)
10521 static unsigned long screen_frame_counter = 0;
10523 if (mode == SCROLL_INIT)
10525 /* set scrolling step size according to actual player's moving speed */
10526 ScrollStepSize = TILEX / player->move_delay_value;
10528 screen_frame_counter = FrameCounter;
10529 ScreenMovDir = player->MovDir;
10530 ScreenMovPos = player->MovPos;
10531 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10534 else if (!FrameReached(&screen_frame_counter, 1))
10539 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10540 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10541 redraw_mask |= REDRAW_FIELD;
10544 ScreenMovDir = MV_NO_MOVING;
10547 void TestIfPlayerTouchesCustomElement(int x, int y)
10549 static int xy[4][2] =
10556 static int trigger_sides[4][2] =
10558 /* center side border side */
10559 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10560 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10561 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10562 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10564 static int touch_dir[4] =
10566 MV_LEFT | MV_RIGHT,
10571 int center_element = Feld[x][y]; /* should always be non-moving! */
10574 for (i = 0; i < NUM_DIRECTIONS; i++)
10576 int xx = x + xy[i][0];
10577 int yy = y + xy[i][1];
10578 int center_side = trigger_sides[i][0];
10579 int border_side = trigger_sides[i][1];
10580 int border_element;
10582 if (!IN_LEV_FIELD(xx, yy))
10585 if (IS_PLAYER(x, y))
10587 struct PlayerInfo *player = PLAYERINFO(x, y);
10589 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10590 border_element = Feld[xx][yy]; /* may be moving! */
10591 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10592 border_element = Feld[xx][yy];
10593 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10594 border_element = MovingOrBlocked2Element(xx, yy);
10596 continue; /* center and border element do not touch */
10599 /* !!! TEST ONLY !!! */
10600 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10601 player->index_bit, border_side);
10602 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10603 CE_PLAYER_TOUCHES_X,
10604 player->index_bit, border_side);
10606 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10607 CE_PLAYER_TOUCHES_X,
10608 player->index_bit, border_side);
10609 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10610 player->index_bit, border_side);
10613 else if (IS_PLAYER(xx, yy))
10615 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10617 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10619 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10620 continue; /* center and border element do not touch */
10624 /* !!! TEST ONLY !!! */
10625 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10626 player->index_bit, center_side);
10627 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10628 CE_PLAYER_TOUCHES_X,
10629 player->index_bit, center_side);
10631 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10632 CE_PLAYER_TOUCHES_X,
10633 player->index_bit, center_side);
10634 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10635 player->index_bit, center_side);
10643 void TestIfElementTouchesCustomElement(int x, int y)
10645 static int xy[4][2] =
10652 static int trigger_sides[4][2] =
10654 /* center side border side */
10655 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10656 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10657 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10658 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10660 static int touch_dir[4] =
10662 MV_LEFT | MV_RIGHT,
10667 boolean change_center_element = FALSE;
10668 int center_element_change_page = 0;
10669 int center_element = Feld[x][y]; /* should always be non-moving! */
10670 int border_trigger_element = EL_UNDEFINED;
10673 for (i = 0; i < NUM_DIRECTIONS; i++)
10675 int xx = x + xy[i][0];
10676 int yy = y + xy[i][1];
10677 int center_side = trigger_sides[i][0];
10678 int border_side = trigger_sides[i][1];
10679 int border_element;
10681 if (!IN_LEV_FIELD(xx, yy))
10684 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10685 border_element = Feld[xx][yy]; /* may be moving! */
10686 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10687 border_element = Feld[xx][yy];
10688 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10689 border_element = MovingOrBlocked2Element(xx, yy);
10691 continue; /* center and border element do not touch */
10693 /* check for change of center element (but change it only once) */
10694 if (IS_CUSTOM_ELEMENT(center_element) &&
10695 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10696 !change_center_element)
10698 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10700 struct ElementChangeInfo *change =
10701 &element_info[center_element].change_page[j];
10703 if (change->can_change &&
10704 change->has_event[CE_TOUCHING_X] &&
10705 change->trigger_side & border_side &&
10707 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10709 change->trigger_element == border_element
10713 change_center_element = TRUE;
10714 center_element_change_page = j;
10715 border_trigger_element = border_element;
10722 /* check for change of border element */
10723 if (IS_CUSTOM_ELEMENT(border_element) &&
10724 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10726 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10728 struct ElementChangeInfo *change =
10729 &element_info[border_element].change_page[j];
10731 if (change->can_change &&
10732 change->has_event[CE_TOUCHING_X] &&
10733 change->trigger_side & center_side &&
10735 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10737 change->trigger_element == center_element
10742 printf("::: border_element %d, %d\n", x, y);
10745 CheckElementChangeByPage(xx, yy, border_element, center_element,
10753 if (change_center_element)
10756 printf("::: center_element %d, %d\n", x, y);
10759 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10760 CE_TOUCHING_X, center_element_change_page);
10764 void TestIfElementHitsCustomElement(int x, int y, int direction)
10766 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10767 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10768 int hitx = x + dx, hity = y + dy;
10769 int hitting_element = Feld[x][y];
10770 int touched_element;
10772 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10773 !IS_FREE(hitx, hity) &&
10774 (!IS_MOVING(hitx, hity) ||
10775 MovDir[hitx][hity] != direction ||
10776 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10779 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10783 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10787 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10788 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10790 #if !USE_HITTING_SOMETHING_BUGFIX
10791 /* "hitting something" is also true when hitting the playfield border */
10792 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10793 CE_HITTING_SOMETHING, direction);
10796 if (IN_LEV_FIELD(hitx, hity))
10798 int opposite_direction = MV_DIR_OPPOSITE(direction);
10799 int hitting_side = direction;
10800 int touched_side = opposite_direction;
10802 int touched_element = MovingOrBlocked2Element(hitx, hity);
10805 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10806 MovDir[hitx][hity] != direction ||
10807 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10816 #if !USE_HIT_BY_SOMETHING_BUGFIX
10817 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10818 CE_HIT_BY_SOMETHING, opposite_direction);
10821 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10822 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10824 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10826 struct ElementChangeInfo *change =
10827 &element_info[hitting_element].change_page[i];
10829 if (change->can_change &&
10830 change->has_event[CE_HITTING_X] &&
10831 change->trigger_side & touched_side &&
10834 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10836 change->trigger_element == touched_element
10840 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10847 if (IS_CUSTOM_ELEMENT(touched_element) &&
10848 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10850 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10852 struct ElementChangeInfo *change =
10853 &element_info[touched_element].change_page[i];
10855 if (change->can_change &&
10856 change->has_event[CE_HIT_BY_X] &&
10857 change->trigger_side & hitting_side &&
10859 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10861 change->trigger_element == hitting_element
10865 CheckElementChangeByPage(hitx, hity, touched_element,
10866 hitting_element, CE_HIT_BY_X, i);
10872 #if USE_HIT_BY_SOMETHING_BUGFIX
10873 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10874 CE_HIT_BY_SOMETHING, opposite_direction);
10879 #if USE_HITTING_SOMETHING_BUGFIX
10880 /* "hitting something" is also true when hitting the playfield border */
10881 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10882 CE_HITTING_SOMETHING, direction);
10887 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10889 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10890 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10891 int hitx = x + dx, hity = y + dy;
10892 int hitting_element = Feld[x][y];
10893 int touched_element;
10895 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10896 !IS_FREE(hitx, hity) &&
10897 (!IS_MOVING(hitx, hity) ||
10898 MovDir[hitx][hity] != direction ||
10899 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10902 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10906 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10910 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10911 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10913 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10914 EP_CAN_SMASH_EVERYTHING, direction);
10916 if (IN_LEV_FIELD(hitx, hity))
10918 int opposite_direction = MV_DIR_OPPOSITE(direction);
10919 int hitting_side = direction;
10920 int touched_side = opposite_direction;
10922 int touched_element = MovingOrBlocked2Element(hitx, hity);
10925 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10926 MovDir[hitx][hity] != direction ||
10927 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10936 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10937 CE_SMASHED_BY_SOMETHING, opposite_direction);
10939 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10940 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10942 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10944 struct ElementChangeInfo *change =
10945 &element_info[hitting_element].change_page[i];
10947 if (change->can_change &&
10948 change->has_event[CE_OTHER_IS_SMASHING] &&
10949 change->trigger_side & touched_side &&
10952 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10954 change->trigger_element == touched_element
10958 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10959 CE_OTHER_IS_SMASHING, i);
10965 if (IS_CUSTOM_ELEMENT(touched_element) &&
10966 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10968 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10970 struct ElementChangeInfo *change =
10971 &element_info[touched_element].change_page[i];
10973 if (change->can_change &&
10974 change->has_event[CE_OTHER_GETS_SMASHED] &&
10975 change->trigger_side & hitting_side &&
10977 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10979 change->trigger_element == hitting_element
10983 CheckElementChangeByPage(hitx, hity, touched_element,
10984 hitting_element, CE_OTHER_GETS_SMASHED,i);
10994 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10996 int i, kill_x = -1, kill_y = -1;
10997 int bad_element = -1;
10998 static int test_xy[4][2] =
11005 static int test_dir[4] =
11013 for (i = 0; i < NUM_DIRECTIONS; i++)
11015 int test_x, test_y, test_move_dir, test_element;
11017 test_x = good_x + test_xy[i][0];
11018 test_y = good_y + test_xy[i][1];
11020 if (!IN_LEV_FIELD(test_x, test_y))
11024 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11027 test_element = Feld[test_x][test_y];
11029 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11032 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11033 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11035 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11036 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11040 bad_element = test_element;
11046 if (kill_x != -1 || kill_y != -1)
11048 if (IS_PLAYER(good_x, good_y))
11050 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11053 if (player->shield_deadly_time_left > 0 &&
11054 !IS_INDESTRUCTIBLE(bad_element))
11055 Bang(kill_x, kill_y);
11056 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11059 if (player->shield_deadly_time_left > 0)
11060 Bang(kill_x, kill_y);
11061 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11066 Bang(good_x, good_y);
11070 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11072 int i, kill_x = -1, kill_y = -1;
11073 int bad_element = Feld[bad_x][bad_y];
11074 static int test_xy[4][2] =
11081 static int touch_dir[4] =
11083 MV_LEFT | MV_RIGHT,
11088 static int test_dir[4] =
11096 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11099 for (i = 0; i < NUM_DIRECTIONS; i++)
11101 int test_x, test_y, test_move_dir, test_element;
11103 test_x = bad_x + test_xy[i][0];
11104 test_y = bad_y + test_xy[i][1];
11105 if (!IN_LEV_FIELD(test_x, test_y))
11109 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11111 test_element = Feld[test_x][test_y];
11113 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11114 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11116 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11117 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11119 /* good thing is player or penguin that does not move away */
11120 if (IS_PLAYER(test_x, test_y))
11122 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11124 if (bad_element == EL_ROBOT && player->is_moving)
11125 continue; /* robot does not kill player if he is moving */
11127 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11129 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11130 continue; /* center and border element do not touch */
11137 else if (test_element == EL_PENGUIN)
11146 if (kill_x != -1 || kill_y != -1)
11148 if (IS_PLAYER(kill_x, kill_y))
11150 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11153 if (player->shield_deadly_time_left > 0 &&
11154 !IS_INDESTRUCTIBLE(bad_element))
11155 Bang(bad_x, bad_y);
11156 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11159 if (player->shield_deadly_time_left > 0)
11160 Bang(bad_x, bad_y);
11161 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11166 Bang(kill_x, kill_y);
11170 void TestIfHeroTouchesBadThing(int x, int y)
11172 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11175 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11177 TestIfGoodThingHitsBadThing(x, y, move_dir);
11180 void TestIfBadThingTouchesHero(int x, int y)
11182 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11185 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11187 TestIfBadThingHitsGoodThing(x, y, move_dir);
11190 void TestIfFriendTouchesBadThing(int x, int y)
11192 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11195 void TestIfBadThingTouchesFriend(int x, int y)
11197 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11200 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11202 int i, kill_x = bad_x, kill_y = bad_y;
11203 static int xy[4][2] =
11211 for (i = 0; i < NUM_DIRECTIONS; i++)
11215 x = bad_x + xy[i][0];
11216 y = bad_y + xy[i][1];
11217 if (!IN_LEV_FIELD(x, y))
11220 element = Feld[x][y];
11221 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11222 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11230 if (kill_x != bad_x || kill_y != bad_y)
11231 Bang(bad_x, bad_y);
11234 void KillHero(struct PlayerInfo *player)
11236 int jx = player->jx, jy = player->jy;
11238 if (!player->active)
11241 /* remove accessible field at the player's position */
11242 Feld[jx][jy] = EL_EMPTY;
11244 /* deactivate shield (else Bang()/Explode() would not work right) */
11245 player->shield_normal_time_left = 0;
11246 player->shield_deadly_time_left = 0;
11252 static void KillHeroUnlessEnemyProtected(int x, int y)
11254 if (!PLAYER_ENEMY_PROTECTED(x, y))
11255 KillHero(PLAYERINFO(x, y));
11258 static void KillHeroUnlessExplosionProtected(int x, int y)
11260 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11261 KillHero(PLAYERINFO(x, y));
11264 void BuryHero(struct PlayerInfo *player)
11266 int jx = player->jx, jy = player->jy;
11268 if (!player->active)
11272 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11274 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11276 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11278 player->GameOver = TRUE;
11279 RemoveHero(player);
11282 void RemoveHero(struct PlayerInfo *player)
11284 int jx = player->jx, jy = player->jy;
11285 int i, found = FALSE;
11287 player->present = FALSE;
11288 player->active = FALSE;
11290 if (!ExplodeField[jx][jy])
11291 StorePlayer[jx][jy] = 0;
11293 for (i = 0; i < MAX_PLAYERS; i++)
11294 if (stored_player[i].active)
11298 AllPlayersGone = TRUE;
11305 =============================================================================
11306 checkDiagonalPushing()
11307 -----------------------------------------------------------------------------
11308 check if diagonal input device direction results in pushing of object
11309 (by checking if the alternative direction is walkable, diggable, ...)
11310 =============================================================================
11313 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11314 int x, int y, int real_dx, int real_dy)
11316 int jx, jy, dx, dy, xx, yy;
11318 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11321 /* diagonal direction: check alternative direction */
11326 xx = jx + (dx == 0 ? real_dx : 0);
11327 yy = jy + (dy == 0 ? real_dy : 0);
11329 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11333 =============================================================================
11335 -----------------------------------------------------------------------------
11336 x, y: field next to player (non-diagonal) to try to dig to
11337 real_dx, real_dy: direction as read from input device (can be diagonal)
11338 =============================================================================
11341 int DigField(struct PlayerInfo *player,
11342 int oldx, int oldy, int x, int y,
11343 int real_dx, int real_dy, int mode)
11346 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11348 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11349 boolean player_was_pushing = player->is_pushing;
11350 int jx = oldx, jy = oldy;
11351 int dx = x - jx, dy = y - jy;
11352 int nextx = x + dx, nexty = y + dy;
11353 int move_direction = (dx == -1 ? MV_LEFT :
11354 dx == +1 ? MV_RIGHT :
11356 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11357 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11359 int dig_side = MV_DIR_OPPOSITE(move_direction);
11361 static int trigger_sides[4] =
11363 CH_SIDE_RIGHT, /* moving left */
11364 CH_SIDE_LEFT, /* moving right */
11365 CH_SIDE_BOTTOM, /* moving up */
11366 CH_SIDE_TOP, /* moving down */
11368 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11370 int old_element = Feld[jx][jy];
11373 if (is_player) /* function can also be called by EL_PENGUIN */
11375 if (player->MovPos == 0)
11377 player->is_digging = FALSE;
11378 player->is_collecting = FALSE;
11381 if (player->MovPos == 0) /* last pushing move finished */
11382 player->is_pushing = FALSE;
11384 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11386 player->is_switching = FALSE;
11387 #if USE_NEW_PUSH_DELAY
11388 player->push_delay = -1;
11390 player->push_delay = 0;
11393 return MF_NO_ACTION;
11397 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11398 return MF_NO_ACTION;
11403 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11405 if (IS_TUBE(Feld[jx][jy]) ||
11406 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11410 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11411 int tube_leave_directions[][2] =
11413 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11414 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11415 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11416 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11417 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11418 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11419 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11420 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11421 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11422 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11423 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11424 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11427 while (tube_leave_directions[i][0] != tube_element)
11430 if (tube_leave_directions[i][0] == -1) /* should not happen */
11434 if (!(tube_leave_directions[i][1] & move_direction))
11435 return MF_NO_ACTION; /* tube has no opening in this direction */
11440 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11441 old_element = Back[jx][jy];
11445 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11446 return MF_NO_ACTION; /* field has no opening in this direction */
11448 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11449 return MF_NO_ACTION; /* field has no opening in this direction */
11451 element = Feld[x][y];
11453 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11454 return MF_NO_ACTION;
11456 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11457 game.engine_version >= VERSION_IDENT(2,2,0,0))
11458 return MF_NO_ACTION;
11461 if (game.gravity && is_player && !player->is_auto_moving &&
11462 canFallDown(player) && move_direction != MV_DOWN &&
11463 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11464 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11468 if (element == EL_EMPTY_SPACE &&
11469 game.gravity && !player->is_auto_moving &&
11470 canFallDown(player) && move_direction != MV_DOWN)
11471 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11477 case EL_SP_PORT_LEFT:
11478 case EL_SP_PORT_RIGHT:
11479 case EL_SP_PORT_UP:
11480 case EL_SP_PORT_DOWN:
11481 case EL_SP_PORT_HORIZONTAL:
11482 case EL_SP_PORT_VERTICAL:
11483 case EL_SP_PORT_ANY:
11484 case EL_SP_GRAVITY_PORT_LEFT:
11485 case EL_SP_GRAVITY_PORT_RIGHT:
11486 case EL_SP_GRAVITY_PORT_UP:
11487 case EL_SP_GRAVITY_PORT_DOWN:
11489 if (!canEnterSupaplexPort(x, y, dx, dy))
11490 return MF_NO_ACTION;
11493 element != EL_SP_PORT_LEFT &&
11494 element != EL_SP_GRAVITY_PORT_LEFT &&
11495 element != EL_SP_PORT_HORIZONTAL &&
11496 element != EL_SP_PORT_ANY) ||
11498 element != EL_SP_PORT_RIGHT &&
11499 element != EL_SP_GRAVITY_PORT_RIGHT &&
11500 element != EL_SP_PORT_HORIZONTAL &&
11501 element != EL_SP_PORT_ANY) ||
11503 element != EL_SP_PORT_UP &&
11504 element != EL_SP_GRAVITY_PORT_UP &&
11505 element != EL_SP_PORT_VERTICAL &&
11506 element != EL_SP_PORT_ANY) ||
11508 element != EL_SP_PORT_DOWN &&
11509 element != EL_SP_GRAVITY_PORT_DOWN &&
11510 element != EL_SP_PORT_VERTICAL &&
11511 element != EL_SP_PORT_ANY) ||
11512 !IN_LEV_FIELD(nextx, nexty) ||
11513 !IS_FREE(nextx, nexty))
11514 return MF_NO_ACTION;
11517 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11518 element == EL_SP_GRAVITY_PORT_RIGHT ||
11519 element == EL_SP_GRAVITY_PORT_UP ||
11520 element == EL_SP_GRAVITY_PORT_DOWN)
11521 game.gravity = !game.gravity;
11523 /* automatically move to the next field with double speed */
11524 player->programmed_action = move_direction;
11526 if (player->move_delay_reset_counter == 0)
11528 player->move_delay_reset_counter = 2; /* two double speed steps */
11530 DOUBLE_PLAYER_SPEED(player);
11533 player->move_delay_reset_counter = 2;
11535 DOUBLE_PLAYER_SPEED(player);
11539 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11542 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11548 case EL_TUBE_VERTICAL:
11549 case EL_TUBE_HORIZONTAL:
11550 case EL_TUBE_VERTICAL_LEFT:
11551 case EL_TUBE_VERTICAL_RIGHT:
11552 case EL_TUBE_HORIZONTAL_UP:
11553 case EL_TUBE_HORIZONTAL_DOWN:
11554 case EL_TUBE_LEFT_UP:
11555 case EL_TUBE_LEFT_DOWN:
11556 case EL_TUBE_RIGHT_UP:
11557 case EL_TUBE_RIGHT_DOWN:
11560 int tube_enter_directions[][2] =
11562 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11563 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11564 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11565 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11566 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11567 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11568 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11569 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11570 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11571 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11572 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11573 { -1, MV_NO_MOVING }
11576 while (tube_enter_directions[i][0] != element)
11579 if (tube_enter_directions[i][0] == -1) /* should not happen */
11583 if (!(tube_enter_directions[i][1] & move_direction))
11584 return MF_NO_ACTION; /* tube has no opening in this direction */
11586 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11594 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11596 if (IS_WALKABLE(element))
11599 int sound_element = SND_ELEMENT(element);
11600 int sound_action = ACTION_WALKING;
11603 if (!ACCESS_FROM(element, opposite_direction))
11604 return MF_NO_ACTION; /* field not accessible from this direction */
11608 if (element == EL_EMPTY_SPACE &&
11609 game.gravity && !player->is_auto_moving &&
11610 canFallDown(player) && move_direction != MV_DOWN)
11611 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11614 if (IS_RND_GATE(element))
11616 if (!player->key[RND_GATE_NR(element)])
11617 return MF_NO_ACTION;
11619 else if (IS_RND_GATE_GRAY(element))
11621 if (!player->key[RND_GATE_GRAY_NR(element)])
11622 return MF_NO_ACTION;
11624 else if (element == EL_EXIT_OPEN ||
11625 element == EL_SP_EXIT_OPEN ||
11626 element == EL_SP_EXIT_OPENING)
11628 sound_action = ACTION_PASSING; /* player is passing exit */
11630 else if (element == EL_EMPTY)
11632 sound_action = ACTION_MOVING; /* nothing to walk on */
11635 /* play sound from background or player, whatever is available */
11636 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11637 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11639 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11644 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11646 else if (IS_PASSABLE(element))
11650 if (!canPassField(x, y, move_direction))
11651 return MF_NO_ACTION;
11656 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11657 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11658 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11659 return MF_NO_ACTION;
11661 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11662 return MF_NO_ACTION;
11667 if (!ACCESS_FROM(element, opposite_direction))
11668 return MF_NO_ACTION; /* field not accessible from this direction */
11670 if (IS_CUSTOM_ELEMENT(element) &&
11671 !ACCESS_FROM(element, opposite_direction))
11672 return MF_NO_ACTION; /* field not accessible from this direction */
11676 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11677 return MF_NO_ACTION;
11682 if (IS_EM_GATE(element))
11684 if (!player->key[EM_GATE_NR(element)])
11685 return MF_NO_ACTION;
11687 else if (IS_EM_GATE_GRAY(element))
11689 if (!player->key[EM_GATE_GRAY_NR(element)])
11690 return MF_NO_ACTION;
11692 else if (IS_SP_PORT(element))
11694 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11695 element == EL_SP_GRAVITY_PORT_RIGHT ||
11696 element == EL_SP_GRAVITY_PORT_UP ||
11697 element == EL_SP_GRAVITY_PORT_DOWN)
11698 game.gravity = !game.gravity;
11699 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11700 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11701 element == EL_SP_GRAVITY_ON_PORT_UP ||
11702 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11703 game.gravity = TRUE;
11704 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11705 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11706 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11707 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11708 game.gravity = FALSE;
11711 /* automatically move to the next field with double speed */
11712 player->programmed_action = move_direction;
11714 if (player->move_delay_reset_counter == 0)
11716 player->move_delay_reset_counter = 2; /* two double speed steps */
11718 DOUBLE_PLAYER_SPEED(player);
11721 player->move_delay_reset_counter = 2;
11723 DOUBLE_PLAYER_SPEED(player);
11726 PlayLevelSoundAction(x, y, ACTION_PASSING);
11730 else if (IS_DIGGABLE(element))
11734 if (mode != DF_SNAP)
11737 GfxElement[x][y] = GFX_ELEMENT(element);
11740 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11742 player->is_digging = TRUE;
11745 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11747 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11748 player->index_bit, dig_side);
11751 if (mode == DF_SNAP)
11752 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11757 else if (IS_COLLECTIBLE(element))
11761 if (is_player && mode != DF_SNAP)
11763 GfxElement[x][y] = element;
11764 player->is_collecting = TRUE;
11767 if (element == EL_SPEED_PILL)
11768 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11769 else if (element == EL_EXTRA_TIME && level.time > 0)
11772 DrawGameValue_Time(TimeLeft);
11774 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11776 player->shield_normal_time_left += 10;
11777 if (element == EL_SHIELD_DEADLY)
11778 player->shield_deadly_time_left += 10;
11780 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11782 if (player->inventory_size < MAX_INVENTORY_SIZE)
11783 player->inventory_element[player->inventory_size++] = element;
11785 DrawGameValue_Dynamite(local_player->inventory_size);
11787 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11789 player->dynabomb_count++;
11790 player->dynabombs_left++;
11792 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11794 player->dynabomb_size++;
11796 else if (element == EL_DYNABOMB_INCREASE_POWER)
11798 player->dynabomb_xl = TRUE;
11800 else if (IS_KEY(element))
11802 player->key[KEY_NR(element)] = TRUE;
11804 DrawGameValue_Keys(player->key);
11806 redraw_mask |= REDRAW_DOOR_1;
11808 else if (IS_ENVELOPE(element))
11811 player->show_envelope = element;
11813 ShowEnvelope(element - EL_ENVELOPE_1);
11816 else if (IS_DROPPABLE(element) ||
11817 IS_THROWABLE(element)) /* can be collected and dropped */
11821 if (element_info[element].collect_count == 0)
11822 player->inventory_infinite_element = element;
11824 for (i = 0; i < element_info[element].collect_count; i++)
11825 if (player->inventory_size < MAX_INVENTORY_SIZE)
11826 player->inventory_element[player->inventory_size++] = element;
11828 DrawGameValue_Dynamite(local_player->inventory_size);
11830 else if (element_info[element].collect_count > 0)
11832 local_player->gems_still_needed -=
11833 element_info[element].collect_count;
11834 if (local_player->gems_still_needed < 0)
11835 local_player->gems_still_needed = 0;
11837 DrawGameValue_Emeralds(local_player->gems_still_needed);
11840 RaiseScoreElement(element);
11841 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11844 CheckTriggeredElementChangeByPlayer(x, y, element,
11845 CE_PLAYER_COLLECTS_X,
11846 player->index_bit, dig_side);
11849 if (mode == DF_SNAP)
11850 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11855 else if (IS_PUSHABLE(element))
11857 if (mode == DF_SNAP && element != EL_BD_ROCK)
11858 return MF_NO_ACTION;
11860 if (CAN_FALL(element) && dy)
11861 return MF_NO_ACTION;
11863 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11864 !(element == EL_SPRING && level.use_spring_bug))
11865 return MF_NO_ACTION;
11868 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11869 ((move_direction & MV_VERTICAL &&
11870 ((element_info[element].move_pattern & MV_LEFT &&
11871 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11872 (element_info[element].move_pattern & MV_RIGHT &&
11873 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11874 (move_direction & MV_HORIZONTAL &&
11875 ((element_info[element].move_pattern & MV_UP &&
11876 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11877 (element_info[element].move_pattern & MV_DOWN &&
11878 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11879 return MF_NO_ACTION;
11883 /* do not push elements already moving away faster than player */
11884 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11885 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11886 return MF_NO_ACTION;
11888 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11889 return MF_NO_ACTION;
11895 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11897 if (player->push_delay_value == -1 || !player_was_pushing)
11898 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11900 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11902 if (player->push_delay_value == -1)
11903 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11906 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11908 if (player->push_delay_value == -1 || !player_was_pushing)
11909 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11912 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11914 if (!player->is_pushing)
11915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11919 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11920 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11921 !player_is_pushing))
11922 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11925 if (!player->is_pushing &&
11926 game.engine_version >= VERSION_IDENT(2,2,0,7))
11927 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11931 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11932 player->push_delay, player->push_delay_value,
11933 FrameCounter, game.engine_version,
11934 player_was_pushing, player->is_pushing,
11935 element, element_info[element].token_name,
11936 GET_NEW_PUSH_DELAY(element));
11939 player->is_pushing = TRUE;
11941 if (!(IN_LEV_FIELD(nextx, nexty) &&
11942 (IS_FREE(nextx, nexty) ||
11943 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11944 IS_SB_ELEMENT(element)))))
11945 return MF_NO_ACTION;
11947 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11948 return MF_NO_ACTION;
11950 #if USE_NEW_PUSH_DELAY
11953 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11954 printf("::: ALERT: %d, %d [%d / %d]\n",
11955 player->push_delay, player->push_delay2,
11956 FrameCounter, FrameCounter / 50);
11959 if (player->push_delay == -1) /* new pushing; restart delay */
11960 player->push_delay = 0;
11962 if (player->push_delay == 0) /* new pushing; restart delay */
11963 player->push_delay = FrameCounter;
11966 #if USE_NEW_PUSH_DELAY
11968 if ( (player->push_delay > 0) != (!xxx_fr) )
11969 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11970 player->push_delay,
11971 xxx_pdv2, player->push_delay2, player->push_delay_value,
11972 FrameCounter, FrameCounter / 50);
11976 if (player->push_delay > 0 &&
11977 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11978 element != EL_SPRING && element != EL_BALLOON)
11981 if (player->push_delay < player->push_delay_value &&
11982 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11983 element != EL_SPRING && element != EL_BALLOON)
11987 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11988 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11989 element != EL_SPRING && element != EL_BALLOON)
11992 /* make sure that there is no move delay before next try to push */
11993 #if USE_NEW_MOVE_DELAY
11994 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11995 player->move_delay = 0;
11997 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11998 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12001 return MF_NO_ACTION;
12005 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12008 if (IS_SB_ELEMENT(element))
12010 if (element == EL_SOKOBAN_FIELD_FULL)
12012 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12013 local_player->sokobanfields_still_needed++;
12016 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12018 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12019 local_player->sokobanfields_still_needed--;
12022 Feld[x][y] = EL_SOKOBAN_OBJECT;
12024 if (Back[x][y] == Back[nextx][nexty])
12025 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12026 else if (Back[x][y] != 0)
12027 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12030 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12033 if (local_player->sokobanfields_still_needed == 0 &&
12034 game.emulation == EMU_SOKOBAN)
12036 player->LevelSolved = player->GameOver = TRUE;
12037 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12041 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12043 InitMovingField(x, y, move_direction);
12044 GfxAction[x][y] = ACTION_PUSHING;
12046 if (mode == DF_SNAP)
12047 ContinueMoving(x, y);
12049 MovPos[x][y] = (dx != 0 ? dx : dy);
12051 Pushed[x][y] = TRUE;
12052 Pushed[nextx][nexty] = TRUE;
12054 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12055 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12057 player->push_delay_value = -1; /* get new value later */
12059 #if USE_PUSH_BUGFIX
12060 /* now: check for element change _after_ element has been pushed! */
12062 if (game.use_change_when_pushing_bug)
12064 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12067 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12068 player->index_bit, dig_side);
12069 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12070 player->index_bit, dig_side);
12076 /* check for element change _after_ element has been pushed! */
12080 /* !!! TEST ONLY !!! */
12081 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12082 player->index_bit, dig_side);
12083 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12084 player->index_bit, dig_side);
12086 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12087 player->index_bit, dig_side);
12088 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12089 player->index_bit, dig_side);
12097 else if (IS_SWITCHABLE(element))
12099 if (PLAYER_SWITCHING(player, x, y))
12101 CheckTriggeredElementChangeByPlayer(x,y, element,
12102 CE_PLAYER_PRESSES_X,
12103 player->index_bit, dig_side);
12108 player->is_switching = TRUE;
12109 player->switch_x = x;
12110 player->switch_y = y;
12112 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12114 if (element == EL_ROBOT_WHEEL)
12116 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12120 DrawLevelField(x, y);
12122 else if (element == EL_SP_TERMINAL)
12126 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12128 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12130 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12131 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12134 else if (IS_BELT_SWITCH(element))
12136 ToggleBeltSwitch(x, y);
12138 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12139 element == EL_SWITCHGATE_SWITCH_DOWN)
12141 ToggleSwitchgateSwitch(x, y);
12143 else if (element == EL_LIGHT_SWITCH ||
12144 element == EL_LIGHT_SWITCH_ACTIVE)
12146 ToggleLightSwitch(x, y);
12149 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12150 SND_LIGHT_SWITCH_ACTIVATING :
12151 SND_LIGHT_SWITCH_DEACTIVATING);
12154 else if (element == EL_TIMEGATE_SWITCH)
12156 ActivateTimegateSwitch(x, y);
12158 else if (element == EL_BALLOON_SWITCH_LEFT ||
12159 element == EL_BALLOON_SWITCH_RIGHT ||
12160 element == EL_BALLOON_SWITCH_UP ||
12161 element == EL_BALLOON_SWITCH_DOWN ||
12162 element == EL_BALLOON_SWITCH_ANY)
12164 if (element == EL_BALLOON_SWITCH_ANY)
12165 game.balloon_dir = move_direction;
12167 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12168 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12169 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12170 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12173 else if (element == EL_LAMP)
12175 Feld[x][y] = EL_LAMP_ACTIVE;
12176 local_player->lights_still_needed--;
12178 ResetGfxAnimation(x, y);
12179 DrawLevelField(x, y);
12181 else if (element == EL_TIME_ORB_FULL)
12183 Feld[x][y] = EL_TIME_ORB_EMPTY;
12185 DrawGameValue_Time(TimeLeft);
12187 ResetGfxAnimation(x, y);
12188 DrawLevelField(x, y);
12191 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12195 CheckTriggeredElementChangeByPlayer(x, y, element,
12197 player->index_bit, dig_side);
12199 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12200 player->index_bit, dig_side);
12206 if (!PLAYER_SWITCHING(player, x, y))
12208 player->is_switching = TRUE;
12209 player->switch_x = x;
12210 player->switch_y = y;
12213 /* !!! TEST ONLY !!! */
12214 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12215 player->index_bit, dig_side);
12216 CheckTriggeredElementChangeByPlayer(x, y, element,
12218 player->index_bit, dig_side);
12220 CheckTriggeredElementChangeByPlayer(x, y, element,
12222 player->index_bit, dig_side);
12223 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12224 player->index_bit, dig_side);
12229 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12230 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12231 player->index_bit, dig_side);
12232 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12233 player->index_bit, dig_side);
12235 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12236 player->index_bit, dig_side);
12237 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12238 player->index_bit, dig_side);
12242 return MF_NO_ACTION;
12245 #if USE_NEW_PUSH_DELAY
12246 player->push_delay = -1;
12248 player->push_delay = 0;
12251 #if USE_PENGUIN_COLLECT_BUGFIX
12252 if (is_player) /* function can also be called by EL_PENGUIN */
12255 if (Feld[x][y] != element) /* really digged/collected something */
12256 player->is_collecting = !player->is_digging;
12262 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12264 int jx = player->jx, jy = player->jy;
12265 int x = jx + dx, y = jy + dy;
12266 int snap_direction = (dx == -1 ? MV_LEFT :
12267 dx == +1 ? MV_RIGHT :
12269 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12272 if (player->MovPos != 0)
12275 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12279 if (!player->active || !IN_LEV_FIELD(x, y))
12287 if (player->MovPos == 0)
12288 player->is_pushing = FALSE;
12290 player->is_snapping = FALSE;
12292 if (player->MovPos == 0)
12294 player->is_moving = FALSE;
12295 player->is_digging = FALSE;
12296 player->is_collecting = FALSE;
12302 if (player->is_snapping)
12305 player->MovDir = snap_direction;
12308 if (player->MovPos == 0)
12311 player->is_moving = FALSE;
12312 player->is_digging = FALSE;
12313 player->is_collecting = FALSE;
12316 player->is_dropping = FALSE;
12318 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12321 player->is_snapping = TRUE;
12324 if (player->MovPos == 0)
12327 player->is_moving = FALSE;
12328 player->is_digging = FALSE;
12329 player->is_collecting = FALSE;
12333 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12334 DrawLevelField(player->last_jx, player->last_jy);
12337 DrawLevelField(x, y);
12346 boolean DropElement(struct PlayerInfo *player)
12348 int old_element, new_element;
12349 int dropx = player->jx, dropy = player->jy;
12350 int drop_direction = player->MovDir;
12352 int drop_side = drop_direction;
12354 static int trigger_sides[4] =
12356 CH_SIDE_LEFT, /* dropping left */
12357 CH_SIDE_RIGHT, /* dropping right */
12358 CH_SIDE_TOP, /* dropping up */
12359 CH_SIDE_BOTTOM, /* dropping down */
12361 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12363 int drop_element = (player->inventory_size > 0 ?
12364 player->inventory_element[player->inventory_size - 1] :
12365 player->inventory_infinite_element != EL_UNDEFINED ?
12366 player->inventory_infinite_element :
12367 player->dynabombs_left > 0 ?
12368 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12371 #if USE_DROP_BUGFIX
12372 /* do not drop an element on top of another element; when holding drop key
12373 pressed without moving, dropped element must move away before the next
12374 element can be dropped (this is especially important if the next element
12375 is dynamite, which can be placed on background for historical reasons) */
12376 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12380 if (IS_THROWABLE(drop_element))
12382 dropx += GET_DX_FROM_DIR(drop_direction);
12383 dropy += GET_DY_FROM_DIR(drop_direction);
12385 if (!IN_LEV_FIELD(dropx, dropy))
12389 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12390 new_element = drop_element; /* default: no change when dropping */
12392 /* check if player is active, not moving and ready to drop */
12393 if (!player->active || player->MovPos || player->drop_delay > 0)
12396 /* check if player has anything that can be dropped */
12398 if (new_element == EL_UNDEFINED)
12401 if (player->inventory_size == 0 &&
12402 player->inventory_infinite_element == EL_UNDEFINED &&
12403 player->dynabombs_left == 0)
12407 /* check if anything can be dropped at the current position */
12408 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12411 /* collected custom elements can only be dropped on empty fields */
12413 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12416 if (player->inventory_size > 0 &&
12417 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12418 && old_element != EL_EMPTY)
12422 if (old_element != EL_EMPTY)
12423 Back[dropx][dropy] = old_element; /* store old element on this field */
12425 ResetGfxAnimation(dropx, dropy);
12426 ResetRandomAnimationValue(dropx, dropy);
12428 if (player->inventory_size > 0 ||
12429 player->inventory_infinite_element != EL_UNDEFINED)
12431 if (player->inventory_size > 0)
12433 player->inventory_size--;
12436 new_element = player->inventory_element[player->inventory_size];
12439 DrawGameValue_Dynamite(local_player->inventory_size);
12441 if (new_element == EL_DYNAMITE)
12442 new_element = EL_DYNAMITE_ACTIVE;
12443 else if (new_element == EL_SP_DISK_RED)
12444 new_element = EL_SP_DISK_RED_ACTIVE;
12447 Feld[dropx][dropy] = new_element;
12449 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12450 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12451 el2img(Feld[dropx][dropy]), 0);
12453 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12456 /* needed if previous element just changed to "empty" in the last frame */
12457 Changed[dropx][dropy] = FALSE; /* allow another change */
12461 /* !!! TEST ONLY !!! */
12462 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12463 player->index_bit, drop_side);
12464 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12466 player->index_bit, drop_side);
12468 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12470 player->index_bit, drop_side);
12471 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12472 player->index_bit, drop_side);
12475 TestIfElementTouchesCustomElement(dropx, dropy);
12477 else /* player is dropping a dyna bomb */
12479 player->dynabombs_left--;
12482 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12485 Feld[dropx][dropy] = new_element;
12487 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12488 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12489 el2img(Feld[dropx][dropy]), 0);
12491 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12498 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12501 InitField_WithBug1(dropx, dropy, FALSE);
12503 InitField(dropx, dropy, FALSE);
12504 if (CAN_MOVE(Feld[dropx][dropy]))
12505 InitMovDir(dropx, dropy);
12509 new_element = Feld[dropx][dropy]; /* element might have changed */
12511 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12512 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12515 int move_stepsize = element_info[new_element].move_stepsize;
12517 int move_direction, nextx, nexty;
12519 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12520 MovDir[dropx][dropy] = drop_direction;
12522 move_direction = MovDir[dropx][dropy];
12523 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12524 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12527 Changed[dropx][dropy] = FALSE; /* allow another change */
12528 CheckCollision[dropx][dropy] = 2;
12531 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12534 WasJustMoving[dropx][dropy] = 3;
12537 InitMovingField(dropx, dropy, move_direction);
12538 ContinueMoving(dropx, dropy);
12543 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12546 Changed[dropx][dropy] = FALSE; /* allow another change */
12549 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12551 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12552 CE_HITTING_SOMETHING, move_direction);
12560 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12565 player->drop_delay = 8 + 8 + 8;
12569 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12574 player->is_dropping = TRUE;
12576 #if USE_DROP_BUGFIX
12577 player->drop_x = dropx;
12578 player->drop_y = dropy;
12584 /* ------------------------------------------------------------------------- */
12585 /* game sound playing functions */
12586 /* ------------------------------------------------------------------------- */
12588 static int *loop_sound_frame = NULL;
12589 static int *loop_sound_volume = NULL;
12591 void InitPlayLevelSound()
12593 int num_sounds = getSoundListSize();
12595 checked_free(loop_sound_frame);
12596 checked_free(loop_sound_volume);
12598 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12599 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12602 static void PlayLevelSound(int x, int y, int nr)
12604 int sx = SCREENX(x), sy = SCREENY(y);
12605 int volume, stereo_position;
12606 int max_distance = 8;
12607 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12609 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12610 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12613 if (!IN_LEV_FIELD(x, y) ||
12614 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12615 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12618 volume = SOUND_MAX_VOLUME;
12620 if (!IN_SCR_FIELD(sx, sy))
12622 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12623 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12625 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12628 stereo_position = (SOUND_MAX_LEFT +
12629 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12630 (SCR_FIELDX + 2 * max_distance));
12632 if (IS_LOOP_SOUND(nr))
12634 /* This assures that quieter loop sounds do not overwrite louder ones,
12635 while restarting sound volume comparison with each new game frame. */
12637 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12640 loop_sound_volume[nr] = volume;
12641 loop_sound_frame[nr] = FrameCounter;
12644 PlaySoundExt(nr, volume, stereo_position, type);
12647 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12649 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12650 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12651 y < LEVELY(BY1) ? LEVELY(BY1) :
12652 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12656 static void PlayLevelSoundAction(int x, int y, int action)
12658 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12661 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12663 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12665 if (sound_effect != SND_UNDEFINED)
12666 PlayLevelSound(x, y, sound_effect);
12669 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12672 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12674 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12675 PlayLevelSound(x, y, sound_effect);
12678 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12680 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12682 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12683 PlayLevelSound(x, y, sound_effect);
12686 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12688 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12690 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12691 StopSound(sound_effect);
12694 static void PlayLevelMusic()
12696 if (levelset.music[level_nr] != MUS_UNDEFINED)
12697 PlayMusic(levelset.music[level_nr]); /* from config file */
12699 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12702 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12704 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12707 if (sample == SAMPLE_bug)
12708 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12714 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12718 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12722 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12726 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12730 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12734 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12738 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12741 case SAMPLE_android_clone:
12742 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12745 case SAMPLE_android_move:
12746 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12749 case SAMPLE_spring:
12750 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12754 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12758 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12761 case SAMPLE_eater_eat:
12762 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12766 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12769 case SAMPLE_collect:
12770 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12773 case SAMPLE_diamond:
12774 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12777 case SAMPLE_squash:
12778 /* !!! CHECK THIS !!! */
12780 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12782 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12786 case SAMPLE_wonderfall:
12787 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12791 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12795 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12799 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12803 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12807 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12811 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12814 case SAMPLE_wonder:
12815 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12819 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12822 case SAMPLE_exit_open:
12823 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12826 case SAMPLE_exit_leave:
12827 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12830 case SAMPLE_dynamite:
12831 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12835 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12839 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12843 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12847 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12851 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12855 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12859 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12864 void RaiseScore(int value)
12866 local_player->score += value;
12868 DrawGameValue_Score(local_player->score);
12871 void RaiseScoreElement(int element)
12876 case EL_BD_DIAMOND:
12877 case EL_EMERALD_YELLOW:
12878 case EL_EMERALD_RED:
12879 case EL_EMERALD_PURPLE:
12880 case EL_SP_INFOTRON:
12881 RaiseScore(level.score[SC_EMERALD]);
12884 RaiseScore(level.score[SC_DIAMOND]);
12887 RaiseScore(level.score[SC_CRYSTAL]);
12890 RaiseScore(level.score[SC_PEARL]);
12893 case EL_BD_BUTTERFLY:
12894 case EL_SP_ELECTRON:
12895 RaiseScore(level.score[SC_BUG]);
12898 case EL_BD_FIREFLY:
12899 case EL_SP_SNIKSNAK:
12900 RaiseScore(level.score[SC_SPACESHIP]);
12903 case EL_DARK_YAMYAM:
12904 RaiseScore(level.score[SC_YAMYAM]);
12907 RaiseScore(level.score[SC_ROBOT]);
12910 RaiseScore(level.score[SC_PACMAN]);
12913 RaiseScore(level.score[SC_NUT]);
12916 case EL_SP_DISK_RED:
12917 case EL_DYNABOMB_INCREASE_NUMBER:
12918 case EL_DYNABOMB_INCREASE_SIZE:
12919 case EL_DYNABOMB_INCREASE_POWER:
12920 RaiseScore(level.score[SC_DYNAMITE]);
12922 case EL_SHIELD_NORMAL:
12923 case EL_SHIELD_DEADLY:
12924 RaiseScore(level.score[SC_SHIELD]);
12926 case EL_EXTRA_TIME:
12927 RaiseScore(level.score[SC_TIME_BONUS]);
12941 RaiseScore(level.score[SC_KEY]);
12944 RaiseScore(element_info[element].collect_score);
12949 void RequestQuitGame(boolean ask_if_really_quit)
12951 if (AllPlayersGone ||
12952 !ask_if_really_quit ||
12953 level_editor_test_game ||
12954 Request("Do you really want to quit the game ?",
12955 REQ_ASK | REQ_STAY_CLOSED))
12957 #if defined(NETWORK_AVALIABLE)
12958 if (options.network)
12959 SendToServer_StopPlaying();
12963 game_status = GAME_MODE_MAIN;
12971 if (tape.playing && tape.deactivate_display)
12972 TapeDeactivateDisplayOff(TRUE);
12975 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12978 if (tape.playing && tape.deactivate_display)
12979 TapeDeactivateDisplayOn();
12986 /* ---------- new game button stuff ---------------------------------------- */
12988 /* graphic position values for game buttons */
12989 #define GAME_BUTTON_XSIZE 30
12990 #define GAME_BUTTON_YSIZE 30
12991 #define GAME_BUTTON_XPOS 5
12992 #define GAME_BUTTON_YPOS 215
12993 #define SOUND_BUTTON_XPOS 5
12994 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12996 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12997 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12998 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12999 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13000 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13001 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13008 } gamebutton_info[NUM_GAME_BUTTONS] =
13011 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13016 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13017 GAME_CTRL_ID_PAUSE,
13021 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13026 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13027 SOUND_CTRL_ID_MUSIC,
13028 "background music on/off"
13031 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13032 SOUND_CTRL_ID_LOOPS,
13033 "sound loops on/off"
13036 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13037 SOUND_CTRL_ID_SIMPLE,
13038 "normal sounds on/off"
13042 void CreateGameButtons()
13046 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13048 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13049 struct GadgetInfo *gi;
13052 unsigned long event_mask;
13053 int gd_xoffset, gd_yoffset;
13054 int gd_x1, gd_x2, gd_y1, gd_y2;
13057 gd_xoffset = gamebutton_info[i].x;
13058 gd_yoffset = gamebutton_info[i].y;
13059 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13060 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13062 if (id == GAME_CTRL_ID_STOP ||
13063 id == GAME_CTRL_ID_PAUSE ||
13064 id == GAME_CTRL_ID_PLAY)
13066 button_type = GD_TYPE_NORMAL_BUTTON;
13068 event_mask = GD_EVENT_RELEASED;
13069 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13070 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13074 button_type = GD_TYPE_CHECK_BUTTON;
13076 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13077 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13078 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13079 event_mask = GD_EVENT_PRESSED;
13080 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13081 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13084 gi = CreateGadget(GDI_CUSTOM_ID, id,
13085 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13086 GDI_X, DX + gd_xoffset,
13087 GDI_Y, DY + gd_yoffset,
13088 GDI_WIDTH, GAME_BUTTON_XSIZE,
13089 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13090 GDI_TYPE, button_type,
13091 GDI_STATE, GD_BUTTON_UNPRESSED,
13092 GDI_CHECKED, checked,
13093 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13094 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13095 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13096 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13097 GDI_EVENT_MASK, event_mask,
13098 GDI_CALLBACK_ACTION, HandleGameButtons,
13102 Error(ERR_EXIT, "cannot create gadget");
13104 game_gadget[id] = gi;
13108 void FreeGameButtons()
13112 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13113 FreeGadget(game_gadget[i]);
13116 static void MapGameButtons()
13120 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13121 MapGadget(game_gadget[i]);
13124 void UnmapGameButtons()
13128 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13129 UnmapGadget(game_gadget[i]);
13132 static void HandleGameButtons(struct GadgetInfo *gi)
13134 int id = gi->custom_id;
13136 if (game_status != GAME_MODE_PLAYING)
13141 case GAME_CTRL_ID_STOP:
13142 RequestQuitGame(TRUE);
13145 case GAME_CTRL_ID_PAUSE:
13146 if (options.network)
13148 #if defined(NETWORK_AVALIABLE)
13150 SendToServer_ContinuePlaying();
13152 SendToServer_PausePlaying();
13156 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13159 case GAME_CTRL_ID_PLAY:
13162 #if defined(NETWORK_AVALIABLE)
13163 if (options.network)
13164 SendToServer_ContinuePlaying();
13168 tape.pausing = FALSE;
13169 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13174 case SOUND_CTRL_ID_MUSIC:
13175 if (setup.sound_music)
13177 setup.sound_music = FALSE;
13180 else if (audio.music_available)
13182 setup.sound = setup.sound_music = TRUE;
13184 SetAudioMode(setup.sound);
13190 case SOUND_CTRL_ID_LOOPS:
13191 if (setup.sound_loops)
13192 setup.sound_loops = FALSE;
13193 else if (audio.loops_available)
13195 setup.sound = setup.sound_loops = TRUE;
13196 SetAudioMode(setup.sound);
13200 case SOUND_CTRL_ID_SIMPLE:
13201 if (setup.sound_simple)
13202 setup.sound_simple = FALSE;
13203 else if (audio.sound_available)
13205 setup.sound = setup.sound_simple = TRUE;
13206 SetAudioMode(setup.sound);