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 * 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_PUSH_BUGFIX TRUE * USE_NEW_STUFF * 1
47 /* for MovePlayer() */
48 #define MF_NO_ACTION 0
52 /* for ScrollPlayer() */
54 #define SCROLL_GO_ON 1
57 #define EX_PHASE_START 0
58 #define EX_TYPE_NONE 0
59 #define EX_TYPE_NORMAL (1 << 0)
60 #define EX_TYPE_CENTER (1 << 1)
61 #define EX_TYPE_BORDER (1 << 2)
62 #define EX_TYPE_CROSS (1 << 3)
63 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
65 /* special positions in the game control window (relative to control window) */
68 #define XX_EMERALDS 29
69 #define YY_EMERALDS 54
70 #define XX_DYNAMITE 29
71 #define YY_DYNAMITE 89
80 /* special positions in the game control window (relative to main window) */
81 #define DX_LEVEL (DX + XX_LEVEL)
82 #define DY_LEVEL (DY + YY_LEVEL)
83 #define DX_EMERALDS (DX + XX_EMERALDS)
84 #define DY_EMERALDS (DY + YY_EMERALDS)
85 #define DX_DYNAMITE (DX + XX_DYNAMITE)
86 #define DY_DYNAMITE (DY + YY_DYNAMITE)
87 #define DX_KEYS (DX + XX_KEYS)
88 #define DY_KEYS (DY + YY_KEYS)
89 #define DX_SCORE (DX + XX_SCORE)
90 #define DY_SCORE (DY + YY_SCORE)
91 #define DX_TIME1 (DX + XX_TIME1)
92 #define DX_TIME2 (DX + XX_TIME2)
93 #define DY_TIME (DY + YY_TIME)
95 /* values for initial player move delay (initial delay counter value) */
96 #define INITIAL_MOVE_DELAY_OFF -1
97 #define INITIAL_MOVE_DELAY_ON 0
99 /* values for player movement speed (which is in fact a delay value) */
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED 4
103 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
104 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
106 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
108 /* values for other actions */
109 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
111 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
112 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
114 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
116 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
117 RND(element_info[e].push_delay_random))
118 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
119 RND(element_info[e].drop_delay_random))
120 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
121 RND(element_info[e].move_delay_random))
122 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
123 (element_info[e].move_delay_random))
125 #define GET_TARGET_ELEMENT(e, ch) \
126 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
127 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
129 #define GET_VALID_PLAYER_ELEMENT(e) \
130 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
132 #define CAN_GROW_INTO(e) \
133 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
135 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
141 (CAN_MOVE_INTO_ACID(e) && \
142 Feld[x][y] == EL_ACID) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
154 (CAN_MOVE_INTO_ACID(e) && \
155 Feld[x][y] == EL_ACID) || \
156 (DONT_COLLIDE_WITH(e) && \
158 !PLAYER_ENEMY_PROTECTED(x, y))))
161 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
164 (DONT_COLLIDE_WITH(e) && \
166 !PLAYER_ENEMY_PROTECTED(x, y))))
169 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
173 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
174 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
176 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
181 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
184 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
189 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
192 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
195 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
198 #define PIG_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
201 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
203 IS_FOOD_PENGUIN(Feld[x][y])))
204 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
207 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
208 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
210 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
211 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
216 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
217 (CAN_MOVE_INTO_ACID(e) && \
218 Feld[x][y] == EL_ACID) || \
219 Feld[x][y] == EL_DIAMOND))
221 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID) || \
225 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
227 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
228 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
229 (CAN_MOVE_INTO_ACID(e) && \
230 Feld[x][y] == EL_ACID) || \
231 IS_AMOEBOID(Feld[x][y])))
233 #define PIG_CAN_ENTER_FIELD(e, x, y) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
235 (CAN_MOVE_INTO_ACID(e) && \
236 Feld[x][y] == EL_ACID) || \
237 IS_FOOD_PIG(Feld[x][y])))
239 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID) || \
243 IS_FOOD_PENGUIN(Feld[x][y]) || \
244 Feld[x][y] == EL_EXIT_OPEN))
246 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
247 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
248 (CAN_MOVE_INTO_ACID(e) && \
249 Feld[x][y] == EL_ACID)))
251 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
252 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
253 (CAN_MOVE_INTO_ACID(e) && \
254 Feld[x][y] == EL_ACID) || \
257 #define SPRING_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)))
264 #define GROUP_NR(e) ((e) - EL_GROUP_START)
265 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
266 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
267 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
269 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
270 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
273 #define CE_ENTER_FIELD_COND(e, x, y) \
274 (!IS_PLAYER(x, y) && \
275 (Feld[x][y] == EL_ACID || \
276 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
278 #define CE_ENTER_FIELD_COND(e, x, y) \
279 (!IS_PLAYER(x, y) && \
280 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
284 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
289 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP 0
296 #define GAME_CTRL_ID_PAUSE 1
297 #define GAME_CTRL_ID_PLAY 2
298 #define SOUND_CTRL_ID_MUSIC 3
299 #define SOUND_CTRL_ID_LOOPS 4
300 #define SOUND_CTRL_ID_SIMPLE 5
302 #define NUM_GAME_BUTTONS 6
305 /* forward declaration for internal use */
307 static void AdvanceFrameAndPlayerCounters(int);
309 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
310 static boolean MovePlayer(struct PlayerInfo *, int, int);
311 static void ScrollPlayer(struct PlayerInfo *, int);
312 static void ScrollScreen(struct PlayerInfo *, int);
314 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
316 static void InitBeltMovement(void);
317 static void CloseAllOpenTimegates(void);
318 static void CheckGravityMovement(struct PlayerInfo *);
319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
320 static void KillHeroUnlessEnemyProtected(int, int);
321 static void KillHeroUnlessExplosionProtected(int, int);
323 static void TestIfPlayerTouchesCustomElement(int, int);
324 static void TestIfElementTouchesCustomElement(int, int);
325 static void TestIfElementHitsCustomElement(int, int, int);
327 static void TestIfElementSmashesCustomElement(int, int, int);
330 static void ChangeElement(int, int, int);
332 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
333 #define CheckTriggeredElementChange(x, y, e, ev) \
334 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
336 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
337 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
338 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
339 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
340 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
341 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
344 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
345 #define CheckElementChange(x, y, e, te, ev) \
346 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
347 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
348 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
349 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
350 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
351 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
352 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
354 static void PlayLevelSound(int, int, int);
355 static void PlayLevelSoundNearest(int, int, int);
356 static void PlayLevelSoundAction(int, int, int);
357 static void PlayLevelSoundElementAction(int, int, int, int);
358 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
359 static void PlayLevelSoundActionIfLoop(int, int, int);
360 static void StopLevelSoundActionIfLoop(int, int, int);
361 static void PlayLevelMusic();
363 static void MapGameButtons();
364 static void HandleGameButtons(struct GadgetInfo *);
366 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
369 /* ------------------------------------------------------------------------- */
370 /* definition of elements that automatically change to other elements after */
371 /* a specified time, eventually calling a function when changing */
372 /* ------------------------------------------------------------------------- */
374 /* forward declaration for changer functions */
375 static void InitBuggyBase(int x, int y);
376 static void WarnBuggyBase(int x, int y);
378 static void InitTrap(int x, int y);
379 static void ActivateTrap(int x, int y);
380 static void ChangeActiveTrap(int x, int y);
382 static void InitRobotWheel(int x, int y);
383 static void RunRobotWheel(int x, int y);
384 static void StopRobotWheel(int x, int y);
386 static void InitTimegateWheel(int x, int y);
387 static void RunTimegateWheel(int x, int y);
389 struct ChangingElementInfo
394 void (*pre_change_function)(int x, int y);
395 void (*change_function)(int x, int y);
396 void (*post_change_function)(int x, int y);
399 static struct ChangingElementInfo change_delay_list[] =
450 EL_SWITCHGATE_OPENING,
458 EL_SWITCHGATE_CLOSING,
459 EL_SWITCHGATE_CLOSED,
491 EL_ACID_SPLASH_RIGHT,
500 EL_SP_BUGGY_BASE_ACTIVATING,
507 EL_SP_BUGGY_BASE_ACTIVATING,
508 EL_SP_BUGGY_BASE_ACTIVE,
515 EL_SP_BUGGY_BASE_ACTIVE,
539 EL_ROBOT_WHEEL_ACTIVE,
547 EL_TIMEGATE_SWITCH_ACTIVE,
568 int push_delay_fixed, push_delay_random;
573 { EL_BALLOON, 0, 0 },
575 { EL_SOKOBAN_OBJECT, 2, 0 },
576 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
577 { EL_SATELLITE, 2, 0 },
578 { EL_SP_DISK_YELLOW, 2, 0 },
580 { EL_UNDEFINED, 0, 0 },
588 move_stepsize_list[] =
590 { EL_AMOEBA_DROP, 2 },
591 { EL_AMOEBA_DROPPING, 2 },
592 { EL_QUICKSAND_FILLING, 1 },
593 { EL_QUICKSAND_EMPTYING, 1 },
594 { EL_MAGIC_WALL_FILLING, 2 },
595 { EL_BD_MAGIC_WALL_FILLING, 2 },
596 { EL_MAGIC_WALL_EMPTYING, 2 },
597 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
607 collect_count_list[] =
610 { EL_BD_DIAMOND, 1 },
611 { EL_EMERALD_YELLOW, 1 },
612 { EL_EMERALD_RED, 1 },
613 { EL_EMERALD_PURPLE, 1 },
615 { EL_SP_INFOTRON, 1 },
627 access_direction_list[] =
629 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
630 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
631 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
632 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
633 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
634 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
635 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
636 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
637 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
638 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
639 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
641 { EL_SP_PORT_LEFT, MV_RIGHT },
642 { EL_SP_PORT_RIGHT, MV_LEFT },
643 { EL_SP_PORT_UP, MV_DOWN },
644 { EL_SP_PORT_DOWN, MV_UP },
645 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
646 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
647 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
649 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
650 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
651 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
652 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
653 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
654 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
655 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
656 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
657 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
658 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
659 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
661 { EL_UNDEFINED, MV_NO_MOVING }
664 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
666 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
667 CH_EVENT_BIT(CE_DELAY))
668 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
669 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
670 IS_JUST_CHANGING(x, y))
672 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
675 void GetPlayerConfig()
677 if (!audio.sound_available)
678 setup.sound_simple = FALSE;
680 if (!audio.loops_available)
681 setup.sound_loops = FALSE;
683 if (!audio.music_available)
684 setup.sound_music = FALSE;
686 if (!video.fullscreen_available)
687 setup.fullscreen = FALSE;
689 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
691 SetAudioMode(setup.sound);
695 static int getBeltNrFromBeltElement(int element)
697 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
698 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
699 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
702 static int getBeltNrFromBeltActiveElement(int element)
704 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
705 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
706 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
709 static int getBeltNrFromBeltSwitchElement(int element)
711 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
712 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
713 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
716 static int getBeltDirNrFromBeltSwitchElement(int element)
718 static int belt_base_element[4] =
720 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
721 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
722 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
723 EL_CONVEYOR_BELT_4_SWITCH_LEFT
726 int belt_nr = getBeltNrFromBeltSwitchElement(element);
727 int belt_dir_nr = element - belt_base_element[belt_nr];
729 return (belt_dir_nr % 3);
732 static int getBeltDirFromBeltSwitchElement(int element)
734 static int belt_move_dir[3] =
741 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
743 return belt_move_dir[belt_dir_nr];
746 static void InitPlayerField(int x, int y, int element, boolean init_game)
748 if (element == EL_SP_MURPHY)
752 if (stored_player[0].present)
754 Feld[x][y] = EL_SP_MURPHY_CLONE;
760 stored_player[0].use_murphy_graphic = TRUE;
763 Feld[x][y] = EL_PLAYER_1;
769 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
770 int jx = player->jx, jy = player->jy;
772 player->present = TRUE;
774 player->block_last_field = (element == EL_SP_MURPHY ?
775 level.sp_block_last_field :
776 level.block_last_field);
778 #if USE_NEW_BLOCK_STYLE
780 player->block_delay = (player->block_last_field ?
781 (element == EL_SP_MURPHY ?
782 level.sp_block_delay :
783 level.block_delay) : 0);
785 player->block_delay = (element == EL_SP_MURPHY ?
786 (player->block_last_field ? 7 : 1) :
787 (player->block_last_field ? 7 : 1));
791 printf("::: block_last_field == %d, block_delay = %d\n",
792 player->block_last_field, player->block_delay);
796 if (!options.network || player->connected)
798 player->active = TRUE;
800 /* remove potentially duplicate players */
801 if (StorePlayer[jx][jy] == Feld[x][y])
802 StorePlayer[jx][jy] = 0;
804 StorePlayer[x][y] = Feld[x][y];
808 printf("Player %d activated.\n", player->element_nr);
809 printf("[Local player is %d and currently %s.]\n",
810 local_player->element_nr,
811 local_player->active ? "active" : "not active");
815 Feld[x][y] = EL_EMPTY;
817 player->jx = player->last_jx = x;
818 player->jy = player->last_jy = y;
822 static void InitField(int x, int y, boolean init_game)
824 int element = Feld[x][y];
833 InitPlayerField(x, y, element, init_game);
836 case EL_SOKOBAN_FIELD_PLAYER:
837 element = Feld[x][y] = EL_PLAYER_1;
838 InitField(x, y, init_game);
840 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
841 InitField(x, y, init_game);
844 case EL_SOKOBAN_FIELD_EMPTY:
845 local_player->sokobanfields_still_needed++;
849 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
850 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
851 else if (x > 0 && Feld[x-1][y] == EL_ACID)
852 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
853 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
854 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
855 else if (y > 0 && Feld[x][y-1] == EL_ACID)
856 Feld[x][y] = EL_ACID_POOL_BOTTOM;
857 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
858 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
866 case EL_SPACESHIP_RIGHT:
867 case EL_SPACESHIP_UP:
868 case EL_SPACESHIP_LEFT:
869 case EL_SPACESHIP_DOWN:
871 case EL_BD_BUTTERFLY_RIGHT:
872 case EL_BD_BUTTERFLY_UP:
873 case EL_BD_BUTTERFLY_LEFT:
874 case EL_BD_BUTTERFLY_DOWN:
875 case EL_BD_BUTTERFLY:
876 case EL_BD_FIREFLY_RIGHT:
877 case EL_BD_FIREFLY_UP:
878 case EL_BD_FIREFLY_LEFT:
879 case EL_BD_FIREFLY_DOWN:
881 case EL_PACMAN_RIGHT:
905 if (y == lev_fieldy - 1)
907 Feld[x][y] = EL_AMOEBA_GROWING;
908 Store[x][y] = EL_AMOEBA_WET;
912 case EL_DYNAMITE_ACTIVE:
913 case EL_SP_DISK_RED_ACTIVE:
914 case EL_DYNABOMB_PLAYER_1_ACTIVE:
915 case EL_DYNABOMB_PLAYER_2_ACTIVE:
916 case EL_DYNABOMB_PLAYER_3_ACTIVE:
917 case EL_DYNABOMB_PLAYER_4_ACTIVE:
922 local_player->lights_still_needed++;
926 local_player->friends_still_needed++;
931 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
936 Feld[x][y] = EL_EMPTY;
941 case EL_EM_KEY_1_FILE:
942 Feld[x][y] = EL_EM_KEY_1;
944 case EL_EM_KEY_2_FILE:
945 Feld[x][y] = EL_EM_KEY_2;
947 case EL_EM_KEY_3_FILE:
948 Feld[x][y] = EL_EM_KEY_3;
950 case EL_EM_KEY_4_FILE:
951 Feld[x][y] = EL_EM_KEY_4;
955 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
956 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
957 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
958 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
959 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
960 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
961 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
962 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
963 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
964 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
965 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
966 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
969 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
970 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
971 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
973 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
975 game.belt_dir[belt_nr] = belt_dir;
976 game.belt_dir_nr[belt_nr] = belt_dir_nr;
978 else /* more than one switch -- set it like the first switch */
980 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
985 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
987 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
990 case EL_LIGHT_SWITCH_ACTIVE:
992 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
996 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
998 else if (IS_GROUP_ELEMENT(element))
1000 struct ElementGroupInfo *group = element_info[element].group;
1001 int last_anim_random_frame = gfx.anim_random_frame;
1004 if (group->choice_mode == ANIM_RANDOM)
1005 gfx.anim_random_frame = RND(group->num_elements_resolved);
1007 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1008 group->choice_mode, 0,
1011 if (group->choice_mode == ANIM_RANDOM)
1012 gfx.anim_random_frame = last_anim_random_frame;
1014 group->choice_pos++;
1016 Feld[x][y] = group->element_resolved[element_pos];
1018 InitField(x, y, init_game);
1024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1026 InitField(x, y, init_game);
1028 /* not needed to call InitMovDir() -- already done by InitField()! */
1029 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1030 CAN_MOVE(Feld[x][y]))
1034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1036 int old_element = Feld[x][y];
1038 InitField(x, y, init_game);
1040 /* not needed to call InitMovDir() -- already done by InitField()! */
1041 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1042 CAN_MOVE(old_element) &&
1043 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1046 /* this case is in fact a combination of not less than three bugs:
1047 first, it calls InitMovDir() for elements that can move, although this is
1048 already done by InitField(); then, it checks the element that was at this
1049 field _before_ the call to InitField() (which can change it); lastly, it
1050 was not called for "mole with direction" elements, which were treated as
1051 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1055 inline void DrawGameValue_Emeralds(int value)
1057 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1060 inline void DrawGameValue_Dynamite(int value)
1062 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1065 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1069 /* currently only 4 of 8 possible keys are displayed */
1070 for (i = 0; i < STD_NUM_KEYS; i++)
1072 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1073 el2edimg(EL_KEY_1 + i));
1076 inline void DrawGameValue_Score(int value)
1078 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1081 inline void DrawGameValue_Time(int value)
1084 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1086 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1089 inline void DrawGameValue_Level(int value)
1092 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1095 /* misuse area for displaying emeralds to draw bigger level number */
1096 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1097 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1099 /* now copy it to the area for displaying level number */
1100 BlitBitmap(drawto, drawto,
1101 DX_EMERALDS, DY_EMERALDS + 1,
1102 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1103 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1104 DX_LEVEL - 1, DY_LEVEL + 1);
1106 /* restore the area for displaying emeralds */
1107 DrawGameValue_Emeralds(local_player->gems_still_needed);
1109 /* yes, this is all really ugly :-) */
1113 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1116 int key[MAX_NUM_KEYS];
1119 for (i = 0; i < MAX_NUM_KEYS; i++)
1120 key[i] = key_bits & (1 << i);
1122 DrawGameValue_Level(level_nr);
1124 DrawGameValue_Emeralds(emeralds);
1125 DrawGameValue_Dynamite(dynamite);
1126 DrawGameValue_Score(score);
1127 DrawGameValue_Time(time);
1129 DrawGameValue_Keys(key);
1132 void DrawGameDoorValues()
1136 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1138 DrawGameDoorValues_EM();
1143 DrawGameValue_Level(level_nr);
1145 DrawGameValue_Emeralds(local_player->gems_still_needed);
1146 DrawGameValue_Dynamite(local_player->inventory_size);
1147 DrawGameValue_Score(local_player->score);
1148 DrawGameValue_Time(TimeLeft);
1150 for (i = 0; i < MAX_PLAYERS; i++)
1151 DrawGameValue_Keys(stored_player[i].key);
1154 static void resolve_group_element(int group_element, int recursion_depth)
1156 static int group_nr;
1157 static struct ElementGroupInfo *group;
1158 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1161 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1163 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1164 group_element - EL_GROUP_START + 1);
1166 /* replace element which caused too deep recursion by question mark */
1167 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1172 if (recursion_depth == 0) /* initialization */
1174 group = element_info[group_element].group;
1175 group_nr = group_element - EL_GROUP_START;
1177 group->num_elements_resolved = 0;
1178 group->choice_pos = 0;
1181 for (i = 0; i < actual_group->num_elements; i++)
1183 int element = actual_group->element[i];
1185 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1188 if (IS_GROUP_ELEMENT(element))
1189 resolve_group_element(element, recursion_depth + 1);
1192 group->element_resolved[group->num_elements_resolved++] = element;
1193 element_info[element].in_group[group_nr] = TRUE;
1198 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1200 printf("::: group %d: %d resolved elements\n",
1201 group_element - EL_GROUP_START, group->num_elements_resolved);
1202 for (i = 0; i < group->num_elements_resolved; i++)
1203 printf("::: - %d ['%s']\n", group->element_resolved[i],
1204 element_info[group->element_resolved[i]].token_name);
1211 =============================================================================
1213 -----------------------------------------------------------------------------
1214 initialize game engine due to level / tape version number
1215 =============================================================================
1218 static void InitGameEngine()
1222 /* set game engine from tape file when re-playing, else from level file */
1223 game.engine_version = (tape.playing ? tape.engine_version :
1224 level.game_version);
1226 /* ---------------------------------------------------------------------- */
1227 /* set flags for bugs and changes according to active game engine version */
1228 /* ---------------------------------------------------------------------- */
1232 Before 3.1.0, custom elements that "change when pushing" changed directly
1233 after the player started pushing them (until then handled in "DigField()").
1234 Since 3.1.0, these custom elements are not changed until the "pushing"
1235 move of the element is finished (now handled in "ContinueMoving()").
1237 Affected levels/tapes:
1238 The first condition is generally needed for all levels/tapes before version
1239 3.1.0, which might use the old behaviour before it was changed; known tapes
1240 that are affected are some tapes from the level set "Walpurgis Gardens" by
1242 The second condition is an exception from the above case and is needed for
1243 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1244 above (including some development versions of 3.1.0), but before it was
1245 known that this change would break tapes like the above and was fixed in
1246 3.1.1, so that the changed behaviour was active although the engine version
1247 while recording maybe was before 3.1.0. There is at least one tape that is
1248 affected by this exception, which is the tape for the one-level set "Bug
1249 Machine" by Juergen Bonhagen.
1252 game.use_bug_change_when_pushing =
1253 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1255 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1256 tape.game_version < VERSION_IDENT(3,1,1,0)));
1258 /* ---------------------------------------------------------------------- */
1260 /* dynamically adjust element properties according to game engine version */
1261 InitElementPropertiesEngine(game.engine_version);
1264 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1265 printf(" tape version == %06d [%s] [file: %06d]\n",
1266 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1268 printf(" => game.engine_version == %06d\n", game.engine_version);
1271 /* ---------- recursively resolve group elements ------------------------- */
1273 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1274 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1275 element_info[i].in_group[j] = FALSE;
1277 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1278 resolve_group_element(EL_GROUP_START + i, 0);
1280 /* ---------- initialize player's initial move delay --------------------- */
1282 #if USE_NEW_MOVE_DELAY
1283 /* dynamically adjust player properties according to level information */
1284 game.initial_move_delay_value =
1285 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1287 /* dynamically adjust player properties according to game engine version */
1288 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1289 game.initial_move_delay_value : 0);
1291 /* dynamically adjust player properties according to game engine version */
1292 game.initial_move_delay =
1293 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1294 INITIAL_MOVE_DELAY_OFF);
1296 /* dynamically adjust player properties according to level information */
1297 game.initial_move_delay_value =
1298 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1301 /* ---------- initialize player's initial push delay --------------------- */
1303 /* dynamically adjust player properties according to game engine version */
1304 game.initial_push_delay_value =
1305 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1307 /* ---------- initialize changing elements ------------------------------- */
1309 /* initialize changing elements information */
1310 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1312 struct ElementInfo *ei = &element_info[i];
1314 /* this pointer might have been changed in the level editor */
1315 ei->change = &ei->change_page[0];
1317 if (!IS_CUSTOM_ELEMENT(i))
1319 ei->change->target_element = EL_EMPTY_SPACE;
1320 ei->change->delay_fixed = 0;
1321 ei->change->delay_random = 0;
1322 ei->change->delay_frames = 1;
1325 ei->change_events = CE_BITMASK_DEFAULT;
1326 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1328 ei->event_page_nr[j] = 0;
1329 ei->event_page[j] = &ei->change_page[0];
1333 /* add changing elements from pre-defined list */
1334 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1336 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1337 struct ElementInfo *ei = &element_info[ch_delay->element];
1339 ei->change->target_element = ch_delay->target_element;
1340 ei->change->delay_fixed = ch_delay->change_delay;
1342 ei->change->pre_change_function = ch_delay->pre_change_function;
1343 ei->change->change_function = ch_delay->change_function;
1344 ei->change->post_change_function = ch_delay->post_change_function;
1346 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1349 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1354 /* add change events from custom element configuration */
1355 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1357 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1359 for (j = 0; j < ei->num_change_pages; j++)
1361 if (!ei->change_page[j].can_change)
1364 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1366 /* only add event page for the first page found with this event */
1367 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1368 !(ei->change_events & CH_EVENT_BIT(k)))
1370 ei->change_events |= CH_EVENT_BIT(k);
1371 ei->event_page_nr[k] = j;
1372 ei->event_page[k] = &ei->change_page[j];
1380 /* add change events from custom element configuration */
1381 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1383 int element = EL_CUSTOM_START + i;
1385 /* only add custom elements that change after fixed/random frame delay */
1386 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1387 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1391 /* ---------- initialize run-time trigger player and element ------------- */
1393 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1395 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1397 for (j = 0; j < ei->num_change_pages; j++)
1399 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1400 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1404 /* ---------- initialize trigger events ---------------------------------- */
1406 /* initialize trigger events information */
1407 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1408 trigger_events[i] = EP_BITMASK_DEFAULT;
1411 /* add trigger events from element change event properties */
1412 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1414 struct ElementInfo *ei = &element_info[i];
1416 for (j = 0; j < ei->num_change_pages; j++)
1418 if (!ei->change_page[j].can_change)
1421 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1423 int trigger_element = ei->change_page[j].trigger_element;
1425 if (IS_GROUP_ELEMENT(trigger_element))
1427 struct ElementGroupInfo *group = element_info[trigger_element].group;
1429 for (k = 0; k < group->num_elements_resolved; k++)
1430 trigger_events[group->element_resolved[k]]
1431 |= ei->change_page[j].events;
1434 trigger_events[trigger_element] |= ei->change_page[j].events;
1439 /* add trigger events from element change event properties */
1440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1441 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1442 trigger_events[element_info[i].change->trigger_element] |=
1443 element_info[i].change->events;
1446 /* ---------- initialize push delay -------------------------------------- */
1448 /* initialize push delay values to default */
1449 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1451 if (!IS_CUSTOM_ELEMENT(i))
1453 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1454 element_info[i].push_delay_random = game.default_push_delay_random;
1458 /* set push delay value for certain elements from pre-defined list */
1459 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1461 int e = push_delay_list[i].element;
1463 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1464 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1467 /* set push delay value for Supaplex elements for newer engine versions */
1468 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1472 if (IS_SP_ELEMENT(i))
1474 #if USE_NEW_MOVE_STYLE
1475 /* set SP push delay to just enough to push under a falling zonk */
1476 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1478 element_info[i].push_delay_fixed = delay;
1479 element_info[i].push_delay_random = 0;
1481 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1482 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1488 /* ---------- initialize move stepsize ----------------------------------- */
1490 /* initialize move stepsize values to default */
1491 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1492 if (!IS_CUSTOM_ELEMENT(i))
1493 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1495 /* set move stepsize value for certain elements from pre-defined list */
1496 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1498 int e = move_stepsize_list[i].element;
1500 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1504 /* ---------- initialize move dig/leave ---------------------------------- */
1506 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1508 element_info[i].can_leave_element = FALSE;
1509 element_info[i].can_leave_element_last = FALSE;
1513 /* ---------- initialize gem count --------------------------------------- */
1515 /* initialize gem count values for each element */
1516 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1517 if (!IS_CUSTOM_ELEMENT(i))
1518 element_info[i].collect_count = 0;
1520 /* add gem count values for all elements from pre-defined list */
1521 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1522 element_info[collect_count_list[i].element].collect_count =
1523 collect_count_list[i].count;
1525 /* ---------- initialize access direction -------------------------------- */
1527 /* initialize access direction values to default (access from every side) */
1528 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1529 if (!IS_CUSTOM_ELEMENT(i))
1530 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1532 /* set access direction value for certain elements from pre-defined list */
1533 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1534 element_info[access_direction_list[i].element].access_direction =
1535 access_direction_list[i].direction;
1540 =============================================================================
1542 -----------------------------------------------------------------------------
1543 initialize and start new game
1544 =============================================================================
1549 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1550 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1551 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1558 #if USE_NEW_AMOEBA_CODE
1559 printf("Using new amoeba code.\n");
1561 printf("Using old amoeba code.\n");
1566 /* don't play tapes over network */
1567 network_playing = (options.network && !tape.playing);
1569 for (i = 0; i < MAX_PLAYERS; i++)
1571 struct PlayerInfo *player = &stored_player[i];
1573 player->index_nr = i;
1574 player->index_bit = (1 << i);
1575 player->element_nr = EL_PLAYER_1 + i;
1577 player->present = FALSE;
1578 player->active = FALSE;
1581 player->effective_action = 0;
1582 player->programmed_action = 0;
1585 player->gems_still_needed = level.gems_needed;
1586 player->sokobanfields_still_needed = 0;
1587 player->lights_still_needed = 0;
1588 player->friends_still_needed = 0;
1590 for (j = 0; j < MAX_NUM_KEYS; j++)
1591 player->key[j] = FALSE;
1593 player->dynabomb_count = 0;
1594 player->dynabomb_size = 1;
1595 player->dynabombs_left = 0;
1596 player->dynabomb_xl = FALSE;
1598 player->MovDir = MV_NO_MOVING;
1601 player->GfxDir = MV_NO_MOVING;
1602 player->GfxAction = ACTION_DEFAULT;
1604 player->StepFrame = 0;
1606 player->use_murphy_graphic = FALSE;
1608 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1609 player->block_delay = -1; /* initialized in InitPlayerField() */
1611 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1613 player->actual_frame_counter = 0;
1615 player->step_counter = 0;
1617 player->last_move_dir = MV_NO_MOVING;
1619 player->is_waiting = FALSE;
1620 player->is_moving = FALSE;
1621 player->is_auto_moving = FALSE;
1622 player->is_digging = FALSE;
1623 player->is_snapping = FALSE;
1624 player->is_collecting = FALSE;
1625 player->is_pushing = FALSE;
1626 player->is_switching = FALSE;
1627 player->is_dropping = FALSE;
1629 player->is_bored = FALSE;
1630 player->is_sleeping = FALSE;
1632 player->frame_counter_bored = -1;
1633 player->frame_counter_sleeping = -1;
1635 player->anim_delay_counter = 0;
1636 player->post_delay_counter = 0;
1638 player->action_waiting = ACTION_DEFAULT;
1639 player->last_action_waiting = ACTION_DEFAULT;
1640 player->special_action_bored = ACTION_DEFAULT;
1641 player->special_action_sleeping = ACTION_DEFAULT;
1643 player->num_special_action_bored = 0;
1644 player->num_special_action_sleeping = 0;
1646 /* determine number of special actions for bored and sleeping animation */
1647 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1649 boolean found = FALSE;
1651 for (k = 0; k < NUM_DIRECTIONS; k++)
1652 if (el_act_dir2img(player->element_nr, j, k) !=
1653 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1657 player->num_special_action_bored++;
1661 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1663 boolean found = FALSE;
1665 for (k = 0; k < NUM_DIRECTIONS; k++)
1666 if (el_act_dir2img(player->element_nr, j, k) !=
1667 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1671 player->num_special_action_sleeping++;
1676 player->switch_x = -1;
1677 player->switch_y = -1;
1679 player->show_envelope = 0;
1681 player->move_delay = game.initial_move_delay;
1682 player->move_delay_value = game.initial_move_delay_value;
1684 player->move_delay_reset_counter = 0;
1686 #if USE_NEW_PUSH_DELAY
1687 player->push_delay = -1; /* initialized when pushing starts */
1688 player->push_delay_value = game.initial_push_delay_value;
1690 player->push_delay = 0;
1691 player->push_delay_value = game.initial_push_delay_value;
1694 player->drop_delay = 0;
1696 player->last_jx = player->last_jy = 0;
1697 player->jx = player->jy = 0;
1699 player->shield_normal_time_left = 0;
1700 player->shield_deadly_time_left = 0;
1702 player->inventory_infinite_element = EL_UNDEFINED;
1703 player->inventory_size = 0;
1705 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1706 SnapField(player, 0, 0);
1708 player->LevelSolved = FALSE;
1709 player->GameOver = FALSE;
1712 network_player_action_received = FALSE;
1714 #if defined(NETWORK_AVALIABLE)
1715 /* initial null action */
1716 if (network_playing)
1717 SendToServer_MovePlayer(MV_NO_MOVING);
1726 TimeLeft = level.time;
1729 ScreenMovDir = MV_NO_MOVING;
1733 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1735 AllPlayersGone = FALSE;
1737 game.yamyam_content_nr = 0;
1738 game.magic_wall_active = FALSE;
1739 game.magic_wall_time_left = 0;
1740 game.light_time_left = 0;
1741 game.timegate_time_left = 0;
1742 game.switchgate_pos = 0;
1743 game.balloon_dir = MV_NO_MOVING;
1744 game.gravity = level.initial_gravity;
1745 game.explosions_delayed = TRUE;
1747 game.envelope_active = FALSE;
1749 for (i = 0; i < NUM_BELTS; i++)
1751 game.belt_dir[i] = MV_NO_MOVING;
1752 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1755 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1756 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1758 for (x = 0; x < lev_fieldx; x++)
1760 for (y = 0; y < lev_fieldy; y++)
1762 Feld[x][y] = level.field[x][y];
1763 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1764 ChangeDelay[x][y] = 0;
1765 ChangePage[x][y] = -1;
1766 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1768 WasJustMoving[x][y] = 0;
1769 WasJustFalling[x][y] = 0;
1770 CheckCollision[x][y] = 0;
1772 Pushed[x][y] = FALSE;
1774 Changed[x][y] = CE_BITMASK_DEFAULT;
1775 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1777 ExplodePhase[x][y] = 0;
1778 ExplodeDelay[x][y] = 0;
1779 ExplodeField[x][y] = EX_TYPE_NONE;
1781 RunnerVisit[x][y] = 0;
1782 PlayerVisit[x][y] = 0;
1785 GfxRandom[x][y] = INIT_GFX_RANDOM();
1786 GfxElement[x][y] = EL_UNDEFINED;
1787 GfxAction[x][y] = ACTION_DEFAULT;
1788 GfxDir[x][y] = MV_NO_MOVING;
1792 for (y = 0; y < lev_fieldy; y++)
1794 for (x = 0; x < lev_fieldx; x++)
1796 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1798 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1800 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1803 InitField(x, y, TRUE);
1809 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1810 emulate_sb ? EMU_SOKOBAN :
1811 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1813 /* initialize explosion and ignition delay */
1814 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1816 if (!IS_CUSTOM_ELEMENT(i))
1819 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1820 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1821 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1822 int last_phase = (num_phase + 1) * delay;
1823 int half_phase = (num_phase / 2) * delay;
1825 element_info[i].explosion_delay = last_phase - 1;
1826 element_info[i].ignition_delay = half_phase;
1829 if (i == EL_BLACK_ORB)
1830 element_info[i].ignition_delay = 0;
1832 if (i == EL_BLACK_ORB)
1833 element_info[i].ignition_delay = 1;
1838 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1839 element_info[i].explosion_delay = 1;
1841 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1842 element_info[i].ignition_delay = 1;
1846 /* correct non-moving belts to start moving left */
1847 for (i = 0; i < NUM_BELTS; i++)
1848 if (game.belt_dir[i] == MV_NO_MOVING)
1849 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1851 /* check if any connected player was not found in playfield */
1852 for (i = 0; i < MAX_PLAYERS; i++)
1854 struct PlayerInfo *player = &stored_player[i];
1856 if (player->connected && !player->present)
1858 for (j = 0; j < MAX_PLAYERS; j++)
1860 struct PlayerInfo *some_player = &stored_player[j];
1861 int jx = some_player->jx, jy = some_player->jy;
1863 /* assign first free player found that is present in the playfield */
1864 if (some_player->present && !some_player->connected)
1866 player->present = TRUE;
1867 player->active = TRUE;
1869 some_player->present = FALSE;
1870 some_player->active = FALSE;
1873 player->element_nr = some_player->element_nr;
1876 #if USE_NEW_BLOCK_STYLE
1877 player->block_last_field = some_player->block_last_field;
1878 player->block_delay = some_player->block_delay;
1881 StorePlayer[jx][jy] = player->element_nr;
1882 player->jx = player->last_jx = jx;
1883 player->jy = player->last_jy = jy;
1893 /* when playing a tape, eliminate all players which do not participate */
1895 for (i = 0; i < MAX_PLAYERS; i++)
1897 if (stored_player[i].active && !tape.player_participates[i])
1899 struct PlayerInfo *player = &stored_player[i];
1900 int jx = player->jx, jy = player->jy;
1902 player->active = FALSE;
1903 StorePlayer[jx][jy] = 0;
1904 Feld[jx][jy] = EL_EMPTY;
1908 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1910 /* when in single player mode, eliminate all but the first active player */
1912 for (i = 0; i < MAX_PLAYERS; i++)
1914 if (stored_player[i].active)
1916 for (j = i + 1; j < MAX_PLAYERS; j++)
1918 if (stored_player[j].active)
1920 struct PlayerInfo *player = &stored_player[j];
1921 int jx = player->jx, jy = player->jy;
1923 player->active = FALSE;
1924 player->present = FALSE;
1926 StorePlayer[jx][jy] = 0;
1927 Feld[jx][jy] = EL_EMPTY;
1934 /* when recording the game, store which players take part in the game */
1937 for (i = 0; i < MAX_PLAYERS; i++)
1938 if (stored_player[i].active)
1939 tape.player_participates[i] = TRUE;
1944 for (i = 0; i < MAX_PLAYERS; i++)
1946 struct PlayerInfo *player = &stored_player[i];
1948 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1953 if (local_player == player)
1954 printf("Player %d is local player.\n", i+1);
1958 if (BorderElement == EL_EMPTY)
1961 SBX_Right = lev_fieldx - SCR_FIELDX;
1963 SBY_Lower = lev_fieldy - SCR_FIELDY;
1968 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1970 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1973 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1974 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1976 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1977 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1979 /* if local player not found, look for custom element that might create
1980 the player (make some assumptions about the right custom element) */
1981 if (!local_player->present)
1983 int start_x = 0, start_y = 0;
1984 int found_rating = 0;
1985 int found_element = EL_UNDEFINED;
1987 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1989 int element = Feld[x][y];
1994 if (!IS_CUSTOM_ELEMENT(element))
1997 if (CAN_CHANGE(element))
1999 for (i = 0; i < element_info[element].num_change_pages; i++)
2001 content = element_info[element].change_page[i].target_element;
2002 is_player = ELEM_IS_PLAYER(content);
2004 if (is_player && (found_rating < 3 || element < found_element))
2010 found_element = element;
2015 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2017 content = element_info[element].content[xx][yy];
2018 is_player = ELEM_IS_PLAYER(content);
2020 if (is_player && (found_rating < 2 || element < found_element))
2022 start_x = x + xx - 1;
2023 start_y = y + yy - 1;
2026 found_element = element;
2029 if (!CAN_CHANGE(element))
2032 for (i = 0; i < element_info[element].num_change_pages; i++)
2034 content= element_info[element].change_page[i].target_content[xx][yy];
2035 is_player = ELEM_IS_PLAYER(content);
2037 if (is_player && (found_rating < 1 || element < found_element))
2039 start_x = x + xx - 1;
2040 start_y = y + yy - 1;
2043 found_element = element;
2049 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2050 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2053 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2054 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2060 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2061 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2062 local_player->jx - MIDPOSX);
2064 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2065 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2066 local_player->jy - MIDPOSY);
2068 scroll_x = SBX_Left;
2069 scroll_y = SBY_Upper;
2070 if (local_player->jx >= SBX_Left + MIDPOSX)
2071 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2072 local_player->jx - MIDPOSX :
2074 if (local_player->jy >= SBY_Upper + MIDPOSY)
2075 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2076 local_player->jy - MIDPOSY :
2081 CloseDoor(DOOR_CLOSE_1);
2083 /* !!! FIX THIS (START) !!! */
2084 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2086 InitGameEngine_EM();
2093 /* after drawing the level, correct some elements */
2094 if (game.timegate_time_left == 0)
2095 CloseAllOpenTimegates();
2097 if (setup.soft_scrolling)
2098 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2100 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2103 /* !!! FIX THIS (END) !!! */
2105 /* copy default game door content to main double buffer */
2106 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2107 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2109 DrawGameDoorValues();
2113 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2114 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2115 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2119 /* copy actual game door content to door double buffer for OpenDoor() */
2120 BlitBitmap(drawto, bitmap_db_door,
2121 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2123 OpenDoor(DOOR_OPEN_ALL);
2125 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2127 if (setup.sound_music)
2130 KeyboardAutoRepeatOffUnlessAutoplay();
2134 for (i = 0; i < MAX_PLAYERS; i++)
2135 printf("Player %d %sactive.\n",
2136 i + 1, (stored_player[i].active ? "" : "not "));
2140 printf("::: starting game [%d]\n", FrameCounter);
2144 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2146 /* this is used for non-R'n'D game engines to update certain engine values */
2148 /* needed to determine if sounds are played within the visible screen area */
2149 scroll_x = actual_scroll_x;
2150 scroll_y = actual_scroll_y;
2153 void InitMovDir(int x, int y)
2155 int i, element = Feld[x][y];
2156 static int xy[4][2] =
2163 static int direction[3][4] =
2165 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2166 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2167 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2176 Feld[x][y] = EL_BUG;
2177 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2180 case EL_SPACESHIP_RIGHT:
2181 case EL_SPACESHIP_UP:
2182 case EL_SPACESHIP_LEFT:
2183 case EL_SPACESHIP_DOWN:
2184 Feld[x][y] = EL_SPACESHIP;
2185 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2188 case EL_BD_BUTTERFLY_RIGHT:
2189 case EL_BD_BUTTERFLY_UP:
2190 case EL_BD_BUTTERFLY_LEFT:
2191 case EL_BD_BUTTERFLY_DOWN:
2192 Feld[x][y] = EL_BD_BUTTERFLY;
2193 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2196 case EL_BD_FIREFLY_RIGHT:
2197 case EL_BD_FIREFLY_UP:
2198 case EL_BD_FIREFLY_LEFT:
2199 case EL_BD_FIREFLY_DOWN:
2200 Feld[x][y] = EL_BD_FIREFLY;
2201 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2204 case EL_PACMAN_RIGHT:
2206 case EL_PACMAN_LEFT:
2207 case EL_PACMAN_DOWN:
2208 Feld[x][y] = EL_PACMAN;
2209 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2212 case EL_SP_SNIKSNAK:
2213 MovDir[x][y] = MV_UP;
2216 case EL_SP_ELECTRON:
2217 MovDir[x][y] = MV_LEFT;
2224 Feld[x][y] = EL_MOLE;
2225 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2229 if (IS_CUSTOM_ELEMENT(element))
2231 struct ElementInfo *ei = &element_info[element];
2232 int move_direction_initial = ei->move_direction_initial;
2233 int move_pattern = ei->move_pattern;
2235 if (move_direction_initial == MV_START_PREVIOUS)
2237 if (MovDir[x][y] != MV_NO_MOVING)
2240 move_direction_initial = MV_START_AUTOMATIC;
2243 if (move_direction_initial == MV_START_RANDOM)
2244 MovDir[x][y] = 1 << RND(4);
2245 else if (move_direction_initial & MV_ANY_DIRECTION)
2246 MovDir[x][y] = move_direction_initial;
2247 else if (move_pattern == MV_ALL_DIRECTIONS ||
2248 move_pattern == MV_TURNING_LEFT ||
2249 move_pattern == MV_TURNING_RIGHT ||
2250 move_pattern == MV_TURNING_LEFT_RIGHT ||
2251 move_pattern == MV_TURNING_RIGHT_LEFT ||
2252 move_pattern == MV_TURNING_RANDOM)
2253 MovDir[x][y] = 1 << RND(4);
2254 else if (move_pattern == MV_HORIZONTAL)
2255 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2256 else if (move_pattern == MV_VERTICAL)
2257 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2258 else if (move_pattern & MV_ANY_DIRECTION)
2259 MovDir[x][y] = element_info[element].move_pattern;
2260 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2261 move_pattern == MV_ALONG_RIGHT_SIDE)
2264 /* use random direction as default start direction */
2265 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2266 MovDir[x][y] = 1 << RND(4);
2269 for (i = 0; i < NUM_DIRECTIONS; i++)
2271 int x1 = x + xy[i][0];
2272 int y1 = y + xy[i][1];
2274 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2276 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2277 MovDir[x][y] = direction[0][i];
2279 MovDir[x][y] = direction[1][i];
2288 MovDir[x][y] = 1 << RND(4);
2290 if (element != EL_BUG &&
2291 element != EL_SPACESHIP &&
2292 element != EL_BD_BUTTERFLY &&
2293 element != EL_BD_FIREFLY)
2296 for (i = 0; i < NUM_DIRECTIONS; i++)
2298 int x1 = x + xy[i][0];
2299 int y1 = y + xy[i][1];
2301 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2303 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2305 MovDir[x][y] = direction[0][i];
2308 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2309 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2311 MovDir[x][y] = direction[1][i];
2320 GfxDir[x][y] = MovDir[x][y];
2323 void InitAmoebaNr(int x, int y)
2326 int group_nr = AmoebeNachbarNr(x, y);
2330 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2332 if (AmoebaCnt[i] == 0)
2340 AmoebaNr[x][y] = group_nr;
2341 AmoebaCnt[group_nr]++;
2342 AmoebaCnt2[group_nr]++;
2348 boolean raise_level = FALSE;
2350 if (local_player->MovPos)
2354 if (tape.auto_play) /* tape might already be stopped here */
2355 tape.auto_play_level_solved = TRUE;
2357 if (tape.playing && tape.auto_play)
2358 tape.auto_play_level_solved = TRUE;
2361 local_player->LevelSolved = FALSE;
2363 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2367 if (!tape.playing && setup.sound_loops)
2368 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2369 SND_CTRL_PLAY_LOOP);
2371 while (TimeLeft > 0)
2373 if (!tape.playing && !setup.sound_loops)
2374 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2375 if (TimeLeft > 0 && !(TimeLeft % 10))
2376 RaiseScore(level.score[SC_TIME_BONUS]);
2377 if (TimeLeft > 100 && !(TimeLeft % 10))
2382 DrawGameValue_Time(TimeLeft);
2390 if (!tape.playing && setup.sound_loops)
2391 StopSound(SND_GAME_LEVELTIME_BONUS);
2393 else if (level.time == 0) /* level without time limit */
2395 if (!tape.playing && setup.sound_loops)
2396 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2397 SND_CTRL_PLAY_LOOP);
2399 while (TimePlayed < 999)
2401 if (!tape.playing && !setup.sound_loops)
2402 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2403 if (TimePlayed < 999 && !(TimePlayed % 10))
2404 RaiseScore(level.score[SC_TIME_BONUS]);
2405 if (TimePlayed < 900 && !(TimePlayed % 10))
2410 DrawGameValue_Time(TimePlayed);
2418 if (!tape.playing && setup.sound_loops)
2419 StopSound(SND_GAME_LEVELTIME_BONUS);
2422 /* close exit door after last player */
2423 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2424 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2425 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2427 int element = Feld[ExitX][ExitY];
2429 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2430 EL_SP_EXIT_CLOSING);
2432 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2435 /* Hero disappears */
2436 if (ExitX >= 0 && ExitY >= 0)
2437 DrawLevelField(ExitX, ExitY);
2444 CloseDoor(DOOR_CLOSE_1);
2449 SaveTape(tape.level_nr); /* Ask to save tape */
2452 if (level_nr == leveldir_current->handicap_level)
2454 leveldir_current->handicap_level++;
2455 SaveLevelSetup_SeriesInfo();
2458 if (level_editor_test_game)
2459 local_player->score = -1; /* no highscore when playing from editor */
2460 else if (level_nr < leveldir_current->last_level)
2461 raise_level = TRUE; /* advance to next level */
2463 if ((hi_pos = NewHiScore()) >= 0)
2465 game_status = GAME_MODE_SCORES;
2466 DrawHallOfFame(hi_pos);
2475 game_status = GAME_MODE_MAIN;
2492 LoadScore(level_nr);
2494 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2495 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2498 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2500 if (local_player->score > highscore[k].Score)
2502 /* player has made it to the hall of fame */
2504 if (k < MAX_SCORE_ENTRIES - 1)
2506 int m = MAX_SCORE_ENTRIES - 1;
2509 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2510 if (!strcmp(setup.player_name, highscore[l].Name))
2512 if (m == k) /* player's new highscore overwrites his old one */
2516 for (l = m; l > k; l--)
2518 strcpy(highscore[l].Name, highscore[l - 1].Name);
2519 highscore[l].Score = highscore[l - 1].Score;
2526 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2527 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2528 highscore[k].Score = local_player->score;
2534 else if (!strncmp(setup.player_name, highscore[k].Name,
2535 MAX_PLAYER_NAME_LEN))
2536 break; /* player already there with a higher score */
2542 SaveScore(level_nr);
2547 inline static int getElementMoveStepsize(int x, int y)
2549 int element = Feld[x][y];
2550 int direction = MovDir[x][y];
2551 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2552 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2553 int horiz_move = (dx != 0);
2554 int sign = (horiz_move ? dx : dy);
2555 int step = sign * element_info[element].move_stepsize;
2557 /* special values for move stepsize for spring and things on conveyor belt */
2561 if (element == EL_SPRING)
2562 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2563 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2564 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2565 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2567 if (CAN_FALL(element) &&
2568 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2569 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2570 else if (element == EL_SPRING)
2571 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2578 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2580 if (player->GfxAction != action || player->GfxDir != dir)
2583 printf("Player frame reset! (%d => %d, %d => %d)\n",
2584 player->GfxAction, action, player->GfxDir, dir);
2587 player->GfxAction = action;
2588 player->GfxDir = dir;
2590 player->StepFrame = 0;
2594 static void ResetRandomAnimationValue(int x, int y)
2596 GfxRandom[x][y] = INIT_GFX_RANDOM();
2599 static void ResetGfxAnimation(int x, int y)
2602 GfxAction[x][y] = ACTION_DEFAULT;
2603 GfxDir[x][y] = MovDir[x][y];
2606 void InitMovingField(int x, int y, int direction)
2608 int element = Feld[x][y];
2609 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2610 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2614 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2615 ResetGfxAnimation(x, y);
2619 MovDir[x][y] = direction;
2620 GfxDir[x][y] = direction;
2621 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2622 ACTION_FALLING : ACTION_MOVING);
2624 if (getElementMoveStepsize(x, y) != 0)
2626 if (Feld[newx][newy] == EL_EMPTY)
2627 Feld[newx][newy] = EL_BLOCKED;
2629 MovDir[newx][newy] = MovDir[x][y];
2630 GfxFrame[newx][newy] = GfxFrame[x][y];
2631 GfxRandom[newx][newy] = GfxRandom[x][y];
2632 GfxAction[newx][newy] = GfxAction[x][y];
2633 GfxDir[newx][newy] = GfxDir[x][y];
2638 MovDir[newx][newy] = MovDir[x][y] = direction;
2639 GfxDir[x][y] = direction;
2641 if (Feld[newx][newy] == EL_EMPTY)
2642 Feld[newx][newy] = EL_BLOCKED;
2644 if (direction == MV_DOWN && CAN_FALL(element))
2645 GfxAction[x][y] = ACTION_FALLING;
2647 GfxAction[x][y] = ACTION_MOVING;
2649 GfxFrame[newx][newy] = GfxFrame[x][y];
2650 GfxRandom[newx][newy] = GfxRandom[x][y];
2651 GfxAction[newx][newy] = GfxAction[x][y];
2652 GfxDir[newx][newy] = GfxDir[x][y];
2656 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2658 int direction = MovDir[x][y];
2659 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2660 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2666 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2668 int oldx = x, oldy = y;
2669 int direction = MovDir[x][y];
2671 if (direction == MV_LEFT)
2673 else if (direction == MV_RIGHT)
2675 else if (direction == MV_UP)
2677 else if (direction == MV_DOWN)
2680 *comes_from_x = oldx;
2681 *comes_from_y = oldy;
2684 int MovingOrBlocked2Element(int x, int y)
2686 int element = Feld[x][y];
2688 if (element == EL_BLOCKED)
2692 Blocked2Moving(x, y, &oldx, &oldy);
2693 return Feld[oldx][oldy];
2699 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2701 /* like MovingOrBlocked2Element(), but if element is moving
2702 and (x,y) is the field the moving element is just leaving,
2703 return EL_BLOCKED instead of the element value */
2704 int element = Feld[x][y];
2706 if (IS_MOVING(x, y))
2708 if (element == EL_BLOCKED)
2712 Blocked2Moving(x, y, &oldx, &oldy);
2713 return Feld[oldx][oldy];
2722 static void RemoveField(int x, int y)
2724 Feld[x][y] = EL_EMPTY;
2731 ChangeDelay[x][y] = 0;
2732 ChangePage[x][y] = -1;
2733 Pushed[x][y] = FALSE;
2736 ExplodeField[x][y] = EX_TYPE_NONE;
2739 GfxElement[x][y] = EL_UNDEFINED;
2740 GfxAction[x][y] = ACTION_DEFAULT;
2741 GfxDir[x][y] = MV_NO_MOVING;
2744 void RemoveMovingField(int x, int y)
2746 int oldx = x, oldy = y, newx = x, newy = y;
2747 int element = Feld[x][y];
2748 int next_element = EL_UNDEFINED;
2750 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2753 if (IS_MOVING(x, y))
2755 Moving2Blocked(x, y, &newx, &newy);
2757 if (Feld[newx][newy] != EL_BLOCKED)
2760 if (Feld[newx][newy] != EL_BLOCKED)
2762 /* element is moving, but target field is not free (blocked), but
2763 already occupied by something different (example: acid pool);
2764 in this case, only remove the moving field, but not the target */
2766 RemoveField(oldx, oldy);
2768 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2770 DrawLevelField(oldx, oldy);
2776 else if (element == EL_BLOCKED)
2778 Blocked2Moving(x, y, &oldx, &oldy);
2779 if (!IS_MOVING(oldx, oldy))
2783 if (element == EL_BLOCKED &&
2784 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2785 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2786 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2787 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2788 next_element = get_next_element(Feld[oldx][oldy]);
2790 RemoveField(oldx, oldy);
2791 RemoveField(newx, newy);
2793 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2795 if (next_element != EL_UNDEFINED)
2796 Feld[oldx][oldy] = next_element;
2798 DrawLevelField(oldx, oldy);
2799 DrawLevelField(newx, newy);
2802 void DrawDynamite(int x, int y)
2804 int sx = SCREENX(x), sy = SCREENY(y);
2805 int graphic = el2img(Feld[x][y]);
2808 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2811 if (IS_WALKABLE_INSIDE(Back[x][y]))
2815 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2816 else if (Store[x][y])
2817 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2819 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2822 if (Back[x][y] || Store[x][y])
2823 DrawGraphicThruMask(sx, sy, graphic, frame);
2825 DrawGraphic(sx, sy, graphic, frame);
2827 if (game.emulation == EMU_SUPAPLEX)
2828 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2829 else if (Store[x][y])
2830 DrawGraphicThruMask(sx, sy, graphic, frame);
2832 DrawGraphic(sx, sy, graphic, frame);
2836 void CheckDynamite(int x, int y)
2838 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2842 if (MovDelay[x][y] != 0)
2845 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2852 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2854 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2855 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2856 StopSound(SND_DYNAMITE_ACTIVE);
2858 StopSound(SND_DYNABOMB_ACTIVE);
2864 void DrawRelocatePlayer(struct PlayerInfo *player)
2866 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2867 boolean no_delay = (tape.warp_forward);
2868 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2869 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2870 int jx = player->jx;
2871 int jy = player->jy;
2873 if (level.instant_relocation)
2876 int offset = (setup.scroll_delay ? 3 : 0);
2878 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2880 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2881 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2882 local_player->jx - MIDPOSX);
2884 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2885 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2886 local_player->jy - MIDPOSY);
2890 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2891 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2892 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2894 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2895 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2896 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2898 /* don't scroll over playfield boundaries */
2899 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2900 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2902 /* don't scroll over playfield boundaries */
2903 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2904 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2907 scroll_x += (local_player->jx - old_jx);
2908 scroll_y += (local_player->jy - old_jy);
2910 /* don't scroll over playfield boundaries */
2911 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2912 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2914 /* don't scroll over playfield boundaries */
2915 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2916 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2919 RedrawPlayfield(TRUE, 0,0,0,0);
2925 int offset = (setup.scroll_delay ? 3 : 0);
2927 int scroll_xx = -999, scroll_yy = -999;
2929 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2931 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2934 int fx = FX, fy = FY;
2936 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2937 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2938 local_player->jx - MIDPOSX);
2940 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2941 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2942 local_player->jy - MIDPOSY);
2944 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2945 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2948 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2951 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2958 fx += dx * TILEX / 2;
2959 fy += dy * TILEY / 2;
2961 ScrollLevel(dx, dy);
2964 /* scroll in two steps of half tile size to make things smoother */
2965 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2967 Delay(wait_delay_value);
2969 /* scroll second step to align at full tile size */
2971 Delay(wait_delay_value);
2974 int scroll_xx = -999, scroll_yy = -999;
2976 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2978 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2981 int fx = FX, fy = FY;
2983 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2984 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2985 local_player->jx - MIDPOSX);
2987 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2988 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2989 local_player->jy - MIDPOSY);
2991 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2992 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2995 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2998 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3005 fx += dx * TILEX / 2;
3006 fy += dy * TILEY / 2;
3008 ScrollLevel(dx, dy);
3011 /* scroll in two steps of half tile size to make things smoother */
3012 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3014 Delay(wait_delay_value);
3016 /* scroll second step to align at full tile size */
3018 Delay(wait_delay_value);
3024 Delay(wait_delay_value);
3028 void RelocatePlayer(int jx, int jy, int el_player_raw)
3031 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3033 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3035 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3036 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3037 boolean no_delay = (tape.warp_forward);
3038 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3039 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3040 int old_jx = player->jx;
3041 int old_jy = player->jy;
3042 int old_element = Feld[old_jx][old_jy];
3043 int element = Feld[jx][jy];
3044 boolean player_relocated = (old_jx != jx || old_jy != jy);
3046 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3047 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3049 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3050 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3051 int leave_side_horiz = move_dir_horiz;
3052 int leave_side_vert = move_dir_vert;
3054 static int trigger_sides[4][2] =
3056 /* enter side leave side */
3057 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3058 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3059 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3060 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3062 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3063 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3064 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3065 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3067 int enter_side = enter_side_horiz | enter_side_vert;
3068 int leave_side = leave_side_horiz | leave_side_vert;
3070 if (player->GameOver) /* do not reanimate dead player */
3073 if (!player_relocated) /* no need to relocate the player */
3076 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3078 RemoveField(jx, jy); /* temporarily remove newly placed player */
3079 DrawLevelField(jx, jy);
3082 if (player->present)
3084 while (player->MovPos)
3086 ScrollPlayer(player, SCROLL_GO_ON);
3087 ScrollScreen(NULL, SCROLL_GO_ON);
3089 #if USE_NEW_MOVE_DELAY
3090 AdvanceFrameAndPlayerCounters(player->index_nr);
3098 Delay(wait_delay_value);
3101 DrawPlayer(player); /* needed here only to cleanup last field */
3102 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3104 player->is_moving = FALSE;
3108 if (IS_CUSTOM_ELEMENT(old_element))
3109 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3111 player->index_bit, leave_side);
3113 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3115 player->index_bit, leave_side);
3118 Feld[jx][jy] = el_player;
3119 InitPlayerField(jx, jy, el_player, TRUE);
3121 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3123 Feld[jx][jy] = element;
3124 InitField(jx, jy, FALSE);
3128 if (player == local_player) /* only visually relocate local player */
3129 DrawRelocatePlayer(player);
3133 TestIfHeroTouchesBadThing(jx, jy);
3134 TestIfPlayerTouchesCustomElement(jx, jy);
3138 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3143 /* needed to allow change of walkable custom element by entering player */
3144 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3145 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3147 /* needed to allow change of walkable custom element by entering player */
3148 Changed[jx][jy] = 0; /* allow another change */
3153 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3154 enter_side == MV_LEFT ? "left" :
3155 enter_side == MV_RIGHT ? "right" :
3156 enter_side == MV_UP ? "top" :
3157 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3161 if (IS_CUSTOM_ELEMENT(element))
3162 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3163 player->index_bit, enter_side);
3165 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3166 CE_OTHER_GETS_ENTERED,
3167 player->index_bit, enter_side);
3171 void Explode(int ex, int ey, int phase, int mode)
3178 /* !!! eliminate this variable !!! */
3179 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3184 int last_phase = num_phase * delay;
3185 int half_phase = (num_phase / 2) * delay;
3186 int first_phase_after_start = EX_PHASE_START + 1;
3190 if (game.explosions_delayed)
3192 ExplodeField[ex][ey] = mode;
3196 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3198 int center_element = Feld[ex][ey];
3201 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3205 /* --- This is only really needed (and now handled) in "Impact()". --- */
3206 /* do not explode moving elements that left the explode field in time */
3207 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3208 center_element == EL_EMPTY &&
3209 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3214 if (mode == EX_TYPE_NORMAL ||
3215 mode == EX_TYPE_CENTER ||
3216 mode == EX_TYPE_CROSS)
3217 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3219 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3220 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3223 /* remove things displayed in background while burning dynamite */
3224 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3227 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3229 /* put moving element to center field (and let it explode there) */
3230 center_element = MovingOrBlocked2Element(ex, ey);
3231 RemoveMovingField(ex, ey);
3232 Feld[ex][ey] = center_element;
3238 last_phase = element_info[center_element].explosion_delay + 1;
3240 last_phase = element_info[center_element].explosion_delay;
3244 printf("::: %d -> %d\n", center_element, last_phase);
3248 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3250 int xx = x - ex + 1;
3251 int yy = y - ey + 1;
3256 if (!IN_LEV_FIELD(x, y) ||
3257 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3258 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3261 if (!IN_LEV_FIELD(x, y) ||
3262 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3266 if (!IN_LEV_FIELD(x, y) ||
3267 ((mode != EX_TYPE_NORMAL ||
3268 center_element == EL_AMOEBA_TO_DIAMOND) &&
3269 (x != ex || y != ey)))
3273 element = Feld[x][y];
3275 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3277 element = MovingOrBlocked2Element(x, y);
3279 if (!IS_EXPLOSION_PROOF(element))
3280 RemoveMovingField(x, y);
3286 if (IS_EXPLOSION_PROOF(element))
3289 /* indestructible elements can only explode in center (but not flames) */
3291 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3292 mode == EX_TYPE_BORDER)) ||
3293 element == EL_FLAMES)
3296 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3297 element == EL_FLAMES)
3303 if ((IS_INDESTRUCTIBLE(element) &&
3304 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3305 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3306 element == EL_FLAMES)
3311 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3312 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3313 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3315 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3318 if (IS_ACTIVE_BOMB(element))
3320 /* re-activate things under the bomb like gate or penguin */
3322 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3325 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3330 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3331 element_info[Feld[x][y]].token_name,
3332 Store[x][y], Store2[x][y]);
3339 /* save walkable background elements while explosion on same tile */
3341 if (IS_INDESTRUCTIBLE(element))
3342 Back[x][y] = element;
3346 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3347 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3348 Back[x][y] = element;
3350 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3351 (x != ex || y != ey))
3352 Back[x][y] = element;
3355 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3356 Back[x][y] = element;
3360 /* ignite explodable elements reached by other explosion */
3361 if (element == EL_EXPLOSION)
3362 element = Store2[x][y];
3365 if (AmoebaNr[x][y] &&
3366 (element == EL_AMOEBA_FULL ||
3367 element == EL_BD_AMOEBA ||
3368 element == EL_AMOEBA_GROWING))
3370 AmoebaCnt[AmoebaNr[x][y]]--;
3371 AmoebaCnt2[AmoebaNr[x][y]]--;
3377 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3379 switch(StorePlayer[ex][ey])
3382 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3385 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3388 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3392 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3397 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3398 Store[x][y] = EL_EMPTY;
3400 if (game.emulation == EMU_SUPAPLEX)
3401 Store[x][y] = EL_EMPTY;
3404 else if (center_element == EL_MOLE)
3405 Store[x][y] = EL_EMERALD_RED;
3406 else if (center_element == EL_PENGUIN)
3407 Store[x][y] = EL_EMERALD_PURPLE;
3408 else if (center_element == EL_BUG)
3409 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3410 else if (center_element == EL_BD_BUTTERFLY)
3411 Store[x][y] = EL_BD_DIAMOND;
3412 else if (center_element == EL_SP_ELECTRON)
3413 Store[x][y] = EL_SP_INFOTRON;
3414 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3415 Store[x][y] = level.amoeba_content;
3416 else if (center_element == EL_YAMYAM)
3417 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3418 else if (IS_CUSTOM_ELEMENT(center_element) &&
3419 element_info[center_element].content[xx][yy] != EL_EMPTY)
3420 Store[x][y] = element_info[center_element].content[xx][yy];
3421 else if (element == EL_WALL_EMERALD)
3422 Store[x][y] = EL_EMERALD;
3423 else if (element == EL_WALL_DIAMOND)
3424 Store[x][y] = EL_DIAMOND;
3425 else if (element == EL_WALL_BD_DIAMOND)
3426 Store[x][y] = EL_BD_DIAMOND;
3427 else if (element == EL_WALL_EMERALD_YELLOW)
3428 Store[x][y] = EL_EMERALD_YELLOW;
3429 else if (element == EL_WALL_EMERALD_RED)
3430 Store[x][y] = EL_EMERALD_RED;
3431 else if (element == EL_WALL_EMERALD_PURPLE)
3432 Store[x][y] = EL_EMERALD_PURPLE;
3433 else if (element == EL_WALL_PEARL)
3434 Store[x][y] = EL_PEARL;
3435 else if (element == EL_WALL_CRYSTAL)
3436 Store[x][y] = EL_CRYSTAL;
3437 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3438 Store[x][y] = element_info[element].content[1][1];
3440 Store[x][y] = EL_EMPTY;
3442 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3443 center_element == EL_AMOEBA_TO_DIAMOND)
3444 Store2[x][y] = element;
3447 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3448 element_info[Store2[x][y]].token_name);
3452 if (AmoebaNr[x][y] &&
3453 (element == EL_AMOEBA_FULL ||
3454 element == EL_BD_AMOEBA ||
3455 element == EL_AMOEBA_GROWING))
3457 AmoebaCnt[AmoebaNr[x][y]]--;
3458 AmoebaCnt2[AmoebaNr[x][y]]--;
3464 MovDir[x][y] = MovPos[x][y] = 0;
3465 GfxDir[x][y] = MovDir[x][y];
3470 Feld[x][y] = EL_EXPLOSION;
3472 GfxElement[x][y] = center_element;
3474 GfxElement[x][y] = EL_UNDEFINED;
3477 ExplodePhase[x][y] = 1;
3479 ExplodeDelay[x][y] = last_phase;
3484 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3486 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3493 if (center_element == EL_YAMYAM)
3494 game.yamyam_content_nr =
3495 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3498 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3499 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3513 GfxFrame[x][y] = 0; /* restart explosion animation */
3517 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3521 last_phase = ExplodeDelay[x][y];
3524 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3528 /* activate this even in non-DEBUG version until cause for crash in
3529 getGraphicAnimationFrame() (see below) is found and eliminated */
3533 if (GfxElement[x][y] == EL_UNDEFINED)
3536 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3537 printf("Explode(): This should never happen!\n");
3540 GfxElement[x][y] = EL_EMPTY;
3546 border_element = Store2[x][y];
3548 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3549 border_element = StorePlayer[x][y];
3551 if (IS_PLAYER(x, y))
3552 border_element = StorePlayer[x][y];
3556 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3557 element_info[border_element].token_name, Store2[x][y]);
3561 printf("::: phase == %d\n", phase);
3564 if (phase == element_info[border_element].ignition_delay ||
3565 phase == last_phase)
3567 boolean border_explosion = FALSE;
3571 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3572 !PLAYER_EXPLOSION_PROTECTED(x, y))
3574 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3577 if (IS_PLAYER(x, y))
3580 KillHeroUnlessExplosionProtected(x, y);
3581 border_explosion = TRUE;
3584 if (phase == last_phase)
3585 printf("::: IS_PLAYER\n");
3588 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3591 printf("::: %d,%d: %d %s\n", x, y, border_element,
3592 element_info[border_element].token_name);
3595 Feld[x][y] = Store2[x][y];
3598 border_explosion = TRUE;
3601 if (phase == last_phase)
3602 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3605 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3607 AmoebeUmwandeln(x, y);
3609 border_explosion = TRUE;
3612 if (phase == last_phase)
3613 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3614 element_info[border_element].explosion_delay,
3615 element_info[border_element].ignition_delay,
3621 /* if an element just explodes due to another explosion (chain-reaction),
3622 do not immediately end the new explosion when it was the last frame of
3623 the explosion (as it would be done in the following "if"-statement!) */
3624 if (border_explosion && phase == last_phase)
3631 if (phase == first_phase_after_start)
3633 int element = Store2[x][y];
3635 if (element == EL_BLACK_ORB)
3637 Feld[x][y] = Store2[x][y];
3642 else if (phase == half_phase)
3644 int element = Store2[x][y];
3646 if (IS_PLAYER(x, y))
3647 KillHeroUnlessExplosionProtected(x, y);
3648 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3650 Feld[x][y] = Store2[x][y];
3654 else if (element == EL_AMOEBA_TO_DIAMOND)
3655 AmoebeUmwandeln(x, y);
3659 if (phase == last_phase)
3664 printf("::: done: phase == %d\n", phase);
3668 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3671 element = Feld[x][y] = Store[x][y];
3672 Store[x][y] = Store2[x][y] = 0;
3673 GfxElement[x][y] = EL_UNDEFINED;
3675 /* player can escape from explosions and might therefore be still alive */
3676 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3677 element <= EL_PLAYER_IS_EXPLODING_4)
3678 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3680 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3681 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3682 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3685 /* restore probably existing indestructible background element */
3686 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3687 element = Feld[x][y] = Back[x][y];
3690 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3691 GfxDir[x][y] = MV_NO_MOVING;
3692 ChangeDelay[x][y] = 0;
3693 ChangePage[x][y] = -1;
3696 InitField_WithBug2(x, y, FALSE);
3698 InitField(x, y, FALSE);
3700 /* !!! not needed !!! */
3702 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3703 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3706 if (CAN_MOVE(element))
3711 DrawLevelField(x, y);
3713 TestIfElementTouchesCustomElement(x, y);
3715 if (GFX_CRUMBLED(element))
3716 DrawLevelFieldCrumbledSandNeighbours(x, y);
3718 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3719 StorePlayer[x][y] = 0;
3721 if (ELEM_IS_PLAYER(element))
3722 RelocatePlayer(x, y, element);
3725 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3727 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3731 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3733 int stored = Store[x][y];
3734 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3735 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3739 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3741 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3745 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3749 printf("::: %d / %d [%d - %d]\n",
3750 GfxFrame[x][y], phase - delay, phase, delay);
3754 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3755 element_info[GfxElement[x][y]].token_name,
3760 DrawLevelFieldCrumbledSand(x, y);
3762 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3764 DrawLevelElement(x, y, Back[x][y]);
3765 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3767 else if (IS_WALKABLE_UNDER(Back[x][y]))
3769 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3770 DrawLevelElementThruMask(x, y, Back[x][y]);
3772 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3773 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3777 void DynaExplode(int ex, int ey)
3780 int dynabomb_element = Feld[ex][ey];
3781 int dynabomb_size = 1;
3782 boolean dynabomb_xl = FALSE;
3783 struct PlayerInfo *player;
3784 static int xy[4][2] =
3792 if (IS_ACTIVE_BOMB(dynabomb_element))
3794 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3795 dynabomb_size = player->dynabomb_size;
3796 dynabomb_xl = player->dynabomb_xl;
3797 player->dynabombs_left++;
3800 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3802 for (i = 0; i < NUM_DIRECTIONS; i++)
3804 for (j = 1; j <= dynabomb_size; j++)
3806 int x = ex + j * xy[i][0];
3807 int y = ey + j * xy[i][1];
3810 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3813 element = Feld[x][y];
3815 /* do not restart explosions of fields with active bombs */
3816 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3819 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3823 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3824 !IS_DIGGABLE(element) && !dynabomb_xl)
3827 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3828 !CAN_GROW_INTO(element) && !dynabomb_xl)
3832 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3833 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3834 element != EL_SAND && !dynabomb_xl)
3841 void Bang(int x, int y)
3844 int element = MovingOrBlocked2Element(x, y);
3846 int element = Feld[x][y];
3850 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3852 if (IS_PLAYER(x, y))
3855 struct PlayerInfo *player = PLAYERINFO(x, y);
3857 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3858 player->element_nr);
3863 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3865 if (game.emulation == EMU_SUPAPLEX)
3866 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3868 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3873 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3881 case EL_BD_BUTTERFLY:
3884 case EL_DARK_YAMYAM:
3888 RaiseScoreElement(element);
3889 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3891 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3892 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3893 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3894 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3895 case EL_DYNABOMB_INCREASE_NUMBER:
3896 case EL_DYNABOMB_INCREASE_SIZE:
3897 case EL_DYNABOMB_INCREASE_POWER:
3902 case EL_LAMP_ACTIVE:
3904 case EL_AMOEBA_TO_DIAMOND:
3906 if (IS_PLAYER(x, y))
3907 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3909 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3913 if (element_info[element].explosion_type == EXPLODES_CROSS)
3915 if (CAN_EXPLODE_CROSS(element))
3918 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3923 else if (element_info[element].explosion_type == EXPLODES_1X1)
3925 else if (CAN_EXPLODE_1X1(element))
3927 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3929 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3933 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3936 void SplashAcid(int x, int y)
3939 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3940 (!IN_LEV_FIELD(x - 1, y - 2) ||
3941 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3942 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3944 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3945 (!IN_LEV_FIELD(x + 1, y - 2) ||
3946 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3947 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3949 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3951 /* input: position of element entering acid (obsolete) */
3953 int element = Feld[x][y];
3955 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3958 if (element != EL_ACID_SPLASH_LEFT &&
3959 element != EL_ACID_SPLASH_RIGHT)
3961 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3963 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3964 (!IN_LEV_FIELD(x - 1, y - 1) ||
3965 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3966 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3968 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3969 (!IN_LEV_FIELD(x + 1, y - 1) ||
3970 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3971 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3976 static void InitBeltMovement()
3978 static int belt_base_element[4] =
3980 EL_CONVEYOR_BELT_1_LEFT,
3981 EL_CONVEYOR_BELT_2_LEFT,
3982 EL_CONVEYOR_BELT_3_LEFT,
3983 EL_CONVEYOR_BELT_4_LEFT
3985 static int belt_base_active_element[4] =
3987 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3988 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3989 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3990 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3995 /* set frame order for belt animation graphic according to belt direction */
3996 for (i = 0; i < NUM_BELTS; i++)
4000 for (j = 0; j < NUM_BELT_PARTS; j++)
4002 int element = belt_base_active_element[belt_nr] + j;
4003 int graphic = el2img(element);
4005 if (game.belt_dir[i] == MV_LEFT)
4006 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4008 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4012 for (y = 0; y < lev_fieldy; y++)
4014 for (x = 0; x < lev_fieldx; x++)
4016 int element = Feld[x][y];
4018 for (i = 0; i < NUM_BELTS; i++)
4020 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4022 int e_belt_nr = getBeltNrFromBeltElement(element);
4025 if (e_belt_nr == belt_nr)
4027 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4029 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4037 static void ToggleBeltSwitch(int x, int y)
4039 static int belt_base_element[4] =
4041 EL_CONVEYOR_BELT_1_LEFT,
4042 EL_CONVEYOR_BELT_2_LEFT,
4043 EL_CONVEYOR_BELT_3_LEFT,
4044 EL_CONVEYOR_BELT_4_LEFT
4046 static int belt_base_active_element[4] =
4048 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4049 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4050 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4051 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4053 static int belt_base_switch_element[4] =
4055 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4056 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4057 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4058 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4060 static int belt_move_dir[4] =
4068 int element = Feld[x][y];
4069 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4070 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4071 int belt_dir = belt_move_dir[belt_dir_nr];
4074 if (!IS_BELT_SWITCH(element))
4077 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4078 game.belt_dir[belt_nr] = belt_dir;
4080 if (belt_dir_nr == 3)
4083 /* set frame order for belt animation graphic according to belt direction */
4084 for (i = 0; i < NUM_BELT_PARTS; i++)
4086 int element = belt_base_active_element[belt_nr] + i;
4087 int graphic = el2img(element);
4089 if (belt_dir == MV_LEFT)
4090 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4092 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4095 for (yy = 0; yy < lev_fieldy; yy++)
4097 for (xx = 0; xx < lev_fieldx; xx++)
4099 int element = Feld[xx][yy];
4101 if (IS_BELT_SWITCH(element))
4103 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4105 if (e_belt_nr == belt_nr)
4107 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4108 DrawLevelField(xx, yy);
4111 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4113 int e_belt_nr = getBeltNrFromBeltElement(element);
4115 if (e_belt_nr == belt_nr)
4117 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4119 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4120 DrawLevelField(xx, yy);
4123 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4125 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4127 if (e_belt_nr == belt_nr)
4129 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4131 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4132 DrawLevelField(xx, yy);
4139 static void ToggleSwitchgateSwitch(int x, int y)
4143 game.switchgate_pos = !game.switchgate_pos;
4145 for (yy = 0; yy < lev_fieldy; yy++)
4147 for (xx = 0; xx < lev_fieldx; xx++)
4149 int element = Feld[xx][yy];
4151 if (element == EL_SWITCHGATE_SWITCH_UP ||
4152 element == EL_SWITCHGATE_SWITCH_DOWN)
4154 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4155 DrawLevelField(xx, yy);
4157 else if (element == EL_SWITCHGATE_OPEN ||
4158 element == EL_SWITCHGATE_OPENING)
4160 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4162 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4164 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4167 else if (element == EL_SWITCHGATE_CLOSED ||
4168 element == EL_SWITCHGATE_CLOSING)
4170 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4172 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4174 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4181 static int getInvisibleActiveFromInvisibleElement(int element)
4183 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4184 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4185 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4189 static int getInvisibleFromInvisibleActiveElement(int element)
4191 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4192 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4193 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4197 static void RedrawAllLightSwitchesAndInvisibleElements()
4201 for (y = 0; y < lev_fieldy; y++)
4203 for (x = 0; x < lev_fieldx; x++)
4205 int element = Feld[x][y];
4207 if (element == EL_LIGHT_SWITCH &&
4208 game.light_time_left > 0)
4210 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4211 DrawLevelField(x, y);
4213 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4214 game.light_time_left == 0)
4216 Feld[x][y] = EL_LIGHT_SWITCH;
4217 DrawLevelField(x, y);
4219 else if (element == EL_INVISIBLE_STEELWALL ||
4220 element == EL_INVISIBLE_WALL ||
4221 element == EL_INVISIBLE_SAND)
4223 if (game.light_time_left > 0)
4224 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4226 DrawLevelField(x, y);
4228 /* uncrumble neighbour fields, if needed */
4229 if (element == EL_INVISIBLE_SAND)
4230 DrawLevelFieldCrumbledSandNeighbours(x, y);
4232 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4233 element == EL_INVISIBLE_WALL_ACTIVE ||
4234 element == EL_INVISIBLE_SAND_ACTIVE)
4236 if (game.light_time_left == 0)
4237 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4239 DrawLevelField(x, y);
4241 /* re-crumble neighbour fields, if needed */
4242 if (element == EL_INVISIBLE_SAND)
4243 DrawLevelFieldCrumbledSandNeighbours(x, y);
4249 static void ToggleLightSwitch(int x, int y)
4251 int element = Feld[x][y];
4253 game.light_time_left =
4254 (element == EL_LIGHT_SWITCH ?
4255 level.time_light * FRAMES_PER_SECOND : 0);
4257 RedrawAllLightSwitchesAndInvisibleElements();
4260 static void ActivateTimegateSwitch(int x, int y)
4264 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4266 for (yy = 0; yy < lev_fieldy; yy++)
4268 for (xx = 0; xx < lev_fieldx; xx++)
4270 int element = Feld[xx][yy];
4272 if (element == EL_TIMEGATE_CLOSED ||
4273 element == EL_TIMEGATE_CLOSING)
4275 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4276 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4280 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4282 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4283 DrawLevelField(xx, yy);
4290 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4293 void Impact(int x, int y)
4295 boolean lastline = (y == lev_fieldy-1);
4296 boolean object_hit = FALSE;
4297 boolean impact = (lastline || object_hit);
4298 int element = Feld[x][y];
4299 int smashed = EL_STEELWALL;
4301 if (!lastline) /* check if element below was hit */
4303 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4306 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4307 MovDir[x][y + 1] != MV_DOWN ||
4308 MovPos[x][y + 1] <= TILEY / 2));
4311 object_hit = !IS_FREE(x, y + 1);
4314 /* do not smash moving elements that left the smashed field in time */
4315 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4316 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4320 smashed = MovingOrBlocked2Element(x, y + 1);
4322 impact = (lastline || object_hit);
4325 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4327 SplashAcid(x, y + 1);
4331 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4332 /* only reset graphic animation if graphic really changes after impact */
4334 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4336 ResetGfxAnimation(x, y);
4337 DrawLevelField(x, y);
4340 if (impact && CAN_EXPLODE_IMPACT(element))
4345 else if (impact && element == EL_PEARL)
4347 ResetGfxAnimation(x, y);
4349 Feld[x][y] = EL_PEARL_BREAKING;
4350 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4353 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4355 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4360 if (impact && element == EL_AMOEBA_DROP)
4362 if (object_hit && IS_PLAYER(x, y + 1))
4363 KillHeroUnlessEnemyProtected(x, y + 1);
4364 else if (object_hit && smashed == EL_PENGUIN)
4368 Feld[x][y] = EL_AMOEBA_GROWING;
4369 Store[x][y] = EL_AMOEBA_WET;
4371 ResetRandomAnimationValue(x, y);
4376 if (object_hit) /* check which object was hit */
4378 if (CAN_PASS_MAGIC_WALL(element) &&
4379 (smashed == EL_MAGIC_WALL ||
4380 smashed == EL_BD_MAGIC_WALL))
4383 int activated_magic_wall =
4384 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4385 EL_BD_MAGIC_WALL_ACTIVE);
4387 /* activate magic wall / mill */
4388 for (yy = 0; yy < lev_fieldy; yy++)
4389 for (xx = 0; xx < lev_fieldx; xx++)
4390 if (Feld[xx][yy] == smashed)
4391 Feld[xx][yy] = activated_magic_wall;
4393 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4394 game.magic_wall_active = TRUE;
4396 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4397 SND_MAGIC_WALL_ACTIVATING :
4398 SND_BD_MAGIC_WALL_ACTIVATING));
4401 if (IS_PLAYER(x, y + 1))
4403 if (CAN_SMASH_PLAYER(element))
4405 KillHeroUnlessEnemyProtected(x, y + 1);
4409 else if (smashed == EL_PENGUIN)
4411 if (CAN_SMASH_PLAYER(element))
4417 else if (element == EL_BD_DIAMOND)
4419 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4425 else if (((element == EL_SP_INFOTRON ||
4426 element == EL_SP_ZONK) &&
4427 (smashed == EL_SP_SNIKSNAK ||
4428 smashed == EL_SP_ELECTRON ||
4429 smashed == EL_SP_DISK_ORANGE)) ||
4430 (element == EL_SP_INFOTRON &&
4431 smashed == EL_SP_DISK_YELLOW))
4437 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4443 else if (CAN_SMASH_EVERYTHING(element))
4445 if (IS_CLASSIC_ENEMY(smashed) ||
4446 CAN_EXPLODE_SMASHED(smashed))
4451 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4453 if (smashed == EL_LAMP ||
4454 smashed == EL_LAMP_ACTIVE)
4459 else if (smashed == EL_NUT)
4461 Feld[x][y + 1] = EL_NUT_BREAKING;
4462 PlayLevelSound(x, y, SND_NUT_BREAKING);
4463 RaiseScoreElement(EL_NUT);
4466 else if (smashed == EL_PEARL)
4468 ResetGfxAnimation(x, y);
4470 Feld[x][y + 1] = EL_PEARL_BREAKING;
4471 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4474 else if (smashed == EL_DIAMOND)
4476 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4477 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4480 else if (IS_BELT_SWITCH(smashed))
4482 ToggleBeltSwitch(x, y + 1);
4484 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4485 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4487 ToggleSwitchgateSwitch(x, y + 1);
4489 else if (smashed == EL_LIGHT_SWITCH ||
4490 smashed == EL_LIGHT_SWITCH_ACTIVE)
4492 ToggleLightSwitch(x, y + 1);
4497 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4500 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4503 /* !!! TEST ONLY !!! */
4504 CheckElementChangeBySide(x, y + 1, smashed, element,
4505 CE_SWITCHED, CH_SIDE_TOP);
4506 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4507 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4509 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4510 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4511 CheckElementChangeBySide(x, y + 1, smashed, element,
4512 CE_SWITCHED, CH_SIDE_TOP);
4518 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4523 /* play sound of magic wall / mill */
4525 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4526 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4528 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4529 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4530 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4531 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4536 /* play sound of object that hits the ground */
4537 if (lastline || object_hit)
4538 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4541 inline static void TurnRoundExt(int x, int y)
4553 { 0, 0 }, { 0, 0 }, { 0, 0 },
4558 int left, right, back;
4562 { MV_DOWN, MV_UP, MV_RIGHT },
4563 { MV_UP, MV_DOWN, MV_LEFT },
4565 { MV_LEFT, MV_RIGHT, MV_DOWN },
4569 { MV_RIGHT, MV_LEFT, MV_UP }
4572 int element = Feld[x][y];
4573 int move_pattern = element_info[element].move_pattern;
4575 int old_move_dir = MovDir[x][y];
4576 int left_dir = turn[old_move_dir].left;
4577 int right_dir = turn[old_move_dir].right;
4578 int back_dir = turn[old_move_dir].back;
4580 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4581 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4582 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4583 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4585 int left_x = x + left_dx, left_y = y + left_dy;
4586 int right_x = x + right_dx, right_y = y + right_dy;
4587 int move_x = x + move_dx, move_y = y + move_dy;
4591 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4593 TestIfBadThingTouchesOtherBadThing(x, y);
4595 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4596 MovDir[x][y] = right_dir;
4597 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4598 MovDir[x][y] = left_dir;
4600 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4602 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4606 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4607 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4609 TestIfBadThingTouchesOtherBadThing(x, y);
4611 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4612 MovDir[x][y] = left_dir;
4613 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4614 MovDir[x][y] = right_dir;
4616 if ((element == EL_SPACESHIP ||
4617 element == EL_SP_SNIKSNAK ||
4618 element == EL_SP_ELECTRON)
4619 && MovDir[x][y] != old_move_dir)
4621 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4625 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4627 TestIfBadThingTouchesOtherBadThing(x, y);
4629 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4630 MovDir[x][y] = left_dir;
4631 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4632 MovDir[x][y] = right_dir;
4634 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4636 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4639 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4641 TestIfBadThingTouchesOtherBadThing(x, y);
4643 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4644 MovDir[x][y] = left_dir;
4645 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4646 MovDir[x][y] = right_dir;
4648 if (MovDir[x][y] != old_move_dir)
4652 else if (element == EL_YAMYAM)
4654 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4655 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4657 if (can_turn_left && can_turn_right)
4658 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4659 else if (can_turn_left)
4660 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4661 else if (can_turn_right)
4662 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4664 MovDir[x][y] = back_dir;
4666 MovDelay[x][y] = 16 + 16 * RND(3);
4668 else if (element == EL_DARK_YAMYAM)
4670 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4672 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4675 if (can_turn_left && can_turn_right)
4676 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4677 else if (can_turn_left)
4678 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4679 else if (can_turn_right)
4680 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4682 MovDir[x][y] = back_dir;
4684 MovDelay[x][y] = 16 + 16 * RND(3);
4686 else if (element == EL_PACMAN)
4688 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4689 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4691 if (can_turn_left && can_turn_right)
4692 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4693 else if (can_turn_left)
4694 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4695 else if (can_turn_right)
4696 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4698 MovDir[x][y] = back_dir;
4700 MovDelay[x][y] = 6 + RND(40);
4702 else if (element == EL_PIG)
4704 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4705 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4706 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4707 boolean should_turn_left, should_turn_right, should_move_on;
4709 int rnd = RND(rnd_value);
4711 should_turn_left = (can_turn_left &&
4713 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4714 y + back_dy + left_dy)));
4715 should_turn_right = (can_turn_right &&
4717 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4718 y + back_dy + right_dy)));
4719 should_move_on = (can_move_on &&
4722 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4723 y + move_dy + left_dy) ||
4724 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4725 y + move_dy + right_dy)));
4727 if (should_turn_left || should_turn_right || should_move_on)
4729 if (should_turn_left && should_turn_right && should_move_on)
4730 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4731 rnd < 2 * rnd_value / 3 ? right_dir :
4733 else if (should_turn_left && should_turn_right)
4734 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4735 else if (should_turn_left && should_move_on)
4736 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4737 else if (should_turn_right && should_move_on)
4738 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4739 else if (should_turn_left)
4740 MovDir[x][y] = left_dir;
4741 else if (should_turn_right)
4742 MovDir[x][y] = right_dir;
4743 else if (should_move_on)
4744 MovDir[x][y] = old_move_dir;
4746 else if (can_move_on && rnd > rnd_value / 8)
4747 MovDir[x][y] = old_move_dir;
4748 else if (can_turn_left && can_turn_right)
4749 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4750 else if (can_turn_left && rnd > rnd_value / 8)
4751 MovDir[x][y] = left_dir;
4752 else if (can_turn_right && rnd > rnd_value/8)
4753 MovDir[x][y] = right_dir;
4755 MovDir[x][y] = back_dir;
4757 xx = x + move_xy[MovDir[x][y]].x;
4758 yy = y + move_xy[MovDir[x][y]].y;
4761 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4762 if (!IN_LEV_FIELD(xx, yy) ||
4763 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4764 MovDir[x][y] = old_move_dir;
4766 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4767 MovDir[x][y] = old_move_dir;
4772 else if (element == EL_DRAGON)
4774 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4775 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4776 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4778 int rnd = RND(rnd_value);
4781 if (FrameCounter < 1 && x == 0 && y == 29)
4782 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4785 if (can_move_on && rnd > rnd_value / 8)
4786 MovDir[x][y] = old_move_dir;
4787 else if (can_turn_left && can_turn_right)
4788 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4789 else if (can_turn_left && rnd > rnd_value / 8)
4790 MovDir[x][y] = left_dir;
4791 else if (can_turn_right && rnd > rnd_value / 8)
4792 MovDir[x][y] = right_dir;
4794 MovDir[x][y] = back_dir;
4796 xx = x + move_xy[MovDir[x][y]].x;
4797 yy = y + move_xy[MovDir[x][y]].y;
4800 if (FrameCounter < 1 && x == 0 && y == 29)
4801 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4802 xx, yy, Feld[xx][yy],
4807 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4808 MovDir[x][y] = old_move_dir;
4810 if (!IS_FREE(xx, yy))
4811 MovDir[x][y] = old_move_dir;
4815 if (FrameCounter < 1 && x == 0 && y == 29)
4816 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4821 else if (element == EL_MOLE)
4823 boolean can_move_on =
4824 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4825 IS_AMOEBOID(Feld[move_x][move_y]) ||
4826 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4829 boolean can_turn_left =
4830 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4831 IS_AMOEBOID(Feld[left_x][left_y])));
4833 boolean can_turn_right =
4834 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4835 IS_AMOEBOID(Feld[right_x][right_y])));
4837 if (can_turn_left && can_turn_right)
4838 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4839 else if (can_turn_left)
4840 MovDir[x][y] = left_dir;
4842 MovDir[x][y] = right_dir;
4845 if (MovDir[x][y] != old_move_dir)
4848 else if (element == EL_BALLOON)
4850 MovDir[x][y] = game.balloon_dir;
4853 else if (element == EL_SPRING)
4856 if (MovDir[x][y] & MV_HORIZONTAL &&
4857 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4858 MovDir[x][y] = MV_NO_MOVING;
4860 if (MovDir[x][y] & MV_HORIZONTAL &&
4861 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4862 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4863 MovDir[x][y] = MV_NO_MOVING;
4868 else if (element == EL_ROBOT ||
4869 element == EL_SATELLITE ||
4870 element == EL_PENGUIN)
4872 int attr_x = -1, attr_y = -1;
4883 for (i = 0; i < MAX_PLAYERS; i++)
4885 struct PlayerInfo *player = &stored_player[i];
4886 int jx = player->jx, jy = player->jy;
4888 if (!player->active)
4892 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4901 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4902 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4903 game.engine_version < VERSION_IDENT(3,1,0,0)))
4905 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4912 if (element == EL_PENGUIN)
4915 static int xy[4][2] =
4923 for (i = 0; i < NUM_DIRECTIONS; i++)
4925 int ex = x + xy[i][0];
4926 int ey = y + xy[i][1];
4928 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4937 MovDir[x][y] = MV_NO_MOVING;
4939 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4940 else if (attr_x > x)
4941 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4943 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4944 else if (attr_y > y)
4945 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4947 if (element == EL_ROBOT)
4951 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4952 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4953 Moving2Blocked(x, y, &newx, &newy);
4955 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4956 MovDelay[x][y] = 8 + 8 * !RND(3);
4958 MovDelay[x][y] = 16;
4960 else if (element == EL_PENGUIN)
4966 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4968 boolean first_horiz = RND(2);
4969 int new_move_dir = MovDir[x][y];
4972 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4973 Moving2Blocked(x, y, &newx, &newy);
4975 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4979 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4980 Moving2Blocked(x, y, &newx, &newy);
4982 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4985 MovDir[x][y] = old_move_dir;
4989 else /* (element == EL_SATELLITE) */
4995 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4997 boolean first_horiz = RND(2);
4998 int new_move_dir = MovDir[x][y];
5001 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5002 Moving2Blocked(x, y, &newx, &newy);
5004 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5008 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5009 Moving2Blocked(x, y, &newx, &newy);
5011 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5014 MovDir[x][y] = old_move_dir;
5019 else if (move_pattern == MV_TURNING_LEFT ||
5020 move_pattern == MV_TURNING_RIGHT ||
5021 move_pattern == MV_TURNING_LEFT_RIGHT ||
5022 move_pattern == MV_TURNING_RIGHT_LEFT ||
5023 move_pattern == MV_TURNING_RANDOM ||
5024 move_pattern == MV_ALL_DIRECTIONS)
5026 boolean can_turn_left =
5027 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5028 boolean can_turn_right =
5029 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5032 if (getElementMoveStepsize(x, y) == 0)
5036 if (move_pattern == MV_TURNING_LEFT)
5037 MovDir[x][y] = left_dir;
5038 else if (move_pattern == MV_TURNING_RIGHT)
5039 MovDir[x][y] = right_dir;
5040 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5041 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5042 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5043 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5044 else if (move_pattern == MV_TURNING_RANDOM)
5045 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5046 can_turn_right && !can_turn_left ? right_dir :
5047 RND(2) ? left_dir : right_dir);
5048 else if (can_turn_left && can_turn_right)
5049 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5050 else if (can_turn_left)
5051 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5052 else if (can_turn_right)
5053 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5055 MovDir[x][y] = back_dir;
5057 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5059 else if (move_pattern == MV_HORIZONTAL ||
5060 move_pattern == MV_VERTICAL)
5062 if (move_pattern & old_move_dir)
5063 MovDir[x][y] = back_dir;
5064 else if (move_pattern == MV_HORIZONTAL)
5065 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5066 else if (move_pattern == MV_VERTICAL)
5067 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5069 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5071 else if (move_pattern & MV_ANY_DIRECTION)
5073 MovDir[x][y] = move_pattern;
5074 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5076 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5078 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5079 MovDir[x][y] = left_dir;
5080 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5081 MovDir[x][y] = right_dir;
5083 if (MovDir[x][y] != old_move_dir)
5084 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5086 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5088 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5089 MovDir[x][y] = right_dir;
5090 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5091 MovDir[x][y] = left_dir;
5093 if (MovDir[x][y] != old_move_dir)
5094 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5096 else if (move_pattern == MV_TOWARDS_PLAYER ||
5097 move_pattern == MV_AWAY_FROM_PLAYER)
5099 int attr_x = -1, attr_y = -1;
5101 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5112 for (i = 0; i < MAX_PLAYERS; i++)
5114 struct PlayerInfo *player = &stored_player[i];
5115 int jx = player->jx, jy = player->jy;
5117 if (!player->active)
5121 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5129 MovDir[x][y] = MV_NO_MOVING;
5131 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5132 else if (attr_x > x)
5133 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5135 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5136 else if (attr_y > y)
5137 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5139 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5141 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5143 boolean first_horiz = RND(2);
5144 int new_move_dir = MovDir[x][y];
5147 if (getElementMoveStepsize(x, y) == 0)
5149 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5150 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5157 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5158 Moving2Blocked(x, y, &newx, &newy);
5160 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5164 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5165 Moving2Blocked(x, y, &newx, &newy);
5167 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5170 MovDir[x][y] = old_move_dir;
5173 else if (move_pattern == MV_WHEN_PUSHED ||
5174 move_pattern == MV_WHEN_DROPPED)
5176 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5177 MovDir[x][y] = MV_NO_MOVING;
5181 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5183 static int test_xy[7][2] =
5193 static int test_dir[7] =
5203 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5204 int move_preference = -1000000; /* start with very low preference */
5205 int new_move_dir = MV_NO_MOVING;
5206 int start_test = RND(4);
5209 for (i = 0; i < NUM_DIRECTIONS; i++)
5211 int move_dir = test_dir[start_test + i];
5212 int move_dir_preference;
5214 xx = x + test_xy[start_test + i][0];
5215 yy = y + test_xy[start_test + i][1];
5217 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5218 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5220 new_move_dir = move_dir;
5225 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5228 move_dir_preference = -1 * RunnerVisit[xx][yy];
5229 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5230 move_dir_preference = PlayerVisit[xx][yy];
5232 if (move_dir_preference > move_preference)
5234 /* prefer field that has not been visited for the longest time */
5235 move_preference = move_dir_preference;
5236 new_move_dir = move_dir;
5238 else if (move_dir_preference == move_preference &&
5239 move_dir == old_move_dir)
5241 /* prefer last direction when all directions are preferred equally */
5242 move_preference = move_dir_preference;
5243 new_move_dir = move_dir;
5247 MovDir[x][y] = new_move_dir;
5248 if (old_move_dir != new_move_dir)
5251 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5259 static void TurnRound(int x, int y)
5261 int direction = MovDir[x][y];
5264 GfxDir[x][y] = MovDir[x][y];
5270 GfxDir[x][y] = MovDir[x][y];
5273 if (direction != MovDir[x][y])
5278 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5281 GfxAction[x][y] = ACTION_WAITING;
5285 static boolean JustBeingPushed(int x, int y)
5289 for (i = 0; i < MAX_PLAYERS; i++)
5291 struct PlayerInfo *player = &stored_player[i];
5293 if (player->active && player->is_pushing && player->MovPos)
5295 int next_jx = player->jx + (player->jx - player->last_jx);
5296 int next_jy = player->jy + (player->jy - player->last_jy);
5298 if (x == next_jx && y == next_jy)
5306 void StartMoving(int x, int y)
5309 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5311 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5312 int element = Feld[x][y];
5318 if (MovDelay[x][y] == 0)
5319 GfxAction[x][y] = ACTION_DEFAULT;
5321 /* !!! this should be handled more generic (not only for mole) !!! */
5322 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5323 GfxAction[x][y] = ACTION_DEFAULT;
5326 if (CAN_FALL(element) && y < lev_fieldy - 1)
5328 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5329 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5330 if (JustBeingPushed(x, y))
5333 if (element == EL_QUICKSAND_FULL)
5335 if (IS_FREE(x, y + 1))
5337 InitMovingField(x, y, MV_DOWN);
5338 started_moving = TRUE;
5340 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5341 Store[x][y] = EL_ROCK;
5343 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5345 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5348 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5350 if (!MovDelay[x][y])
5351 MovDelay[x][y] = TILEY + 1;
5360 Feld[x][y] = EL_QUICKSAND_EMPTY;
5361 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5362 Store[x][y + 1] = Store[x][y];
5365 PlayLevelSoundAction(x, y, ACTION_FILLING);
5367 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5371 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5372 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5374 InitMovingField(x, y, MV_DOWN);
5375 started_moving = TRUE;
5377 Feld[x][y] = EL_QUICKSAND_FILLING;
5378 Store[x][y] = element;
5380 PlayLevelSoundAction(x, y, ACTION_FILLING);
5382 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5385 else if (element == EL_MAGIC_WALL_FULL)
5387 if (IS_FREE(x, y + 1))
5389 InitMovingField(x, y, MV_DOWN);
5390 started_moving = TRUE;
5392 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5393 Store[x][y] = EL_CHANGED(Store[x][y]);
5395 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5397 if (!MovDelay[x][y])
5398 MovDelay[x][y] = TILEY/4 + 1;
5407 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5408 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5409 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5413 else if (element == EL_BD_MAGIC_WALL_FULL)
5415 if (IS_FREE(x, y + 1))
5417 InitMovingField(x, y, MV_DOWN);
5418 started_moving = TRUE;
5420 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5421 Store[x][y] = EL_CHANGED2(Store[x][y]);
5423 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5425 if (!MovDelay[x][y])
5426 MovDelay[x][y] = TILEY/4 + 1;
5435 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5436 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5437 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5441 else if (CAN_PASS_MAGIC_WALL(element) &&
5442 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5443 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5445 InitMovingField(x, y, MV_DOWN);
5446 started_moving = TRUE;
5449 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5450 EL_BD_MAGIC_WALL_FILLING);
5451 Store[x][y] = element;
5454 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5456 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5459 SplashAcid(x, y + 1);
5461 InitMovingField(x, y, MV_DOWN);
5462 started_moving = TRUE;
5464 Store[x][y] = EL_ACID;
5466 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5467 GfxAction[x][y + 1] = ACTION_ACTIVE;
5471 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5472 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5474 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5475 CAN_SMASH(element) && WasJustFalling[x][y] &&
5476 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5478 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5479 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5480 (Feld[x][y + 1] == EL_BLOCKED)))
5484 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5485 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5486 WasJustMoving[x][y] && !Pushed[x][y + 1])
5488 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5489 WasJustMoving[x][y])
5494 /* this is needed for a special case not covered by calling "Impact()"
5495 from "ContinueMoving()": if an element moves to a tile directly below
5496 another element which was just falling on that tile (which was empty
5497 in the previous frame), the falling element above would just stop
5498 instead of smashing the element below (in previous version, the above
5499 element was just checked for "moving" instead of "falling", resulting
5500 in incorrect smashes caused by horizontal movement of the above
5501 element; also, the case of the player being the element to smash was
5502 simply not covered here... :-/ ) */
5505 WasJustMoving[x][y] = 0;
5506 WasJustFalling[x][y] = 0;
5509 CheckCollision[x][y] = 0;
5512 if (IS_PLAYER(x, y + 1))
5513 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5518 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5520 if (MovDir[x][y] == MV_NO_MOVING)
5522 InitMovingField(x, y, MV_DOWN);
5523 started_moving = TRUE;
5526 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5528 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5529 MovDir[x][y] = MV_DOWN;
5531 InitMovingField(x, y, MV_DOWN);
5532 started_moving = TRUE;
5534 else if (element == EL_AMOEBA_DROP)
5536 Feld[x][y] = EL_AMOEBA_GROWING;
5537 Store[x][y] = EL_AMOEBA_WET;
5539 /* Store[x][y + 1] must be zero, because:
5540 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5543 #if OLD_GAME_BEHAVIOUR
5544 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5546 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5547 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5548 element != EL_DX_SUPABOMB)
5551 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5552 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5553 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5554 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5557 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5558 (IS_FREE(x - 1, y + 1) ||
5559 Feld[x - 1][y + 1] == EL_ACID));
5560 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5561 (IS_FREE(x + 1, y + 1) ||
5562 Feld[x + 1][y + 1] == EL_ACID));
5563 boolean can_fall_any = (can_fall_left || can_fall_right);
5564 boolean can_fall_both = (can_fall_left && can_fall_right);
5566 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5568 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5570 if (slippery_type == SLIPPERY_ONLY_LEFT)
5571 can_fall_right = FALSE;
5572 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5573 can_fall_left = FALSE;
5574 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5575 can_fall_right = FALSE;
5576 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5577 can_fall_left = FALSE;
5579 can_fall_any = (can_fall_left || can_fall_right);
5580 can_fall_both = (can_fall_left && can_fall_right);
5583 #if USE_NEW_SP_SLIPPERY
5584 /* !!! better use the same properties as for custom elements here !!! */
5585 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5586 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5588 can_fall_right = FALSE; /* slip down on left side */
5589 can_fall_both = FALSE;
5596 if (game.emulation == EMU_BOULDERDASH ||
5597 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5598 can_fall_right = FALSE; /* slip down on left side */
5600 can_fall_left = !(can_fall_right = RND(2));
5602 can_fall_both = FALSE;
5609 if (can_fall_both &&
5610 (game.emulation != EMU_BOULDERDASH &&
5611 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5612 can_fall_left = !(can_fall_right = RND(2));
5615 /* if not determined otherwise, prefer left side for slipping down */
5616 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5617 started_moving = TRUE;
5621 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5623 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5626 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5627 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5628 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5629 int belt_dir = game.belt_dir[belt_nr];
5631 if ((belt_dir == MV_LEFT && left_is_free) ||
5632 (belt_dir == MV_RIGHT && right_is_free))
5635 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5638 InitMovingField(x, y, belt_dir);
5639 started_moving = TRUE;
5642 Pushed[x][y] = TRUE;
5643 Pushed[nextx][y] = TRUE;
5646 GfxAction[x][y] = ACTION_DEFAULT;
5650 MovDir[x][y] = 0; /* if element was moving, stop it */
5655 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5657 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5659 if (CAN_MOVE(element) && !started_moving)
5662 int move_pattern = element_info[element].move_pattern;
5667 if (MovDir[x][y] == MV_NO_MOVING)
5669 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5670 x, y, element, element_info[element].token_name);
5671 printf("StartMoving(): This should never happen!\n");
5676 Moving2Blocked(x, y, &newx, &newy);
5679 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5682 if ((element == EL_SATELLITE ||
5683 element == EL_BALLOON ||
5684 element == EL_SPRING)
5685 && JustBeingPushed(x, y))
5692 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5693 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5695 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5696 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5697 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5701 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5702 element, element_info[element].token_name,
5703 WasJustMoving[x][y],
5704 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5705 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5706 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5707 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5711 WasJustMoving[x][y] = 0;
5714 CheckCollision[x][y] = 0;
5716 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5719 if (Feld[x][y] != element) /* element has changed */
5721 element = Feld[x][y];
5722 move_pattern = element_info[element].move_pattern;
5724 if (!CAN_MOVE(element))
5728 if (Feld[x][y] != element) /* element has changed */
5736 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5737 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5739 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5741 Moving2Blocked(x, y, &newx, &newy);
5742 if (Feld[newx][newy] == EL_BLOCKED)
5743 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5749 if (FrameCounter < 1 && x == 0 && y == 29)
5750 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5753 if (!MovDelay[x][y]) /* start new movement phase */
5755 /* all objects that can change their move direction after each step
5756 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5758 if (element != EL_YAMYAM &&
5759 element != EL_DARK_YAMYAM &&
5760 element != EL_PACMAN &&
5761 !(move_pattern & MV_ANY_DIRECTION) &&
5762 move_pattern != MV_TURNING_LEFT &&
5763 move_pattern != MV_TURNING_RIGHT &&
5764 move_pattern != MV_TURNING_LEFT_RIGHT &&
5765 move_pattern != MV_TURNING_RIGHT_LEFT &&
5766 move_pattern != MV_TURNING_RANDOM)
5771 if (FrameCounter < 1 && x == 0 && y == 29)
5772 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5775 if (MovDelay[x][y] && (element == EL_BUG ||
5776 element == EL_SPACESHIP ||
5777 element == EL_SP_SNIKSNAK ||
5778 element == EL_SP_ELECTRON ||
5779 element == EL_MOLE))
5780 DrawLevelField(x, y);
5784 if (MovDelay[x][y]) /* wait some time before next movement */
5789 if (element == EL_YAMYAM)
5792 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5793 DrawLevelElementAnimation(x, y, element);
5797 if (MovDelay[x][y]) /* element still has to wait some time */
5800 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5801 ResetGfxAnimation(x, y);
5805 if (GfxAction[x][y] != ACTION_WAITING)
5806 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5808 GfxAction[x][y] = ACTION_WAITING;
5812 if (element == EL_ROBOT ||
5814 element == EL_PACMAN ||
5816 element == EL_YAMYAM ||
5817 element == EL_DARK_YAMYAM)
5820 DrawLevelElementAnimation(x, y, element);
5822 DrawLevelElementAnimationIfNeeded(x, y, element);
5824 PlayLevelSoundAction(x, y, ACTION_WAITING);
5826 else if (element == EL_SP_ELECTRON)
5827 DrawLevelElementAnimationIfNeeded(x, y, element);
5828 else if (element == EL_DRAGON)
5831 int dir = MovDir[x][y];
5832 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5833 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5834 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5835 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5836 dir == MV_UP ? IMG_FLAMES_1_UP :
5837 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5838 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5841 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5844 GfxAction[x][y] = ACTION_ATTACKING;
5846 if (IS_PLAYER(x, y))
5847 DrawPlayerField(x, y);
5849 DrawLevelField(x, y);
5851 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5853 for (i = 1; i <= 3; i++)
5855 int xx = x + i * dx;
5856 int yy = y + i * dy;
5857 int sx = SCREENX(xx);
5858 int sy = SCREENY(yy);
5859 int flame_graphic = graphic + (i - 1);
5861 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5866 int flamed = MovingOrBlocked2Element(xx, yy);
5870 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5872 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5873 RemoveMovingField(xx, yy);
5875 RemoveField(xx, yy);
5877 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5880 RemoveMovingField(xx, yy);
5884 if (ChangeDelay[xx][yy])
5885 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5886 Feld[xx][yy] == EL_BLOCKED));
5890 ChangeDelay[xx][yy] = 0;
5892 Feld[xx][yy] = EL_FLAMES;
5893 if (IN_SCR_FIELD(sx, sy))
5895 DrawLevelFieldCrumbledSand(xx, yy);
5896 DrawGraphic(sx, sy, flame_graphic, frame);
5901 if (Feld[xx][yy] == EL_FLAMES)
5902 Feld[xx][yy] = EL_EMPTY;
5903 DrawLevelField(xx, yy);
5908 if (MovDelay[x][y]) /* element still has to wait some time */
5910 PlayLevelSoundAction(x, y, ACTION_WAITING);
5916 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5917 for all other elements GfxAction will be set by InitMovingField() */
5918 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5919 GfxAction[x][y] = ACTION_MOVING;
5923 /* now make next step */
5925 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5927 if (DONT_COLLIDE_WITH(element) &&
5928 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5929 !PLAYER_ENEMY_PROTECTED(newx, newy))
5932 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5936 /* player killed by element which is deadly when colliding with */
5938 KillHero(PLAYERINFO(newx, newy));
5945 else if (CAN_MOVE_INTO_ACID(element) &&
5946 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5947 (MovDir[x][y] == MV_DOWN ||
5948 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5950 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5951 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5955 else if ((element == EL_PENGUIN ||
5956 element == EL_ROBOT ||
5957 element == EL_SATELLITE ||
5958 element == EL_BALLOON ||
5959 IS_CUSTOM_ELEMENT(element)) &&
5960 IN_LEV_FIELD(newx, newy) &&
5961 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5964 SplashAcid(newx, newy);
5965 Store[x][y] = EL_ACID;
5967 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5969 if (Feld[newx][newy] == EL_EXIT_OPEN)
5973 DrawLevelField(x, y);
5975 Feld[x][y] = EL_EMPTY;
5976 DrawLevelField(x, y);
5979 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5980 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5981 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5983 local_player->friends_still_needed--;
5984 if (!local_player->friends_still_needed &&
5985 !local_player->GameOver && AllPlayersGone)
5986 local_player->LevelSolved = local_player->GameOver = TRUE;
5990 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5992 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5993 DrawLevelField(newx, newy);
5995 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5997 else if (!IS_FREE(newx, newy))
5999 GfxAction[x][y] = ACTION_WAITING;
6001 if (IS_PLAYER(x, y))
6002 DrawPlayerField(x, y);
6004 DrawLevelField(x, y);
6009 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6011 if (IS_FOOD_PIG(Feld[newx][newy]))
6013 if (IS_MOVING(newx, newy))
6014 RemoveMovingField(newx, newy);
6017 Feld[newx][newy] = EL_EMPTY;
6018 DrawLevelField(newx, newy);
6021 PlayLevelSound(x, y, SND_PIG_DIGGING);
6023 else if (!IS_FREE(newx, newy))
6025 if (IS_PLAYER(x, y))
6026 DrawPlayerField(x, y);
6028 DrawLevelField(x, y);
6037 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6040 else if (IS_CUSTOM_ELEMENT(element) &&
6041 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6045 !IS_FREE(newx, newy)
6050 int new_element = Feld[newx][newy];
6053 printf("::: '%s' digs '%s' [%d]\n",
6054 element_info[element].token_name,
6055 element_info[Feld[newx][newy]].token_name,
6056 StorePlayer[newx][newy]);
6059 if (!IS_FREE(newx, newy))
6061 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6062 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6065 /* no element can dig solid indestructible elements */
6066 if (IS_INDESTRUCTIBLE(new_element) &&
6067 !IS_DIGGABLE(new_element) &&
6068 !IS_COLLECTIBLE(new_element))
6071 if (AmoebaNr[newx][newy] &&
6072 (new_element == EL_AMOEBA_FULL ||
6073 new_element == EL_BD_AMOEBA ||
6074 new_element == EL_AMOEBA_GROWING))
6076 AmoebaCnt[AmoebaNr[newx][newy]]--;
6077 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6080 if (IS_MOVING(newx, newy))
6081 RemoveMovingField(newx, newy);
6084 RemoveField(newx, newy);
6085 DrawLevelField(newx, newy);
6088 /* if digged element was about to explode, prevent the explosion */
6089 ExplodeField[newx][newy] = EX_TYPE_NONE;
6091 PlayLevelSoundAction(x, y, action);
6096 Store[newx][newy] = EL_EMPTY;
6097 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6098 Store[newx][newy] = element_info[element].move_leave_element;
6100 Store[newx][newy] = EL_EMPTY;
6101 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6102 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6103 Store[newx][newy] = element_info[element].move_leave_element;
6106 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6107 element_info[element].can_leave_element = TRUE;
6110 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6112 RunnerVisit[x][y] = FrameCounter;
6113 PlayerVisit[x][y] /= 8; /* expire player visit path */
6119 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6121 if (!IS_FREE(newx, newy))
6123 if (IS_PLAYER(x, y))
6124 DrawPlayerField(x, y);
6126 DrawLevelField(x, y);
6132 boolean wanna_flame = !RND(10);
6133 int dx = newx - x, dy = newy - y;
6134 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6135 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6136 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6137 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6138 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6139 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6142 IS_CLASSIC_ENEMY(element1) ||
6143 IS_CLASSIC_ENEMY(element2)) &&
6144 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6145 element1 != EL_FLAMES && element2 != EL_FLAMES)
6148 ResetGfxAnimation(x, y);
6149 GfxAction[x][y] = ACTION_ATTACKING;
6152 if (IS_PLAYER(x, y))
6153 DrawPlayerField(x, y);
6155 DrawLevelField(x, y);
6157 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6159 MovDelay[x][y] = 50;
6163 RemoveField(newx, newy);
6165 Feld[newx][newy] = EL_FLAMES;
6166 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6169 RemoveField(newx1, newy1);
6171 Feld[newx1][newy1] = EL_FLAMES;
6173 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6176 RemoveField(newx2, newy2);
6178 Feld[newx2][newy2] = EL_FLAMES;
6185 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6186 Feld[newx][newy] == EL_DIAMOND)
6188 if (IS_MOVING(newx, newy))
6189 RemoveMovingField(newx, newy);
6192 Feld[newx][newy] = EL_EMPTY;
6193 DrawLevelField(newx, newy);
6196 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6198 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6199 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6201 if (AmoebaNr[newx][newy])
6203 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6204 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6205 Feld[newx][newy] == EL_BD_AMOEBA)
6206 AmoebaCnt[AmoebaNr[newx][newy]]--;
6211 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6213 if (IS_MOVING(newx, newy))
6216 RemoveMovingField(newx, newy);
6220 Feld[newx][newy] = EL_EMPTY;
6221 DrawLevelField(newx, newy);
6224 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6226 else if ((element == EL_PACMAN || element == EL_MOLE)
6227 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6229 if (AmoebaNr[newx][newy])
6231 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6232 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6233 Feld[newx][newy] == EL_BD_AMOEBA)
6234 AmoebaCnt[AmoebaNr[newx][newy]]--;
6237 if (element == EL_MOLE)
6239 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6240 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6242 ResetGfxAnimation(x, y);
6243 GfxAction[x][y] = ACTION_DIGGING;
6244 DrawLevelField(x, y);
6246 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6248 return; /* wait for shrinking amoeba */
6250 else /* element == EL_PACMAN */
6252 Feld[newx][newy] = EL_EMPTY;
6253 DrawLevelField(newx, newy);
6254 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6257 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6258 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6259 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6261 /* wait for shrinking amoeba to completely disappear */
6264 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6266 /* object was running against a wall */
6271 if (move_pattern & MV_ANY_DIRECTION &&
6272 move_pattern == MovDir[x][y])
6274 int blocking_element =
6275 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6278 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6279 element_info[element].token_name,
6280 element_info[blocking_element].token_name,
6284 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6287 element = Feld[x][y]; /* element might have changed */
6292 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6293 DrawLevelElementAnimation(x, y, element);
6295 if (element == EL_BUG ||
6296 element == EL_SPACESHIP ||
6297 element == EL_SP_SNIKSNAK)
6298 DrawLevelField(x, y);
6299 else if (element == EL_MOLE)
6300 DrawLevelField(x, y);
6301 else if (element == EL_BD_BUTTERFLY ||
6302 element == EL_BD_FIREFLY)
6303 DrawLevelElementAnimationIfNeeded(x, y, element);
6304 else if (element == EL_SATELLITE)
6305 DrawLevelElementAnimationIfNeeded(x, y, element);
6306 else if (element == EL_SP_ELECTRON)
6307 DrawLevelElementAnimationIfNeeded(x, y, element);
6310 if (DONT_TOUCH(element))
6311 TestIfBadThingTouchesHero(x, y);
6314 PlayLevelSoundAction(x, y, ACTION_WAITING);
6320 InitMovingField(x, y, MovDir[x][y]);
6322 PlayLevelSoundAction(x, y, ACTION_MOVING);
6326 ContinueMoving(x, y);
6329 void ContinueMoving(int x, int y)
6331 int element = Feld[x][y];
6332 int stored = Store[x][y];
6333 struct ElementInfo *ei = &element_info[element];
6334 int direction = MovDir[x][y];
6335 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6336 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6337 int newx = x + dx, newy = y + dy;
6339 int nextx = newx + dx, nexty = newy + dy;
6342 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6343 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6345 boolean pushed_by_player = Pushed[x][y];
6348 MovPos[x][y] += getElementMoveStepsize(x, y);
6351 if (pushed_by_player && IS_PLAYER(x, y))
6353 /* special case: moving object pushed by player */
6354 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6357 if (pushed_by_player) /* special case: moving object pushed by player */
6358 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6361 if (ABS(MovPos[x][y]) < TILEX)
6363 DrawLevelField(x, y);
6365 return; /* element is still moving */
6368 /* element reached destination field */
6370 Feld[x][y] = EL_EMPTY;
6371 Feld[newx][newy] = element;
6372 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6375 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6377 element = Feld[newx][newy] = EL_ACID;
6380 else if (element == EL_MOLE)
6382 Feld[x][y] = EL_SAND;
6384 DrawLevelFieldCrumbledSandNeighbours(x, y);
6386 else if (element == EL_QUICKSAND_FILLING)
6388 element = Feld[newx][newy] = get_next_element(element);
6389 Store[newx][newy] = Store[x][y];
6391 else if (element == EL_QUICKSAND_EMPTYING)
6393 Feld[x][y] = get_next_element(element);
6394 element = Feld[newx][newy] = Store[x][y];
6396 else if (element == EL_MAGIC_WALL_FILLING)
6398 element = Feld[newx][newy] = get_next_element(element);
6399 if (!game.magic_wall_active)
6400 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6401 Store[newx][newy] = Store[x][y];
6403 else if (element == EL_MAGIC_WALL_EMPTYING)
6405 Feld[x][y] = get_next_element(element);
6406 if (!game.magic_wall_active)
6407 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6408 element = Feld[newx][newy] = Store[x][y];
6410 else if (element == EL_BD_MAGIC_WALL_FILLING)
6412 element = Feld[newx][newy] = get_next_element(element);
6413 if (!game.magic_wall_active)
6414 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6415 Store[newx][newy] = Store[x][y];
6417 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6419 Feld[x][y] = get_next_element(element);
6420 if (!game.magic_wall_active)
6421 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6422 element = Feld[newx][newy] = Store[x][y];
6424 else if (element == EL_AMOEBA_DROPPING)
6426 Feld[x][y] = get_next_element(element);
6427 element = Feld[newx][newy] = Store[x][y];
6429 else if (element == EL_SOKOBAN_OBJECT)
6432 Feld[x][y] = Back[x][y];
6434 if (Back[newx][newy])
6435 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6437 Back[x][y] = Back[newx][newy] = 0;
6440 else if (Store[x][y] == EL_ACID)
6442 element = Feld[newx][newy] = EL_ACID;
6446 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6447 ei->move_leave_element != EL_EMPTY &&
6448 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6449 Store[x][y] != EL_EMPTY))
6451 /* some elements can leave other elements behind after moving */
6453 Feld[x][y] = ei->move_leave_element;
6454 InitField(x, y, FALSE);
6456 if (GFX_CRUMBLED(Feld[x][y]))
6457 DrawLevelFieldCrumbledSandNeighbours(x, y);
6461 Store[x][y] = EL_EMPTY;
6462 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6463 MovDelay[newx][newy] = 0;
6465 if (CAN_CHANGE(element))
6467 /* copy element change control values to new field */
6468 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6469 ChangePage[newx][newy] = ChangePage[x][y];
6470 Changed[newx][newy] = Changed[x][y];
6471 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6474 ChangeDelay[x][y] = 0;
6475 ChangePage[x][y] = -1;
6476 Changed[x][y] = CE_BITMASK_DEFAULT;
6477 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6479 /* copy animation control values to new field */
6480 GfxFrame[newx][newy] = GfxFrame[x][y];
6481 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6482 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6483 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6485 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6487 ResetGfxAnimation(x, y); /* reset animation values for old field */
6490 /* some elements can leave other elements behind after moving */
6492 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6493 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6494 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6496 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6497 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6501 int move_leave_element = ei->move_leave_element;
6503 Feld[x][y] = move_leave_element;
6504 InitField(x, y, FALSE);
6506 if (GFX_CRUMBLED(Feld[x][y]))
6507 DrawLevelFieldCrumbledSandNeighbours(x, y);
6509 if (ELEM_IS_PLAYER(move_leave_element))
6510 RelocatePlayer(x, y, move_leave_element);
6515 /* some elements can leave other elements behind after moving */
6516 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6517 ei->move_leave_element != EL_EMPTY &&
6518 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6519 ei->can_leave_element_last))
6521 Feld[x][y] = ei->move_leave_element;
6522 InitField(x, y, FALSE);
6524 if (GFX_CRUMBLED(Feld[x][y]))
6525 DrawLevelFieldCrumbledSandNeighbours(x, y);
6528 ei->can_leave_element_last = ei->can_leave_element;
6529 ei->can_leave_element = FALSE;
6533 /* 2.1.1 (does not work correctly for spring) */
6534 if (!CAN_MOVE(element))
6535 MovDir[newx][newy] = 0;
6539 /* (does not work for falling objects that slide horizontally) */
6540 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6541 MovDir[newx][newy] = 0;
6544 if (!CAN_MOVE(element) ||
6545 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6546 MovDir[newx][newy] = 0;
6550 if (!CAN_MOVE(element) ||
6551 (CAN_FALL(element) && direction == MV_DOWN))
6552 GfxDir[x][y] = MovDir[newx][newy] = 0;
6554 if (!CAN_MOVE(element) ||
6555 (CAN_FALL(element) && direction == MV_DOWN &&
6556 (element == EL_SPRING ||
6557 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6558 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6559 GfxDir[x][y] = MovDir[newx][newy] = 0;
6565 DrawLevelField(x, y);
6566 DrawLevelField(newx, newy);
6568 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6570 /* prevent pushed element from moving on in pushed direction */
6571 if (pushed_by_player && CAN_MOVE(element) &&
6572 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6573 !(element_info[element].move_pattern & direction))
6574 TurnRound(newx, newy);
6577 /* prevent elements on conveyor belt from moving on in last direction */
6578 if (pushed_by_conveyor && CAN_FALL(element) &&
6579 direction & MV_HORIZONTAL)
6582 if (CAN_MOVE(element))
6583 InitMovDir(newx, newy);
6585 MovDir[newx][newy] = 0;
6587 MovDir[newx][newy] = 0;
6592 if (!pushed_by_player)
6594 int nextx = newx + dx, nexty = newy + dy;
6595 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6597 WasJustMoving[newx][newy] = 3;
6599 if (CAN_FALL(element) && direction == MV_DOWN)
6600 WasJustFalling[newx][newy] = 3;
6602 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6603 CheckCollision[newx][newy] = 2;
6606 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6608 TestIfBadThingTouchesHero(newx, newy);
6609 TestIfBadThingTouchesFriend(newx, newy);
6611 if (!IS_CUSTOM_ELEMENT(element))
6612 TestIfBadThingTouchesOtherBadThing(newx, newy);
6614 else if (element == EL_PENGUIN)
6615 TestIfFriendTouchesBadThing(newx, newy);
6617 #if USE_NEW_MOVE_STYLE
6619 if (CAN_FALL(element) && direction == MV_DOWN &&
6620 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6621 IS_PLAYER(x, newy + 1))
6622 printf("::: we would now kill the player [%d]\n", FrameCounter);
6625 /* give the player one last chance (one more frame) to move away */
6626 if (CAN_FALL(element) && direction == MV_DOWN &&
6627 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6628 (!IS_PLAYER(x, newy + 1) ||
6629 game.engine_version < VERSION_IDENT(3,1,1,0)))
6632 if (CAN_FALL(element) && direction == MV_DOWN &&
6633 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6641 if (pushed_by_player && !game.use_bug_change_when_pushing)
6643 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6646 if (pushed_by_player)
6651 int dig_side = MV_DIR_OPPOSITE(direction);
6653 static int trigger_sides[4] =
6655 CH_SIDE_RIGHT, /* moving left */
6656 CH_SIDE_LEFT, /* moving right */
6657 CH_SIDE_BOTTOM, /* moving up */
6658 CH_SIDE_TOP, /* moving down */
6660 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6662 struct PlayerInfo *player = PLAYERINFO(x, y);
6664 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6665 player->index_bit, dig_side);
6666 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6667 player->index_bit, dig_side);
6672 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6676 if (ChangePage[newx][newy] != -1) /* delayed change */
6677 ChangeElement(newx, newy, ChangePage[newx][newy]);
6682 TestIfElementHitsCustomElement(newx, newy, direction);
6686 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6688 int hitting_element = Feld[newx][newy];
6690 /* !!! fix side (direction) orientation here and elsewhere !!! */
6691 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6695 if (IN_LEV_FIELD(nextx, nexty))
6697 int opposite_direction = MV_DIR_OPPOSITE(direction);
6698 int hitting_side = direction;
6699 int touched_side = opposite_direction;
6700 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6701 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6702 MovDir[nextx][nexty] != direction ||
6703 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6709 CheckElementChangeBySide(nextx, nexty, touched_element,
6710 CE_HIT_BY_SOMETHING, opposite_direction);
6712 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6713 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6715 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6717 struct ElementChangeInfo *change =
6718 &element_info[hitting_element].change_page[i];
6720 if (change->can_change &&
6721 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6722 change->trigger_side & touched_side &&
6723 change->trigger_element == touched_element)
6725 CheckElementChangeByPage(newx, newy, hitting_element,
6726 touched_element, CE_OTHER_IS_HITTING,i);
6732 if (IS_CUSTOM_ELEMENT(touched_element) &&
6733 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6735 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6737 struct ElementChangeInfo *change =
6738 &element_info[touched_element].change_page[i];
6740 if (change->can_change &&
6741 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6742 change->trigger_side & hitting_side &&
6743 change->trigger_element == hitting_element)
6745 CheckElementChangeByPage(nextx, nexty, touched_element,
6746 hitting_element, CE_OTHER_GETS_HIT, i);
6757 TestIfPlayerTouchesCustomElement(newx, newy);
6758 TestIfElementTouchesCustomElement(newx, newy);
6761 int AmoebeNachbarNr(int ax, int ay)
6764 int element = Feld[ax][ay];
6766 static int xy[4][2] =
6774 for (i = 0; i < NUM_DIRECTIONS; i++)
6776 int x = ax + xy[i][0];
6777 int y = ay + xy[i][1];
6779 if (!IN_LEV_FIELD(x, y))
6782 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6783 group_nr = AmoebaNr[x][y];
6789 void AmoebenVereinigen(int ax, int ay)
6791 int i, x, y, xx, yy;
6792 int new_group_nr = AmoebaNr[ax][ay];
6793 static int xy[4][2] =
6801 if (new_group_nr == 0)
6804 for (i = 0; i < NUM_DIRECTIONS; i++)
6809 if (!IN_LEV_FIELD(x, y))
6812 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6813 Feld[x][y] == EL_BD_AMOEBA ||
6814 Feld[x][y] == EL_AMOEBA_DEAD) &&
6815 AmoebaNr[x][y] != new_group_nr)
6817 int old_group_nr = AmoebaNr[x][y];
6819 if (old_group_nr == 0)
6822 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6823 AmoebaCnt[old_group_nr] = 0;
6824 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6825 AmoebaCnt2[old_group_nr] = 0;
6827 for (yy = 0; yy < lev_fieldy; yy++)
6829 for (xx = 0; xx < lev_fieldx; xx++)
6831 if (AmoebaNr[xx][yy] == old_group_nr)
6832 AmoebaNr[xx][yy] = new_group_nr;
6839 void AmoebeUmwandeln(int ax, int ay)
6843 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6845 int group_nr = AmoebaNr[ax][ay];
6850 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6851 printf("AmoebeUmwandeln(): This should never happen!\n");
6856 for (y = 0; y < lev_fieldy; y++)
6858 for (x = 0; x < lev_fieldx; x++)
6860 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6863 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6867 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6868 SND_AMOEBA_TURNING_TO_GEM :
6869 SND_AMOEBA_TURNING_TO_ROCK));
6874 static int xy[4][2] =
6882 for (i = 0; i < NUM_DIRECTIONS; i++)
6887 if (!IN_LEV_FIELD(x, y))
6890 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6892 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6893 SND_AMOEBA_TURNING_TO_GEM :
6894 SND_AMOEBA_TURNING_TO_ROCK));
6901 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6904 int group_nr = AmoebaNr[ax][ay];
6905 boolean done = FALSE;
6910 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6911 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6916 for (y = 0; y < lev_fieldy; y++)
6918 for (x = 0; x < lev_fieldx; x++)
6920 if (AmoebaNr[x][y] == group_nr &&
6921 (Feld[x][y] == EL_AMOEBA_DEAD ||
6922 Feld[x][y] == EL_BD_AMOEBA ||
6923 Feld[x][y] == EL_AMOEBA_GROWING))
6926 Feld[x][y] = new_element;
6927 InitField(x, y, FALSE);
6928 DrawLevelField(x, y);
6935 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6936 SND_BD_AMOEBA_TURNING_TO_ROCK :
6937 SND_BD_AMOEBA_TURNING_TO_GEM));
6940 void AmoebeWaechst(int x, int y)
6942 static unsigned long sound_delay = 0;
6943 static unsigned long sound_delay_value = 0;
6945 if (!MovDelay[x][y]) /* start new growing cycle */
6949 if (DelayReached(&sound_delay, sound_delay_value))
6952 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6954 if (Store[x][y] == EL_BD_AMOEBA)
6955 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6957 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6959 sound_delay_value = 30;
6963 if (MovDelay[x][y]) /* wait some time before growing bigger */
6966 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6968 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6969 6 - MovDelay[x][y]);
6971 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6974 if (!MovDelay[x][y])
6976 Feld[x][y] = Store[x][y];
6978 DrawLevelField(x, y);
6983 void AmoebaDisappearing(int x, int y)
6985 static unsigned long sound_delay = 0;
6986 static unsigned long sound_delay_value = 0;
6988 if (!MovDelay[x][y]) /* start new shrinking cycle */
6992 if (DelayReached(&sound_delay, sound_delay_value))
6993 sound_delay_value = 30;
6996 if (MovDelay[x][y]) /* wait some time before shrinking */
6999 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7001 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7002 6 - MovDelay[x][y]);
7004 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7007 if (!MovDelay[x][y])
7009 Feld[x][y] = EL_EMPTY;
7010 DrawLevelField(x, y);
7012 /* don't let mole enter this field in this cycle;
7013 (give priority to objects falling to this field from above) */
7019 void AmoebeAbleger(int ax, int ay)
7022 int element = Feld[ax][ay];
7023 int graphic = el2img(element);
7024 int newax = ax, neway = ay;
7025 static int xy[4][2] =
7033 if (!level.amoeba_speed)
7035 Feld[ax][ay] = EL_AMOEBA_DEAD;
7036 DrawLevelField(ax, ay);
7040 if (IS_ANIMATED(graphic))
7041 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7043 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7044 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7046 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7049 if (MovDelay[ax][ay])
7053 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7056 int x = ax + xy[start][0];
7057 int y = ay + xy[start][1];
7059 if (!IN_LEV_FIELD(x, y))
7063 if (IS_FREE(x, y) ||
7064 CAN_GROW_INTO(Feld[x][y]) ||
7065 Feld[x][y] == EL_QUICKSAND_EMPTY)
7071 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7072 if (IS_FREE(x, y) ||
7073 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7080 if (newax == ax && neway == ay)
7083 else /* normal or "filled" (BD style) amoeba */
7086 boolean waiting_for_player = FALSE;
7088 for (i = 0; i < NUM_DIRECTIONS; i++)
7090 int j = (start + i) % 4;
7091 int x = ax + xy[j][0];
7092 int y = ay + xy[j][1];
7094 if (!IN_LEV_FIELD(x, y))
7098 if (IS_FREE(x, y) ||
7099 CAN_GROW_INTO(Feld[x][y]) ||
7100 Feld[x][y] == EL_QUICKSAND_EMPTY)
7107 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7108 if (IS_FREE(x, y) ||
7109 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7116 else if (IS_PLAYER(x, y))
7117 waiting_for_player = TRUE;
7120 if (newax == ax && neway == ay) /* amoeba cannot grow */
7123 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7125 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7128 Feld[ax][ay] = EL_AMOEBA_DEAD;
7129 DrawLevelField(ax, ay);
7130 AmoebaCnt[AmoebaNr[ax][ay]]--;
7132 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7134 if (element == EL_AMOEBA_FULL)
7135 AmoebeUmwandeln(ax, ay);
7136 else if (element == EL_BD_AMOEBA)
7137 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7142 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7144 /* amoeba gets larger by growing in some direction */
7146 int new_group_nr = AmoebaNr[ax][ay];
7149 if (new_group_nr == 0)
7151 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7152 printf("AmoebeAbleger(): This should never happen!\n");
7157 AmoebaNr[newax][neway] = new_group_nr;
7158 AmoebaCnt[new_group_nr]++;
7159 AmoebaCnt2[new_group_nr]++;
7161 /* if amoeba touches other amoeba(s) after growing, unify them */
7162 AmoebenVereinigen(newax, neway);
7164 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7166 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7172 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7173 (neway == lev_fieldy - 1 && newax != ax))
7175 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7176 Store[newax][neway] = element;
7178 else if (neway == ay)
7180 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7182 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7184 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7189 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7190 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7191 Store[ax][ay] = EL_AMOEBA_DROP;
7192 ContinueMoving(ax, ay);
7196 DrawLevelField(newax, neway);
7199 void Life(int ax, int ay)
7202 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7204 int element = Feld[ax][ay];
7205 int graphic = el2img(element);
7206 boolean changed = FALSE;
7208 if (IS_ANIMATED(graphic))
7209 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7214 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7215 MovDelay[ax][ay] = life_time;
7217 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7220 if (MovDelay[ax][ay])
7224 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7226 int xx = ax+x1, yy = ay+y1;
7229 if (!IN_LEV_FIELD(xx, yy))
7232 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7234 int x = xx+x2, y = yy+y2;
7236 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7239 if (((Feld[x][y] == element ||
7240 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7242 (IS_FREE(x, y) && Stop[x][y]))
7246 if (xx == ax && yy == ay) /* field in the middle */
7248 if (nachbarn < life[0] || nachbarn > life[1])
7250 Feld[xx][yy] = EL_EMPTY;
7252 DrawLevelField(xx, yy);
7253 Stop[xx][yy] = TRUE;
7258 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7259 { /* free border field */
7260 if (nachbarn >= life[2] && nachbarn <= life[3])
7262 Feld[xx][yy] = element;
7263 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7265 DrawLevelField(xx, yy);
7266 Stop[xx][yy] = TRUE;
7271 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7272 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7273 { /* free border field */
7274 if (nachbarn >= life[2] && nachbarn <= life[3])
7276 Feld[xx][yy] = element;
7277 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7279 DrawLevelField(xx, yy);
7280 Stop[xx][yy] = TRUE;
7288 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7289 SND_GAME_OF_LIFE_GROWING);
7292 static void InitRobotWheel(int x, int y)
7294 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7297 static void RunRobotWheel(int x, int y)
7299 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7302 static void StopRobotWheel(int x, int y)
7304 if (ZX == x && ZY == y)
7308 static void InitTimegateWheel(int x, int y)
7311 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7313 /* another brainless, "type style" bug ... :-( */
7314 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7318 static void RunTimegateWheel(int x, int y)
7320 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7323 void CheckExit(int x, int y)
7325 if (local_player->gems_still_needed > 0 ||
7326 local_player->sokobanfields_still_needed > 0 ||
7327 local_player->lights_still_needed > 0)
7329 int element = Feld[x][y];
7330 int graphic = el2img(element);
7332 if (IS_ANIMATED(graphic))
7333 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7338 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7341 Feld[x][y] = EL_EXIT_OPENING;
7343 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7346 void CheckExitSP(int x, int y)
7348 if (local_player->gems_still_needed > 0)
7350 int element = Feld[x][y];
7351 int graphic = el2img(element);
7353 if (IS_ANIMATED(graphic))
7354 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7359 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7362 Feld[x][y] = EL_SP_EXIT_OPENING;
7364 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7367 static void CloseAllOpenTimegates()
7371 for (y = 0; y < lev_fieldy; y++)
7373 for (x = 0; x < lev_fieldx; x++)
7375 int element = Feld[x][y];
7377 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7379 Feld[x][y] = EL_TIMEGATE_CLOSING;
7381 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7383 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7390 void EdelsteinFunkeln(int x, int y)
7392 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7395 if (Feld[x][y] == EL_BD_DIAMOND)
7398 if (MovDelay[x][y] == 0) /* next animation frame */
7399 MovDelay[x][y] = 11 * !SimpleRND(500);
7401 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7405 if (setup.direct_draw && MovDelay[x][y])
7406 SetDrawtoField(DRAW_BUFFERED);
7408 DrawLevelElementAnimation(x, y, Feld[x][y]);
7410 if (MovDelay[x][y] != 0)
7412 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7413 10 - MovDelay[x][y]);
7415 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7417 if (setup.direct_draw)
7421 dest_x = FX + SCREENX(x) * TILEX;
7422 dest_y = FY + SCREENY(y) * TILEY;
7424 BlitBitmap(drawto_field, window,
7425 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7426 SetDrawtoField(DRAW_DIRECT);
7432 void MauerWaechst(int x, int y)
7436 if (!MovDelay[x][y]) /* next animation frame */
7437 MovDelay[x][y] = 3 * delay;
7439 if (MovDelay[x][y]) /* wait some time before next frame */
7443 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7445 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7446 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7448 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7451 if (!MovDelay[x][y])
7453 if (MovDir[x][y] == MV_LEFT)
7455 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7456 DrawLevelField(x - 1, y);
7458 else if (MovDir[x][y] == MV_RIGHT)
7460 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7461 DrawLevelField(x + 1, y);
7463 else if (MovDir[x][y] == MV_UP)
7465 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7466 DrawLevelField(x, y - 1);
7470 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7471 DrawLevelField(x, y + 1);
7474 Feld[x][y] = Store[x][y];
7476 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7477 DrawLevelField(x, y);
7482 void MauerAbleger(int ax, int ay)
7484 int element = Feld[ax][ay];
7485 int graphic = el2img(element);
7486 boolean oben_frei = FALSE, unten_frei = FALSE;
7487 boolean links_frei = FALSE, rechts_frei = FALSE;
7488 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7489 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7490 boolean new_wall = FALSE;
7492 if (IS_ANIMATED(graphic))
7493 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7495 if (!MovDelay[ax][ay]) /* start building new wall */
7496 MovDelay[ax][ay] = 6;
7498 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7501 if (MovDelay[ax][ay])
7505 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7507 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7509 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7511 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7514 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7515 element == EL_EXPANDABLE_WALL_ANY)
7519 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7520 Store[ax][ay-1] = element;
7521 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7522 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7523 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7524 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7529 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7530 Store[ax][ay+1] = element;
7531 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7532 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7533 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7534 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7539 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7540 element == EL_EXPANDABLE_WALL_ANY ||
7541 element == EL_EXPANDABLE_WALL)
7545 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7546 Store[ax-1][ay] = element;
7547 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7548 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7549 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7550 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7556 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7557 Store[ax+1][ay] = element;
7558 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7559 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7560 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7561 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7566 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7567 DrawLevelField(ax, ay);
7569 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7571 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7572 unten_massiv = TRUE;
7573 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7574 links_massiv = TRUE;
7575 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7576 rechts_massiv = TRUE;
7578 if (((oben_massiv && unten_massiv) ||
7579 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7580 element == EL_EXPANDABLE_WALL) &&
7581 ((links_massiv && rechts_massiv) ||
7582 element == EL_EXPANDABLE_WALL_VERTICAL))
7583 Feld[ax][ay] = EL_WALL;
7587 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7589 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7593 void CheckForDragon(int x, int y)
7596 boolean dragon_found = FALSE;
7597 static int xy[4][2] =
7605 for (i = 0; i < NUM_DIRECTIONS; i++)
7607 for (j = 0; j < 4; j++)
7609 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7611 if (IN_LEV_FIELD(xx, yy) &&
7612 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7614 if (Feld[xx][yy] == EL_DRAGON)
7615 dragon_found = TRUE;
7624 for (i = 0; i < NUM_DIRECTIONS; i++)
7626 for (j = 0; j < 3; j++)
7628 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7630 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7632 Feld[xx][yy] = EL_EMPTY;
7633 DrawLevelField(xx, yy);
7642 static void InitBuggyBase(int x, int y)
7644 int element = Feld[x][y];
7645 int activating_delay = FRAMES_PER_SECOND / 4;
7648 (element == EL_SP_BUGGY_BASE ?
7649 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7650 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7652 element == EL_SP_BUGGY_BASE_ACTIVE ?
7653 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7656 static void WarnBuggyBase(int x, int y)
7659 static int xy[4][2] =
7667 for (i = 0; i < NUM_DIRECTIONS; i++)
7669 int xx = x + xy[i][0], yy = y + xy[i][1];
7671 if (IS_PLAYER(xx, yy))
7673 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7680 static void InitTrap(int x, int y)
7682 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7685 static void ActivateTrap(int x, int y)
7687 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7690 static void ChangeActiveTrap(int x, int y)
7692 int graphic = IMG_TRAP_ACTIVE;
7694 /* if new animation frame was drawn, correct crumbled sand border */
7695 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7696 DrawLevelFieldCrumbledSand(x, y);
7699 static void ChangeElementNowExt(int x, int y, int target_element)
7701 int previous_move_direction = MovDir[x][y];
7703 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7704 IS_WALKABLE(Feld[x][y]));
7706 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7707 IS_WALKABLE(Feld[x][y]) &&
7711 /* check if element under player changes from accessible to unaccessible
7712 (needed for special case of dropping element which then changes) */
7713 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7714 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7717 printf("::: BOOOM! [%d, '%s']\n", target_element,
7718 element_info[target_element].token_name);
7730 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7731 RemoveMovingField(x, y);
7735 Feld[x][y] = target_element;
7738 Feld[x][y] = target_element;
7741 ResetGfxAnimation(x, y);
7742 ResetRandomAnimationValue(x, y);
7744 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7745 MovDir[x][y] = previous_move_direction;
7748 InitField_WithBug1(x, y, FALSE);
7750 InitField(x, y, FALSE);
7751 if (CAN_MOVE(Feld[x][y]))
7755 DrawLevelField(x, y);
7757 if (GFX_CRUMBLED(Feld[x][y]))
7758 DrawLevelFieldCrumbledSandNeighbours(x, y);
7762 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7766 TestIfBadThingTouchesHero(x, y);
7767 TestIfPlayerTouchesCustomElement(x, y);
7768 TestIfElementTouchesCustomElement(x, y);
7771 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7772 if (ELEM_IS_PLAYER(target_element))
7773 RelocatePlayer(x, y, target_element);
7776 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7780 TestIfBadThingTouchesHero(x, y);
7781 TestIfPlayerTouchesCustomElement(x, y);
7782 TestIfElementTouchesCustomElement(x, y);
7786 static boolean ChangeElementNow(int x, int y, int element, int page)
7788 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7790 int old_element = Feld[x][y];
7792 /* always use default change event to prevent running into a loop */
7793 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7794 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7796 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7798 /* reset actual trigger element and player */
7799 change->actual_trigger_element = EL_EMPTY;
7800 change->actual_trigger_player = EL_PLAYER_1;
7803 /* do not change already changed elements with same change event */
7805 if (Changed[x][y] & ChangeEvent[x][y])
7812 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7815 /* !!! indirect change before direct change !!! */
7816 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7819 if (change->explode)
7826 if (change->use_target_content)
7828 boolean complete_replace = TRUE;
7829 boolean can_replace[3][3];
7832 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7835 boolean is_walkable;
7836 boolean is_diggable;
7837 boolean is_collectible;
7838 boolean is_removable;
7839 boolean is_destructible;
7840 int ex = x + xx - 1;
7841 int ey = y + yy - 1;
7842 int content_element = change->target_content[xx][yy];
7845 can_replace[xx][yy] = TRUE;
7847 if (ex == x && ey == y) /* do not check changing element itself */
7850 if (content_element == EL_EMPTY_SPACE)
7852 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7857 if (!IN_LEV_FIELD(ex, ey))
7859 can_replace[xx][yy] = FALSE;
7860 complete_replace = FALSE;
7866 if (Changed[ex][ey]) /* do not change already changed elements */
7868 can_replace[xx][yy] = FALSE;
7869 complete_replace = FALSE;
7877 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7878 e = MovingOrBlocked2Element(ex, ey);
7883 is_empty = (IS_FREE(ex, ey) ||
7884 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7885 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7886 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7890 is_empty = (IS_FREE(ex, ey) ||
7891 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7893 is_empty = (IS_FREE(ex, ey) ||
7894 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7899 is_walkable = (is_empty || IS_WALKABLE(e));
7900 is_diggable = (is_empty || IS_DIGGABLE(e));
7901 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7902 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7903 is_removable = (is_diggable || is_collectible);
7905 can_replace[xx][yy] =
7906 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7907 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7908 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7909 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7910 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7911 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7912 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7914 if (!can_replace[xx][yy])
7915 complete_replace = FALSE;
7917 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7918 IS_WALKABLE(content_element)));
7920 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7922 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7925 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7926 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7927 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7929 can_replace[xx][yy] = FALSE;
7930 complete_replace = FALSE;
7935 if (!change->only_if_complete || complete_replace)
7937 boolean something_has_changed = FALSE;
7939 if (change->only_if_complete && change->use_random_replace &&
7940 RND(100) < change->random_percentage)
7943 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7945 int ex = x + xx - 1;
7946 int ey = y + yy - 1;
7947 int content_element;
7949 if (can_replace[xx][yy] && (!change->use_random_replace ||
7950 RND(100) < change->random_percentage))
7952 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7953 RemoveMovingField(ex, ey);
7955 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7957 content_element = change->target_content[xx][yy];
7958 target_element = GET_TARGET_ELEMENT(content_element, change);
7960 ChangeElementNowExt(ex, ey, target_element);
7962 something_has_changed = TRUE;
7964 /* for symmetry reasons, freeze newly created border elements */
7965 if (ex != x || ey != y)
7966 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7970 if (something_has_changed)
7971 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7976 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7978 ChangeElementNowExt(x, y, target_element);
7980 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7984 /* this uses direct change before indirect change */
7985 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7991 static void ChangeElement(int x, int y, int page)
7993 int element = MovingOrBlocked2Element(x, y);
7994 struct ElementInfo *ei = &element_info[element];
7995 struct ElementChangeInfo *change = &ei->change_page[page];
7998 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8001 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8002 x, y, element, element_info[element].token_name);
8003 printf("ChangeElement(): This should never happen!\n");
8008 /* this can happen with classic bombs on walkable, changing elements */
8009 if (!CAN_CHANGE(element))
8012 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8013 ChangeDelay[x][y] = 0;
8019 if (ChangeDelay[x][y] == 0) /* initialize element change */
8021 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8022 RND(change->delay_random * change->delay_frames)) + 1;
8024 ResetGfxAnimation(x, y);
8025 ResetRandomAnimationValue(x, y);
8027 if (change->pre_change_function)
8028 change->pre_change_function(x, y);
8031 ChangeDelay[x][y]--;
8033 if (ChangeDelay[x][y] != 0) /* continue element change */
8035 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8037 if (IS_ANIMATED(graphic))
8038 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8040 if (change->change_function)
8041 change->change_function(x, y);
8043 else /* finish element change */
8045 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8047 page = ChangePage[x][y];
8048 ChangePage[x][y] = -1;
8050 change = &ei->change_page[page];
8054 if (IS_MOVING(x, y) && !change->explode)
8056 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8059 ChangeDelay[x][y] = 1; /* try change after next move step */
8060 ChangePage[x][y] = page; /* remember page to use for change */
8065 if (ChangeElementNow(x, y, element, page))
8067 if (change->post_change_function)
8068 change->post_change_function(x, y);
8073 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8074 int trigger_element,
8081 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8083 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8086 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8088 int element = EL_CUSTOM_START + i;
8090 boolean change_element = FALSE;
8093 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8096 for (j = 0; j < element_info[element].num_change_pages; j++)
8098 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8100 if (change->can_change &&
8101 change->events & CH_EVENT_BIT(trigger_event) &&
8102 change->trigger_side & trigger_side &&
8103 change->trigger_player & trigger_player &&
8104 change->trigger_page & trigger_page_bits &&
8105 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8108 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8109 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8110 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8113 change_element = TRUE;
8116 change->actual_trigger_element = trigger_element;
8117 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8123 if (!change_element)
8126 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8129 if (x == lx && y == ly) /* do not change trigger element itself */
8133 if (Feld[x][y] == element)
8135 ChangeDelay[x][y] = 1;
8136 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8137 ChangeElement(x, y, page);
8145 static boolean CheckElementChangeExt(int x, int y,
8147 int trigger_element,
8153 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8156 if (Feld[x][y] == EL_BLOCKED)
8158 Blocked2Moving(x, y, &x, &y);
8159 element = Feld[x][y];
8163 if (Feld[x][y] != element) /* check if element has already changed */
8166 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8167 Feld[x][y], element_info[Feld[x][y]].token_name,
8168 element, element_info[element].token_name,
8177 if (trigger_page < 0)
8179 boolean change_element = FALSE;
8182 for (i = 0; i < element_info[element].num_change_pages; i++)
8184 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8186 if (change->can_change &&
8187 change->events & CH_EVENT_BIT(trigger_event) &&
8188 change->trigger_side & trigger_side &&
8189 change->trigger_player & trigger_player)
8191 change_element = TRUE;
8194 change->actual_trigger_element = trigger_element;
8195 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8201 if (!change_element)
8206 struct ElementInfo *ei = &element_info[element];
8207 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8209 change->actual_trigger_element = trigger_element;
8210 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8215 /* !!! this check misses pages with same event, but different side !!! */
8217 if (trigger_page < 0)
8218 trigger_page = element_info[element].event_page_nr[trigger_event];
8220 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8224 ChangeDelay[x][y] = 1;
8225 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8226 ChangeElement(x, y, trigger_page);
8231 static void PlayPlayerSound(struct PlayerInfo *player)
8233 int jx = player->jx, jy = player->jy;
8234 int element = player->element_nr;
8235 int last_action = player->last_action_waiting;
8236 int action = player->action_waiting;
8238 if (player->is_waiting)
8240 if (action != last_action)
8241 PlayLevelSoundElementAction(jx, jy, element, action);
8243 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8247 if (action != last_action)
8248 StopSound(element_info[element].sound[last_action]);
8250 if (last_action == ACTION_SLEEPING)
8251 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8255 static void PlayAllPlayersSound()
8259 for (i = 0; i < MAX_PLAYERS; i++)
8260 if (stored_player[i].active)
8261 PlayPlayerSound(&stored_player[i]);
8264 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8266 boolean last_waiting = player->is_waiting;
8267 int move_dir = player->MovDir;
8269 player->last_action_waiting = player->action_waiting;
8273 if (!last_waiting) /* not waiting -> waiting */
8275 player->is_waiting = TRUE;
8277 player->frame_counter_bored =
8279 game.player_boring_delay_fixed +
8280 SimpleRND(game.player_boring_delay_random);
8281 player->frame_counter_sleeping =
8283 game.player_sleeping_delay_fixed +
8284 SimpleRND(game.player_sleeping_delay_random);
8286 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8289 if (game.player_sleeping_delay_fixed +
8290 game.player_sleeping_delay_random > 0 &&
8291 player->anim_delay_counter == 0 &&
8292 player->post_delay_counter == 0 &&
8293 FrameCounter >= player->frame_counter_sleeping)
8294 player->is_sleeping = TRUE;
8295 else if (game.player_boring_delay_fixed +
8296 game.player_boring_delay_random > 0 &&
8297 FrameCounter >= player->frame_counter_bored)
8298 player->is_bored = TRUE;
8300 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8301 player->is_bored ? ACTION_BORING :
8304 if (player->is_sleeping)
8306 if (player->num_special_action_sleeping > 0)
8308 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8310 int last_special_action = player->special_action_sleeping;
8311 int num_special_action = player->num_special_action_sleeping;
8312 int special_action =
8313 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8314 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8315 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8316 last_special_action + 1 : ACTION_SLEEPING);
8317 int special_graphic =
8318 el_act_dir2img(player->element_nr, special_action, move_dir);
8320 player->anim_delay_counter =
8321 graphic_info[special_graphic].anim_delay_fixed +
8322 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8323 player->post_delay_counter =
8324 graphic_info[special_graphic].post_delay_fixed +
8325 SimpleRND(graphic_info[special_graphic].post_delay_random);
8327 player->special_action_sleeping = special_action;
8330 if (player->anim_delay_counter > 0)
8332 player->action_waiting = player->special_action_sleeping;
8333 player->anim_delay_counter--;
8335 else if (player->post_delay_counter > 0)
8337 player->post_delay_counter--;
8341 else if (player->is_bored)
8343 if (player->num_special_action_bored > 0)
8345 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8347 int special_action =
8348 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8349 int special_graphic =
8350 el_act_dir2img(player->element_nr, special_action, move_dir);
8352 player->anim_delay_counter =
8353 graphic_info[special_graphic].anim_delay_fixed +
8354 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8355 player->post_delay_counter =
8356 graphic_info[special_graphic].post_delay_fixed +
8357 SimpleRND(graphic_info[special_graphic].post_delay_random);
8359 player->special_action_bored = special_action;
8362 if (player->anim_delay_counter > 0)
8364 player->action_waiting = player->special_action_bored;
8365 player->anim_delay_counter--;
8367 else if (player->post_delay_counter > 0)
8369 player->post_delay_counter--;
8374 else if (last_waiting) /* waiting -> not waiting */
8376 player->is_waiting = FALSE;
8377 player->is_bored = FALSE;
8378 player->is_sleeping = FALSE;
8380 player->frame_counter_bored = -1;
8381 player->frame_counter_sleeping = -1;
8383 player->anim_delay_counter = 0;
8384 player->post_delay_counter = 0;
8386 player->action_waiting = ACTION_DEFAULT;
8388 player->special_action_bored = ACTION_DEFAULT;
8389 player->special_action_sleeping = ACTION_DEFAULT;
8394 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8397 static byte stored_player_action[MAX_PLAYERS];
8398 static int num_stored_actions = 0;
8400 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8401 int left = player_action & JOY_LEFT;
8402 int right = player_action & JOY_RIGHT;
8403 int up = player_action & JOY_UP;
8404 int down = player_action & JOY_DOWN;
8405 int button1 = player_action & JOY_BUTTON_1;
8406 int button2 = player_action & JOY_BUTTON_2;
8407 int dx = (left ? -1 : right ? 1 : 0);
8408 int dy = (up ? -1 : down ? 1 : 0);
8411 stored_player_action[player->index_nr] = 0;
8412 num_stored_actions++;
8416 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8419 if (!player->active || tape.pausing)
8423 printf("::: [%d %d %d %d] [%d %d]\n",
8424 left, right, up, down, button1, button2);
8430 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8435 if (player->MovPos == 0)
8436 CheckGravityMovement(player);
8439 snapped = SnapField(player, dx, dy);
8443 dropped = DropElement(player);
8445 moved = MovePlayer(player, dx, dy);
8448 if (tape.single_step && tape.recording && !tape.pausing)
8450 if (button1 || (dropped && !moved))
8452 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8453 SnapField(player, 0, 0); /* stop snapping */
8457 SetPlayerWaiting(player, FALSE);
8460 return player_action;
8462 stored_player_action[player->index_nr] = player_action;
8468 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8471 /* no actions for this player (no input at player's configured device) */
8473 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8474 SnapField(player, 0, 0);
8475 CheckGravityMovementWhenNotMoving(player);
8477 if (player->MovPos == 0)
8478 SetPlayerWaiting(player, TRUE);
8480 if (player->MovPos == 0) /* needed for tape.playing */
8481 player->is_moving = FALSE;
8483 player->is_dropping = FALSE;
8489 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8491 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8493 TapeRecordAction(stored_player_action);
8494 num_stored_actions = 0;
8501 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8503 static byte stored_player_action[MAX_PLAYERS];
8504 static int num_stored_actions = 0;
8505 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8506 int left = player_action & JOY_LEFT;
8507 int right = player_action & JOY_RIGHT;
8508 int up = player_action & JOY_UP;
8509 int down = player_action & JOY_DOWN;
8510 int button1 = player_action & JOY_BUTTON_1;
8511 int button2 = player_action & JOY_BUTTON_2;
8512 int dx = (left ? -1 : right ? 1 : 0);
8513 int dy = (up ? -1 : down ? 1 : 0);
8515 stored_player_action[player->index_nr] = 0;
8516 num_stored_actions++;
8518 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8520 if (!player->active || tape.pausing)
8525 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8528 snapped = SnapField(player, dx, dy);
8532 dropped = DropElement(player);
8534 moved = MovePlayer(player, dx, dy);
8537 if (tape.single_step && tape.recording && !tape.pausing)
8539 if (button1 || (dropped && !moved))
8541 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8542 SnapField(player, 0, 0); /* stop snapping */
8546 stored_player_action[player->index_nr] = player_action;
8550 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8552 /* no actions for this player (no input at player's configured device) */
8554 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8555 SnapField(player, 0, 0);
8556 CheckGravityMovementWhenNotMoving(player);
8558 if (player->MovPos == 0)
8559 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8561 if (player->MovPos == 0) /* needed for tape.playing */
8562 player->is_moving = FALSE;
8565 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8567 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8569 TapeRecordAction(stored_player_action);
8570 num_stored_actions = 0;
8575 void AdvanceFrameAndPlayerCounters(int player_nr)
8579 /* advance frame counters (global frame counter and time frame counter) */
8583 /* advance player counters (counters for move delay, move animation etc.) */
8584 for (i = 0; i < MAX_PLAYERS; i++)
8586 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8588 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8590 if (!advance_player_counters) /* not all players may be affected */
8593 stored_player[i].Frame += move_frames;
8595 if (stored_player[i].MovPos != 0)
8596 stored_player[i].StepFrame += move_frames;
8598 #if USE_NEW_MOVE_DELAY
8599 if (stored_player[i].move_delay > 0)
8600 stored_player[i].move_delay--;
8603 #if USE_NEW_PUSH_DELAY
8604 /* due to bugs in previous versions, counter must count up, not down */
8605 if (stored_player[i].push_delay != -1)
8606 stored_player[i].push_delay++;
8609 if (stored_player[i].drop_delay > 0)
8610 stored_player[i].drop_delay--;
8616 static unsigned long game_frame_delay = 0;
8617 unsigned long game_frame_delay_value;
8618 int magic_wall_x = 0, magic_wall_y = 0;
8619 int i, x, y, element, graphic;
8620 byte *recorded_player_action;
8621 byte summarized_player_action = 0;
8623 byte tape_action[MAX_PLAYERS];
8626 if (game_status != GAME_MODE_PLAYING)
8629 game_frame_delay_value =
8630 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8632 if (tape.playing && tape.warp_forward && !tape.pausing)
8633 game_frame_delay_value = 0;
8635 /* ---------- main game synchronization point ---------- */
8637 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8639 if (network_playing && !network_player_action_received)
8643 printf("DEBUG: try to get network player actions in time\n");
8647 #if defined(NETWORK_AVALIABLE)
8648 /* last chance to get network player actions without main loop delay */
8652 if (game_status != GAME_MODE_PLAYING)
8655 if (!network_player_action_received)
8659 printf("DEBUG: failed to get network player actions in time\n");
8670 printf("::: getting new tape action [%d]\n", FrameCounter);
8673 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8676 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8677 if (recorded_player_action == NULL && tape.pausing)
8682 printf("::: %d\n", stored_player[0].action);
8686 if (recorded_player_action != NULL)
8687 for (i = 0; i < MAX_PLAYERS; i++)
8688 stored_player[i].action = recorded_player_action[i];
8691 for (i = 0; i < MAX_PLAYERS; i++)
8693 summarized_player_action |= stored_player[i].action;
8695 if (!network_playing)
8696 stored_player[i].effective_action = stored_player[i].action;
8699 #if defined(NETWORK_AVALIABLE)
8700 if (network_playing)
8701 SendToServer_MovePlayer(summarized_player_action);
8704 if (!options.network && !setup.team_mode)
8705 local_player->effective_action = summarized_player_action;
8708 if (recorded_player_action != NULL)
8709 for (i = 0; i < MAX_PLAYERS; i++)
8710 stored_player[i].effective_action = recorded_player_action[i];
8714 for (i = 0; i < MAX_PLAYERS; i++)
8716 tape_action[i] = stored_player[i].effective_action;
8718 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8719 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8722 /* only save actions from input devices, but not programmed actions */
8724 TapeRecordAction(tape_action);
8727 for (i = 0; i < MAX_PLAYERS; i++)
8729 int actual_player_action = stored_player[i].effective_action;
8732 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8733 - rnd_equinox_tetrachloride 048
8734 - rnd_equinox_tetrachloride_ii 096
8735 - rnd_emanuel_schmieg 002
8736 - doctor_sloan_ww 001, 020
8738 if (stored_player[i].MovPos == 0)
8739 CheckGravityMovement(&stored_player[i]);
8743 /* overwrite programmed action with tape action */
8744 if (stored_player[i].programmed_action)
8745 actual_player_action = stored_player[i].programmed_action;
8749 if (stored_player[i].programmed_action)
8750 printf("::: %d\n", stored_player[i].programmed_action);
8753 if (recorded_player_action)
8756 if (stored_player[i].programmed_action &&
8757 stored_player[i].programmed_action != recorded_player_action[i])
8758 printf("::: %d: %d <-> %d\n", i,
8759 stored_player[i].programmed_action, recorded_player_action[i]);
8763 actual_player_action = recorded_player_action[i];
8768 /* overwrite tape action with programmed action */
8769 if (stored_player[i].programmed_action)
8770 actual_player_action = stored_player[i].programmed_action;
8775 printf("::: action: %d: %x [%d]\n",
8776 stored_player[i].MovPos, actual_player_action, FrameCounter);
8780 PlayerActions(&stored_player[i], actual_player_action);
8782 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8784 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8785 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8788 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8793 TapeRecordAction(tape_action);
8796 network_player_action_received = FALSE;
8798 ScrollScreen(NULL, SCROLL_GO_ON);
8804 for (i = 0; i < MAX_PLAYERS; i++)
8805 stored_player[i].Frame++;
8809 /* for backwards compatibility, the following code emulates a fixed bug that
8810 occured when pushing elements (causing elements that just made their last
8811 pushing step to already (if possible) make their first falling step in the
8812 same game frame, which is bad); this code is also needed to use the famous
8813 "spring push bug" which is used in older levels and might be wanted to be
8814 used also in newer levels, but in this case the buggy pushing code is only
8815 affecting the "spring" element and no other elements */
8818 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8820 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8823 for (i = 0; i < MAX_PLAYERS; i++)
8825 struct PlayerInfo *player = &stored_player[i];
8830 if (player->active && player->is_pushing && player->is_moving &&
8832 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8833 Feld[x][y] == EL_SPRING))
8835 if (player->active && player->is_pushing && player->is_moving &&
8839 ContinueMoving(x, y);
8841 /* continue moving after pushing (this is actually a bug) */
8842 if (!IS_MOVING(x, y))
8851 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8853 Changed[x][y] = CE_BITMASK_DEFAULT;
8854 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8856 #if USE_NEW_BLOCK_STYLE
8857 /* this must be handled before main playfield loop */
8858 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8861 if (MovDelay[x][y] <= 0)
8867 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8869 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8870 printf("GameActions(): This should never happen!\n");
8872 ChangePage[x][y] = -1;
8877 if (WasJustMoving[x][y] > 0)
8878 WasJustMoving[x][y]--;
8879 if (WasJustFalling[x][y] > 0)
8880 WasJustFalling[x][y]--;
8881 if (CheckCollision[x][y] > 0)
8882 CheckCollision[x][y]--;
8887 /* reset finished pushing action (not done in ContinueMoving() to allow
8888 continous pushing animation for elements with zero push delay) */
8889 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8891 ResetGfxAnimation(x, y);
8892 DrawLevelField(x, y);
8897 if (IS_BLOCKED(x, y))
8901 Blocked2Moving(x, y, &oldx, &oldy);
8902 if (!IS_MOVING(oldx, oldy))
8904 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8905 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8906 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8907 printf("GameActions(): This should never happen!\n");
8913 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8915 element = Feld[x][y];
8917 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8919 graphic = el2img(element);
8925 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8927 element = graphic = 0;
8931 if (graphic_info[graphic].anim_global_sync)
8932 GfxFrame[x][y] = FrameCounter;
8934 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8935 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8936 ResetRandomAnimationValue(x, y);
8938 SetRandomAnimationValue(x, y);
8941 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8944 if (IS_INACTIVE(element))
8946 if (IS_ANIMATED(graphic))
8947 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8953 /* this may take place after moving, so 'element' may have changed */
8955 if (IS_CHANGING(x, y))
8957 if (IS_CHANGING(x, y) &&
8958 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8962 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8963 element_info[element].event_page_nr[CE_DELAY]);
8965 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8968 element = Feld[x][y];
8969 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8973 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8978 element = Feld[x][y];
8979 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8981 if (element == EL_MOLE)
8982 printf("::: %d, %d, %d [%d]\n",
8983 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8987 if (element == EL_YAMYAM)
8988 printf("::: %d, %d, %d\n",
8989 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8993 if (IS_ANIMATED(graphic) &&
8997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9000 if (element == EL_BUG)
9001 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9005 if (element == EL_MOLE)
9006 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9010 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9011 EdelsteinFunkeln(x, y);
9013 else if ((element == EL_ACID ||
9014 element == EL_EXIT_OPEN ||
9015 element == EL_SP_EXIT_OPEN ||
9016 element == EL_SP_TERMINAL ||
9017 element == EL_SP_TERMINAL_ACTIVE ||
9018 element == EL_EXTRA_TIME ||
9019 element == EL_SHIELD_NORMAL ||
9020 element == EL_SHIELD_DEADLY) &&
9021 IS_ANIMATED(graphic))
9022 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9023 else if (IS_MOVING(x, y))
9024 ContinueMoving(x, y);
9025 else if (IS_ACTIVE_BOMB(element))
9026 CheckDynamite(x, y);
9028 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9029 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9031 else if (element == EL_AMOEBA_GROWING)
9032 AmoebeWaechst(x, y);
9033 else if (element == EL_AMOEBA_SHRINKING)
9034 AmoebaDisappearing(x, y);
9036 #if !USE_NEW_AMOEBA_CODE
9037 else if (IS_AMOEBALIVE(element))
9038 AmoebeAbleger(x, y);
9041 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9043 else if (element == EL_EXIT_CLOSED)
9045 else if (element == EL_SP_EXIT_CLOSED)
9047 else if (element == EL_EXPANDABLE_WALL_GROWING)
9049 else if (element == EL_EXPANDABLE_WALL ||
9050 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9051 element == EL_EXPANDABLE_WALL_VERTICAL ||
9052 element == EL_EXPANDABLE_WALL_ANY)
9054 else if (element == EL_FLAMES)
9055 CheckForDragon(x, y);
9057 else if (IS_AUTO_CHANGING(element))
9058 ChangeElement(x, y);
9060 else if (element == EL_EXPLOSION)
9061 ; /* drawing of correct explosion animation is handled separately */
9062 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9066 /* this may take place after moving, so 'element' may have changed */
9067 if (IS_AUTO_CHANGING(Feld[x][y]))
9068 ChangeElement(x, y);
9071 if (IS_BELT_ACTIVE(element))
9072 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9074 if (game.magic_wall_active)
9076 int jx = local_player->jx, jy = local_player->jy;
9078 /* play the element sound at the position nearest to the player */
9079 if ((element == EL_MAGIC_WALL_FULL ||
9080 element == EL_MAGIC_WALL_ACTIVE ||
9081 element == EL_MAGIC_WALL_EMPTYING ||
9082 element == EL_BD_MAGIC_WALL_FULL ||
9083 element == EL_BD_MAGIC_WALL_ACTIVE ||
9084 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9085 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9093 #if USE_NEW_AMOEBA_CODE
9094 /* new experimental amoeba growth stuff */
9096 if (!(FrameCounter % 8))
9099 static unsigned long random = 1684108901;
9101 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9104 x = (random >> 10) % lev_fieldx;
9105 y = (random >> 20) % lev_fieldy;
9107 x = RND(lev_fieldx);
9108 y = RND(lev_fieldy);
9110 element = Feld[x][y];
9113 if (!IS_PLAYER(x,y) &&
9114 (element == EL_EMPTY ||
9115 CAN_GROW_INTO(element) ||
9116 element == EL_QUICKSAND_EMPTY ||
9117 element == EL_ACID_SPLASH_LEFT ||
9118 element == EL_ACID_SPLASH_RIGHT))
9120 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9121 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9122 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9123 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9124 Feld[x][y] = EL_AMOEBA_DROP;
9127 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9128 if (!IS_PLAYER(x,y) &&
9129 (element == EL_EMPTY ||
9130 element == EL_SAND ||
9131 element == EL_QUICKSAND_EMPTY ||
9132 element == EL_ACID_SPLASH_LEFT ||
9133 element == EL_ACID_SPLASH_RIGHT))
9135 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9136 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9137 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9138 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9139 Feld[x][y] = EL_AMOEBA_DROP;
9143 random = random * 129 + 1;
9149 if (game.explosions_delayed)
9152 game.explosions_delayed = FALSE;
9154 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9156 element = Feld[x][y];
9158 if (ExplodeField[x][y])
9159 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9160 else if (element == EL_EXPLOSION)
9161 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9163 ExplodeField[x][y] = EX_TYPE_NONE;
9166 game.explosions_delayed = TRUE;
9169 if (game.magic_wall_active)
9171 if (!(game.magic_wall_time_left % 4))
9173 int element = Feld[magic_wall_x][magic_wall_y];
9175 if (element == EL_BD_MAGIC_WALL_FULL ||
9176 element == EL_BD_MAGIC_WALL_ACTIVE ||
9177 element == EL_BD_MAGIC_WALL_EMPTYING)
9178 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9180 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9183 if (game.magic_wall_time_left > 0)
9185 game.magic_wall_time_left--;
9186 if (!game.magic_wall_time_left)
9188 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9190 element = Feld[x][y];
9192 if (element == EL_MAGIC_WALL_ACTIVE ||
9193 element == EL_MAGIC_WALL_FULL)
9195 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9196 DrawLevelField(x, y);
9198 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9199 element == EL_BD_MAGIC_WALL_FULL)
9201 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9202 DrawLevelField(x, y);
9206 game.magic_wall_active = FALSE;
9211 if (game.light_time_left > 0)
9213 game.light_time_left--;
9215 if (game.light_time_left == 0)
9216 RedrawAllLightSwitchesAndInvisibleElements();
9219 if (game.timegate_time_left > 0)
9221 game.timegate_time_left--;
9223 if (game.timegate_time_left == 0)
9224 CloseAllOpenTimegates();
9227 for (i = 0; i < MAX_PLAYERS; i++)
9229 struct PlayerInfo *player = &stored_player[i];
9231 if (SHIELD_ON(player))
9233 if (player->shield_deadly_time_left)
9234 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9235 else if (player->shield_normal_time_left)
9236 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9240 if (TimeFrames >= FRAMES_PER_SECOND)
9245 for (i = 0; i < MAX_PLAYERS; i++)
9247 struct PlayerInfo *player = &stored_player[i];
9249 if (SHIELD_ON(player))
9251 player->shield_normal_time_left--;
9253 if (player->shield_deadly_time_left > 0)
9254 player->shield_deadly_time_left--;
9258 if (!level.use_step_counter)
9266 if (TimeLeft <= 10 && setup.time_limit)
9267 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9269 DrawGameValue_Time(TimeLeft);
9271 if (!TimeLeft && setup.time_limit)
9272 for (i = 0; i < MAX_PLAYERS; i++)
9273 KillHero(&stored_player[i]);
9275 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9276 DrawGameValue_Time(TimePlayed);
9279 if (tape.recording || tape.playing)
9280 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9284 PlayAllPlayersSound();
9286 if (options.debug) /* calculate frames per second */
9288 static unsigned long fps_counter = 0;
9289 static int fps_frames = 0;
9290 unsigned long fps_delay_ms = Counter() - fps_counter;
9294 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9296 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9299 fps_counter = Counter();
9302 redraw_mask |= REDRAW_FPS;
9306 if (stored_player[0].jx != stored_player[0].last_jx ||
9307 stored_player[0].jy != stored_player[0].last_jy)
9308 printf("::: %d, %d, %d, %d, %d\n",
9309 stored_player[0].MovDir,
9310 stored_player[0].MovPos,
9311 stored_player[0].GfxPos,
9312 stored_player[0].Frame,
9313 stored_player[0].StepFrame);
9316 #if USE_NEW_MOVE_DELAY
9317 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9322 for (i = 0; i < MAX_PLAYERS; i++)
9325 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9327 stored_player[i].Frame += move_frames;
9329 if (stored_player[i].MovPos != 0)
9330 stored_player[i].StepFrame += move_frames;
9332 #if USE_NEW_MOVE_DELAY
9333 if (stored_player[i].move_delay > 0)
9334 stored_player[i].move_delay--;
9337 if (stored_player[i].drop_delay > 0)
9338 stored_player[i].drop_delay--;
9343 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9345 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9347 local_player->show_envelope = 0;
9351 #if USE_NEW_RANDOMIZE
9352 /* use random number generator in every frame to make it less predictable */
9353 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9358 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9360 int min_x = x, min_y = y, max_x = x, max_y = y;
9363 for (i = 0; i < MAX_PLAYERS; i++)
9365 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9367 if (!stored_player[i].active || &stored_player[i] == player)
9370 min_x = MIN(min_x, jx);
9371 min_y = MIN(min_y, jy);
9372 max_x = MAX(max_x, jx);
9373 max_y = MAX(max_y, jy);
9376 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9379 static boolean AllPlayersInVisibleScreen()
9383 for (i = 0; i < MAX_PLAYERS; i++)
9385 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9387 if (!stored_player[i].active)
9390 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9397 void ScrollLevel(int dx, int dy)
9399 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9402 BlitBitmap(drawto_field, drawto_field,
9403 FX + TILEX * (dx == -1) - softscroll_offset,
9404 FY + TILEY * (dy == -1) - softscroll_offset,
9405 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9406 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9407 FX + TILEX * (dx == 1) - softscroll_offset,
9408 FY + TILEY * (dy == 1) - softscroll_offset);
9412 x = (dx == 1 ? BX1 : BX2);
9413 for (y = BY1; y <= BY2; y++)
9414 DrawScreenField(x, y);
9419 y = (dy == 1 ? BY1 : BY2);
9420 for (x = BX1; x <= BX2; x++)
9421 DrawScreenField(x, y);
9424 redraw_mask |= REDRAW_FIELD;
9428 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9430 int nextx = x + dx, nexty = y + dy;
9431 int element = Feld[x][y];
9434 element != EL_SP_PORT_LEFT &&
9435 element != EL_SP_GRAVITY_PORT_LEFT &&
9436 element != EL_SP_PORT_HORIZONTAL &&
9437 element != EL_SP_PORT_ANY) ||
9439 element != EL_SP_PORT_RIGHT &&
9440 element != EL_SP_GRAVITY_PORT_RIGHT &&
9441 element != EL_SP_PORT_HORIZONTAL &&
9442 element != EL_SP_PORT_ANY) ||
9444 element != EL_SP_PORT_UP &&
9445 element != EL_SP_GRAVITY_PORT_UP &&
9446 element != EL_SP_PORT_VERTICAL &&
9447 element != EL_SP_PORT_ANY) ||
9449 element != EL_SP_PORT_DOWN &&
9450 element != EL_SP_GRAVITY_PORT_DOWN &&
9451 element != EL_SP_PORT_VERTICAL &&
9452 element != EL_SP_PORT_ANY) ||
9453 !IN_LEV_FIELD(nextx, nexty) ||
9454 !IS_FREE(nextx, nexty))
9461 static boolean canFallDown(struct PlayerInfo *player)
9463 int jx = player->jx, jy = player->jy;
9465 return (IN_LEV_FIELD(jx, jy + 1) &&
9466 (IS_FREE(jx, jy + 1) ||
9467 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9468 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9469 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9472 static boolean canPassField(int x, int y, int move_dir)
9474 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9475 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9476 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9479 int element = Feld[x][y];
9481 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9482 !CAN_MOVE(element) &&
9483 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9484 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9485 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9488 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9490 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9491 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9492 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9496 int nextx = newx + dx;
9497 int nexty = newy + dy;
9501 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9502 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9504 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9506 (IS_DIGGABLE(Feld[newx][newy]) ||
9507 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9508 canPassField(newx, newy, move_dir)));
9511 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9512 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9513 (IS_DIGGABLE(Feld[newx][newy]) ||
9514 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9515 canPassField(newx, newy, move_dir)));
9518 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9519 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9520 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9521 canPassField(newx, newy, move_dir)));
9523 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9524 (IS_DIGGABLE(Feld[newx][newy]) ||
9525 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9526 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9527 !CAN_MOVE(Feld[newx][newy]) &&
9528 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9529 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9530 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9536 static void CheckGravityMovement(struct PlayerInfo *player)
9538 if (game.gravity && !player->programmed_action)
9541 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9542 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9544 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9545 int move_dir_vertical = player->action & MV_VERTICAL;
9549 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9551 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9554 int jx = player->jx, jy = player->jy;
9556 boolean player_is_moving_to_valid_field =
9557 (!player_is_snapping &&
9558 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9559 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9563 (player->last_move_dir & MV_HORIZONTAL ?
9564 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9565 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9569 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9570 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9571 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9572 int new_jx = jx + dx, new_jy = jy + dy;
9573 int nextx = new_jx + dx, nexty = new_jy + dy;
9579 boolean player_can_fall_down = canFallDown(player);
9581 boolean player_can_fall_down =
9582 (IN_LEV_FIELD(jx, jy + 1) &&
9583 (IS_FREE(jx, jy + 1) ||
9584 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9588 boolean player_can_fall_down =
9589 (IN_LEV_FIELD(jx, jy + 1) &&
9590 (IS_FREE(jx, jy + 1)));
9594 boolean player_is_moving_to_valid_field =
9597 !player_is_snapping &&
9601 IN_LEV_FIELD(new_jx, new_jy) &&
9602 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9603 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9604 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9605 IN_LEV_FIELD(nextx, nexty) &&
9606 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9608 IN_LEV_FIELD(new_jx, new_jy) &&
9609 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9610 Feld[new_jx][new_jy] == EL_SAND ||
9611 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9612 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9613 /* !!! extend EL_SAND to anything diggable !!! */
9619 boolean player_is_standing_on_valid_field =
9620 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9621 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9625 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9626 player_can_fall_down,
9627 player_is_standing_on_valid_field,
9628 player_is_moving_to_valid_field,
9629 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9630 player->effective_action,
9631 player->can_fall_into_acid);
9634 if (player_can_fall_down &&
9636 !player_is_standing_on_valid_field &&
9638 !player_is_moving_to_valid_field)
9641 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9642 jx, jy, FrameCounter);
9645 player->programmed_action = MV_DOWN;
9650 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9653 return CheckGravityMovement(player);
9656 if (game.gravity && !player->programmed_action)
9658 int jx = player->jx, jy = player->jy;
9659 boolean field_under_player_is_free =
9660 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9661 boolean player_is_standing_on_valid_field =
9662 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9663 (IS_WALKABLE(Feld[jx][jy]) &&
9664 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9666 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9667 player->programmed_action = MV_DOWN;
9673 -----------------------------------------------------------------------------
9674 dx, dy: direction (non-diagonal) to try to move the player to
9675 real_dx, real_dy: direction as read from input device (can be diagonal)
9678 boolean MovePlayerOneStep(struct PlayerInfo *player,
9679 int dx, int dy, int real_dx, int real_dy)
9682 static int trigger_sides[4][2] =
9684 /* enter side leave side */
9685 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9686 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9687 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9688 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9690 int move_direction = (dx == -1 ? MV_LEFT :
9691 dx == +1 ? MV_RIGHT :
9693 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9694 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9695 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9697 int jx = player->jx, jy = player->jy;
9698 int new_jx = jx + dx, new_jy = jy + dy;
9702 if (!player->active || (!dx && !dy))
9703 return MF_NO_ACTION;
9705 player->MovDir = (dx < 0 ? MV_LEFT :
9708 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9710 if (!IN_LEV_FIELD(new_jx, new_jy))
9711 return MF_NO_ACTION;
9713 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9714 return MF_NO_ACTION;
9717 element = MovingOrBlocked2Element(new_jx, new_jy);
9719 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9722 if (DONT_RUN_INTO(element))
9724 if (element == EL_ACID && dx == 0 && dy == 1)
9726 SplashAcid(new_jx, new_jy);
9727 Feld[jx][jy] = EL_PLAYER_1;
9728 InitMovingField(jx, jy, MV_DOWN);
9729 Store[jx][jy] = EL_ACID;
9730 ContinueMoving(jx, jy);
9734 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9739 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9740 if (can_move != MF_MOVING)
9743 /* check if DigField() has caused relocation of the player */
9744 if (player->jx != jx || player->jy != jy)
9745 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9747 StorePlayer[jx][jy] = 0;
9748 player->last_jx = jx;
9749 player->last_jy = jy;
9750 player->jx = new_jx;
9751 player->jy = new_jy;
9752 StorePlayer[new_jx][new_jy] = player->element_nr;
9755 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9757 player->step_counter++;
9760 player->drop_delay = 0;
9763 PlayerVisit[jx][jy] = FrameCounter;
9765 ScrollPlayer(player, SCROLL_INIT);
9768 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9770 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9772 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9775 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9777 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9778 CE_OTHER_GETS_ENTERED, enter_side);
9779 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9780 CE_ENTERED_BY_PLAYER, enter_side);
9787 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9789 int jx = player->jx, jy = player->jy;
9790 int old_jx = jx, old_jy = jy;
9791 int moved = MF_NO_ACTION;
9794 if (!player->active)
9799 if (player->MovPos == 0)
9801 player->is_moving = FALSE;
9802 player->is_digging = FALSE;
9803 player->is_collecting = FALSE;
9804 player->is_snapping = FALSE;
9805 player->is_pushing = FALSE;
9811 if (!player->active || (!dx && !dy))
9816 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9824 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9825 player->move_delay + player->move_delay_value);
9828 #if USE_NEW_MOVE_DELAY
9829 if (player->move_delay > 0)
9831 if (!FrameReached(&player->move_delay, player->move_delay_value))
9835 printf("::: can NOT move\n");
9841 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9842 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9849 printf("::: COULD move now\n");
9852 #if USE_NEW_MOVE_DELAY
9853 player->move_delay = -1; /* set to "uninitialized" value */
9856 /* store if player is automatically moved to next field */
9857 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9859 /* remove the last programmed player action */
9860 player->programmed_action = 0;
9864 /* should only happen if pre-1.2 tape recordings are played */
9865 /* this is only for backward compatibility */
9867 int original_move_delay_value = player->move_delay_value;
9870 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9874 /* scroll remaining steps with finest movement resolution */
9875 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9877 while (player->MovPos)
9879 ScrollPlayer(player, SCROLL_GO_ON);
9880 ScrollScreen(NULL, SCROLL_GO_ON);
9882 #if USE_NEW_MOVE_DELAY
9883 AdvanceFrameAndPlayerCounters(player->index_nr);
9892 player->move_delay_value = original_move_delay_value;
9895 if (player->last_move_dir & MV_HORIZONTAL)
9897 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9898 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9902 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9903 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9909 if (moved & MF_MOVING && !ScreenMovPos &&
9910 (player == local_player || !options.network))
9912 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9913 int offset = (setup.scroll_delay ? 3 : 0);
9915 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9917 /* actual player has left the screen -- scroll in that direction */
9918 if (jx != old_jx) /* player has moved horizontally */
9919 scroll_x += (jx - old_jx);
9920 else /* player has moved vertically */
9921 scroll_y += (jy - old_jy);
9925 if (jx != old_jx) /* player has moved horizontally */
9927 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9928 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9929 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9931 /* don't scroll over playfield boundaries */
9932 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9933 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9935 /* don't scroll more than one field at a time */
9936 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9938 /* don't scroll against the player's moving direction */
9939 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9940 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9941 scroll_x = old_scroll_x;
9943 else /* player has moved vertically */
9945 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9946 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9947 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9949 /* don't scroll over playfield boundaries */
9950 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9951 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9953 /* don't scroll more than one field at a time */
9954 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9956 /* don't scroll against the player's moving direction */
9957 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9958 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9959 scroll_y = old_scroll_y;
9963 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9965 if (!options.network && !AllPlayersInVisibleScreen())
9967 scroll_x = old_scroll_x;
9968 scroll_y = old_scroll_y;
9972 ScrollScreen(player, SCROLL_INIT);
9973 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9980 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9982 if (!(moved & MF_MOVING) && !player->is_pushing)
9987 player->StepFrame = 0;
9989 if (moved & MF_MOVING)
9992 printf("::: REALLY moves now\n");
9995 if (old_jx != jx && old_jy == jy)
9996 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9997 else if (old_jx == jx && old_jy != jy)
9998 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10000 DrawLevelField(jx, jy); /* for "crumbled sand" */
10002 player->last_move_dir = player->MovDir;
10003 player->is_moving = TRUE;
10005 player->is_snapping = FALSE;
10009 player->is_switching = FALSE;
10012 player->is_dropping = FALSE;
10016 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10019 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10022 int move_direction = player->MovDir;
10024 int enter_side = MV_DIR_OPPOSITE(move_direction);
10025 int leave_side = move_direction;
10027 static int trigger_sides[4][2] =
10029 /* enter side leave side */
10030 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10031 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10032 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10033 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10035 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10036 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10038 int old_element = Feld[old_jx][old_jy];
10039 int new_element = Feld[jx][jy];
10042 /* !!! TEST ONLY !!! */
10043 if (IS_CUSTOM_ELEMENT(old_element))
10044 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10046 player->index_bit, leave_side);
10048 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10049 CE_OTHER_GETS_LEFT,
10050 player->index_bit, leave_side);
10052 if (IS_CUSTOM_ELEMENT(new_element))
10053 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10054 player->index_bit, enter_side);
10056 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10057 CE_OTHER_GETS_ENTERED,
10058 player->index_bit, enter_side);
10068 CheckGravityMovementWhenNotMoving(player);
10071 player->last_move_dir = MV_NO_MOVING;
10073 player->is_moving = FALSE;
10075 #if USE_NEW_MOVE_STYLE
10076 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10077 /* ensure that the player is also allowed to move in the next frame */
10078 /* (currently, the player is forced to wait eight frames before he can try
10081 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10082 player->move_delay = 0; /* allow direct movement in the next frame */
10086 #if USE_NEW_MOVE_DELAY
10087 if (player->move_delay == -1) /* not yet initialized by DigField() */
10088 player->move_delay = player->move_delay_value;
10091 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10093 TestIfHeroTouchesBadThing(jx, jy);
10094 TestIfPlayerTouchesCustomElement(jx, jy);
10097 if (!player->active)
10098 RemoveHero(player);
10103 void ScrollPlayer(struct PlayerInfo *player, int mode)
10105 int jx = player->jx, jy = player->jy;
10106 int last_jx = player->last_jx, last_jy = player->last_jy;
10107 int move_stepsize = TILEX / player->move_delay_value;
10109 if (!player->active || !player->MovPos)
10112 if (mode == SCROLL_INIT)
10114 player->actual_frame_counter = FrameCounter;
10115 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10118 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10120 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10121 player->block_delay);
10124 #if USE_NEW_BLOCK_STYLE
10127 if (player->block_delay <= 0)
10128 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10131 if (player->block_delay > 0 &&
10132 Feld[last_jx][last_jy] == EL_EMPTY)
10134 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10135 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10138 #if USE_NEW_MOVE_STYLE
10139 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10140 player->block_last_field) &&
10141 Feld[last_jx][last_jy] == EL_EMPTY)
10142 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10144 if (Feld[last_jx][last_jy] == EL_EMPTY)
10145 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10150 DrawPlayer(player);
10155 else if (!FrameReached(&player->actual_frame_counter, 1))
10158 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10159 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10161 #if USE_NEW_BLOCK_STYLE
10163 if (!player->block_last_field &&
10164 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10166 RemoveField(last_jx, last_jy);
10168 Feld[last_jx][last_jy] = EL_EMPTY;
10172 /* before DrawPlayer() to draw correct player graphic for this case */
10173 if (player->MovPos == 0)
10174 CheckGravityMovement(player);
10177 DrawPlayer(player); /* needed here only to cleanup last field */
10180 if (player->MovPos == 0) /* player reached destination field */
10183 if (player->move_delay_reset_counter > 0)
10185 player->move_delay_reset_counter--;
10187 if (player->move_delay_reset_counter == 0)
10189 /* continue with normal speed after quickly moving through gate */
10190 HALVE_PLAYER_SPEED(player);
10192 /* be able to make the next move without delay */
10193 player->move_delay = 0;
10197 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10199 /* continue with normal speed after quickly moving through gate */
10200 HALVE_PLAYER_SPEED(player);
10202 /* be able to make the next move without delay */
10203 player->move_delay = 0;
10207 #if USE_NEW_BLOCK_STYLE
10209 if (player->block_last_field &&
10210 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10212 RemoveField(last_jx, last_jy);
10214 Feld[last_jx][last_jy] = EL_EMPTY;
10218 player->last_jx = jx;
10219 player->last_jy = jy;
10221 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10222 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10223 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10225 DrawPlayer(player); /* needed here only to cleanup last field */
10226 RemoveHero(player);
10228 if (local_player->friends_still_needed == 0 ||
10229 IS_SP_ELEMENT(Feld[jx][jy]))
10230 player->LevelSolved = player->GameOver = TRUE;
10234 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10235 /* this breaks one level: "machine", level 000 */
10237 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10240 int move_direction = player->MovDir;
10242 int enter_side = MV_DIR_OPPOSITE(move_direction);
10243 int leave_side = move_direction;
10245 static int trigger_sides[4][2] =
10247 /* enter side leave side */
10248 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10249 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10250 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10251 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10253 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10254 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10256 int old_jx = last_jx;
10257 int old_jy = last_jy;
10258 int old_element = Feld[old_jx][old_jy];
10259 int new_element = Feld[jx][jy];
10262 /* !!! TEST ONLY !!! */
10263 if (IS_CUSTOM_ELEMENT(old_element))
10264 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10266 player->index_bit, leave_side);
10268 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10269 CE_OTHER_GETS_LEFT,
10270 player->index_bit, leave_side);
10272 if (IS_CUSTOM_ELEMENT(new_element))
10273 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10274 player->index_bit, enter_side);
10276 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10277 CE_OTHER_GETS_ENTERED,
10278 player->index_bit, enter_side);
10284 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10286 TestIfHeroTouchesBadThing(jx, jy);
10287 TestIfPlayerTouchesCustomElement(jx, jy);
10290 /* needed because pushed element has not yet reached its destination,
10291 so it would trigger a change event at its previous field location */
10292 if (!player->is_pushing)
10294 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10297 if (!player->active)
10298 RemoveHero(player);
10301 if (level.use_step_counter)
10311 if (TimeLeft <= 10 && setup.time_limit)
10312 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10314 DrawGameValue_Time(TimeLeft);
10316 if (!TimeLeft && setup.time_limit)
10317 for (i = 0; i < MAX_PLAYERS; i++)
10318 KillHero(&stored_player[i]);
10320 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10321 DrawGameValue_Time(TimePlayed);
10324 if (tape.single_step && tape.recording && !tape.pausing &&
10325 !player->programmed_action)
10326 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10330 void ScrollScreen(struct PlayerInfo *player, int mode)
10332 static unsigned long screen_frame_counter = 0;
10334 if (mode == SCROLL_INIT)
10336 /* set scrolling step size according to actual player's moving speed */
10337 ScrollStepSize = TILEX / player->move_delay_value;
10339 screen_frame_counter = FrameCounter;
10340 ScreenMovDir = player->MovDir;
10341 ScreenMovPos = player->MovPos;
10342 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10345 else if (!FrameReached(&screen_frame_counter, 1))
10350 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10351 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10352 redraw_mask |= REDRAW_FIELD;
10355 ScreenMovDir = MV_NO_MOVING;
10358 void TestIfPlayerTouchesCustomElement(int x, int y)
10360 static int xy[4][2] =
10367 static int trigger_sides[4][2] =
10369 /* center side border side */
10370 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10371 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10372 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10373 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10375 static int touch_dir[4] =
10377 MV_LEFT | MV_RIGHT,
10382 int center_element = Feld[x][y]; /* should always be non-moving! */
10385 for (i = 0; i < NUM_DIRECTIONS; i++)
10387 int xx = x + xy[i][0];
10388 int yy = y + xy[i][1];
10389 int center_side = trigger_sides[i][0];
10390 int border_side = trigger_sides[i][1];
10391 int border_element;
10393 if (!IN_LEV_FIELD(xx, yy))
10396 if (IS_PLAYER(x, y))
10398 struct PlayerInfo *player = PLAYERINFO(x, y);
10400 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10401 border_element = Feld[xx][yy]; /* may be moving! */
10402 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10403 border_element = Feld[xx][yy];
10404 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10405 border_element = MovingOrBlocked2Element(xx, yy);
10407 continue; /* center and border element do not touch */
10410 /* !!! TEST ONLY !!! */
10411 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10412 player->index_bit, border_side);
10413 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10414 CE_OTHER_GETS_TOUCHED,
10415 player->index_bit, border_side);
10417 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10418 CE_OTHER_GETS_TOUCHED,
10419 player->index_bit, border_side);
10420 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10421 player->index_bit, border_side);
10424 else if (IS_PLAYER(xx, yy))
10426 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10428 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10430 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10431 continue; /* center and border element do not touch */
10435 /* !!! TEST ONLY !!! */
10436 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10437 player->index_bit, center_side);
10438 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10439 CE_OTHER_GETS_TOUCHED,
10440 player->index_bit, center_side);
10442 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10443 CE_OTHER_GETS_TOUCHED,
10444 player->index_bit, center_side);
10445 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10446 player->index_bit, center_side);
10454 void TestIfElementTouchesCustomElement(int x, int y)
10456 static int xy[4][2] =
10463 static int trigger_sides[4][2] =
10465 /* center side border side */
10466 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10467 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10468 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10469 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10471 static int touch_dir[4] =
10473 MV_LEFT | MV_RIGHT,
10478 boolean change_center_element = FALSE;
10479 int center_element_change_page = 0;
10480 int center_element = Feld[x][y]; /* should always be non-moving! */
10481 int border_trigger_element = EL_UNDEFINED;
10484 for (i = 0; i < NUM_DIRECTIONS; i++)
10486 int xx = x + xy[i][0];
10487 int yy = y + xy[i][1];
10488 int center_side = trigger_sides[i][0];
10489 int border_side = trigger_sides[i][1];
10490 int border_element;
10492 if (!IN_LEV_FIELD(xx, yy))
10495 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10496 border_element = Feld[xx][yy]; /* may be moving! */
10497 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10498 border_element = Feld[xx][yy];
10499 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10500 border_element = MovingOrBlocked2Element(xx, yy);
10502 continue; /* center and border element do not touch */
10504 /* check for change of center element (but change it only once) */
10505 if (IS_CUSTOM_ELEMENT(center_element) &&
10506 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10507 !change_center_element)
10509 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10511 struct ElementChangeInfo *change =
10512 &element_info[center_element].change_page[j];
10514 if (change->can_change &&
10515 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10516 change->trigger_side & border_side &&
10518 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10520 change->trigger_element == border_element
10524 change_center_element = TRUE;
10525 center_element_change_page = j;
10526 border_trigger_element = border_element;
10533 /* check for change of border element */
10534 if (IS_CUSTOM_ELEMENT(border_element) &&
10535 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10537 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10539 struct ElementChangeInfo *change =
10540 &element_info[border_element].change_page[j];
10542 if (change->can_change &&
10543 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10544 change->trigger_side & center_side &&
10546 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10548 change->trigger_element == center_element
10553 printf("::: border_element %d, %d\n", x, y);
10556 CheckElementChangeByPage(xx, yy, border_element, center_element,
10557 CE_OTHER_IS_TOUCHING, j);
10564 if (change_center_element)
10567 printf("::: center_element %d, %d\n", x, y);
10570 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10571 CE_OTHER_IS_TOUCHING, center_element_change_page);
10575 void TestIfElementHitsCustomElement(int x, int y, int direction)
10577 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10578 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10579 int hitx = x + dx, hity = y + dy;
10580 int hitting_element = Feld[x][y];
10581 int touched_element;
10583 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10584 !IS_FREE(hitx, hity) &&
10585 (!IS_MOVING(hitx, hity) ||
10586 MovDir[hitx][hity] != direction ||
10587 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10590 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10594 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10598 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10599 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10601 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10602 CE_HITTING_SOMETHING, direction);
10604 if (IN_LEV_FIELD(hitx, hity))
10606 int opposite_direction = MV_DIR_OPPOSITE(direction);
10607 int hitting_side = direction;
10608 int touched_side = opposite_direction;
10610 int touched_element = MovingOrBlocked2Element(hitx, hity);
10613 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10614 MovDir[hitx][hity] != direction ||
10615 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10624 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10625 CE_HIT_BY_SOMETHING, opposite_direction);
10627 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10628 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10630 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10632 struct ElementChangeInfo *change =
10633 &element_info[hitting_element].change_page[i];
10635 if (change->can_change &&
10636 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10637 change->trigger_side & touched_side &&
10640 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10642 change->trigger_element == touched_element
10646 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10647 CE_OTHER_IS_HITTING, i);
10653 if (IS_CUSTOM_ELEMENT(touched_element) &&
10654 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10656 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10658 struct ElementChangeInfo *change =
10659 &element_info[touched_element].change_page[i];
10661 if (change->can_change &&
10662 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10663 change->trigger_side & hitting_side &&
10665 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10667 change->trigger_element == hitting_element
10671 CheckElementChangeByPage(hitx, hity, touched_element,
10672 hitting_element, CE_OTHER_GETS_HIT, i);
10682 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10684 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10685 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10686 int hitx = x + dx, hity = y + dy;
10687 int hitting_element = Feld[x][y];
10688 int touched_element;
10690 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10691 !IS_FREE(hitx, hity) &&
10692 (!IS_MOVING(hitx, hity) ||
10693 MovDir[hitx][hity] != direction ||
10694 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10697 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10701 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10705 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10706 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10708 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10709 EP_CAN_SMASH_EVERYTHING, direction);
10711 if (IN_LEV_FIELD(hitx, hity))
10713 int opposite_direction = MV_DIR_OPPOSITE(direction);
10714 int hitting_side = direction;
10715 int touched_side = opposite_direction;
10717 int touched_element = MovingOrBlocked2Element(hitx, hity);
10720 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10721 MovDir[hitx][hity] != direction ||
10722 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10731 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10732 CE_SMASHED_BY_SOMETHING, opposite_direction);
10734 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10735 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10737 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10739 struct ElementChangeInfo *change =
10740 &element_info[hitting_element].change_page[i];
10742 if (change->can_change &&
10743 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10744 change->trigger_side & touched_side &&
10747 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10749 change->trigger_element == touched_element
10753 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10754 CE_OTHER_IS_SMASHING, i);
10760 if (IS_CUSTOM_ELEMENT(touched_element) &&
10761 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10763 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10765 struct ElementChangeInfo *change =
10766 &element_info[touched_element].change_page[i];
10768 if (change->can_change &&
10769 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10770 change->trigger_side & hitting_side &&
10772 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10774 change->trigger_element == hitting_element
10778 CheckElementChangeByPage(hitx, hity, touched_element,
10779 hitting_element, CE_OTHER_GETS_SMASHED,i);
10789 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10791 int i, kill_x = -1, kill_y = -1;
10792 int bad_element = -1;
10793 static int test_xy[4][2] =
10800 static int test_dir[4] =
10808 for (i = 0; i < NUM_DIRECTIONS; i++)
10810 int test_x, test_y, test_move_dir, test_element;
10812 test_x = good_x + test_xy[i][0];
10813 test_y = good_y + test_xy[i][1];
10815 if (!IN_LEV_FIELD(test_x, test_y))
10819 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10822 test_element = Feld[test_x][test_y];
10824 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10827 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10828 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10830 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10831 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10835 bad_element = test_element;
10841 if (kill_x != -1 || kill_y != -1)
10843 if (IS_PLAYER(good_x, good_y))
10845 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10848 if (player->shield_deadly_time_left > 0 &&
10849 !IS_INDESTRUCTIBLE(bad_element))
10850 Bang(kill_x, kill_y);
10851 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10854 if (player->shield_deadly_time_left > 0)
10855 Bang(kill_x, kill_y);
10856 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10861 Bang(good_x, good_y);
10865 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10867 int i, kill_x = -1, kill_y = -1;
10868 int bad_element = Feld[bad_x][bad_y];
10869 static int test_xy[4][2] =
10876 static int touch_dir[4] =
10878 MV_LEFT | MV_RIGHT,
10883 static int test_dir[4] =
10891 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10894 for (i = 0; i < NUM_DIRECTIONS; i++)
10896 int test_x, test_y, test_move_dir, test_element;
10898 test_x = bad_x + test_xy[i][0];
10899 test_y = bad_y + test_xy[i][1];
10900 if (!IN_LEV_FIELD(test_x, test_y))
10904 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10906 test_element = Feld[test_x][test_y];
10908 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10909 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10911 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10912 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10914 /* good thing is player or penguin that does not move away */
10915 if (IS_PLAYER(test_x, test_y))
10917 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10919 if (bad_element == EL_ROBOT && player->is_moving)
10920 continue; /* robot does not kill player if he is moving */
10922 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10924 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10925 continue; /* center and border element do not touch */
10932 else if (test_element == EL_PENGUIN)
10941 if (kill_x != -1 || kill_y != -1)
10943 if (IS_PLAYER(kill_x, kill_y))
10945 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10948 if (player->shield_deadly_time_left > 0 &&
10949 !IS_INDESTRUCTIBLE(bad_element))
10950 Bang(bad_x, bad_y);
10951 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10954 if (player->shield_deadly_time_left > 0)
10955 Bang(bad_x, bad_y);
10956 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10961 Bang(kill_x, kill_y);
10965 void TestIfHeroTouchesBadThing(int x, int y)
10967 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10970 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10972 TestIfGoodThingHitsBadThing(x, y, move_dir);
10975 void TestIfBadThingTouchesHero(int x, int y)
10977 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10980 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10982 TestIfBadThingHitsGoodThing(x, y, move_dir);
10985 void TestIfFriendTouchesBadThing(int x, int y)
10987 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10990 void TestIfBadThingTouchesFriend(int x, int y)
10992 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10995 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10997 int i, kill_x = bad_x, kill_y = bad_y;
10998 static int xy[4][2] =
11006 for (i = 0; i < NUM_DIRECTIONS; i++)
11010 x = bad_x + xy[i][0];
11011 y = bad_y + xy[i][1];
11012 if (!IN_LEV_FIELD(x, y))
11015 element = Feld[x][y];
11016 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11017 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11025 if (kill_x != bad_x || kill_y != bad_y)
11026 Bang(bad_x, bad_y);
11029 void KillHero(struct PlayerInfo *player)
11031 int jx = player->jx, jy = player->jy;
11033 if (!player->active)
11036 /* remove accessible field at the player's position */
11037 Feld[jx][jy] = EL_EMPTY;
11039 /* deactivate shield (else Bang()/Explode() would not work right) */
11040 player->shield_normal_time_left = 0;
11041 player->shield_deadly_time_left = 0;
11047 static void KillHeroUnlessEnemyProtected(int x, int y)
11049 if (!PLAYER_ENEMY_PROTECTED(x, y))
11050 KillHero(PLAYERINFO(x, y));
11053 static void KillHeroUnlessExplosionProtected(int x, int y)
11055 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11056 KillHero(PLAYERINFO(x, y));
11059 void BuryHero(struct PlayerInfo *player)
11061 int jx = player->jx, jy = player->jy;
11063 if (!player->active)
11067 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11069 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11071 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11073 player->GameOver = TRUE;
11074 RemoveHero(player);
11077 void RemoveHero(struct PlayerInfo *player)
11079 int jx = player->jx, jy = player->jy;
11080 int i, found = FALSE;
11082 player->present = FALSE;
11083 player->active = FALSE;
11085 if (!ExplodeField[jx][jy])
11086 StorePlayer[jx][jy] = 0;
11088 for (i = 0; i < MAX_PLAYERS; i++)
11089 if (stored_player[i].active)
11093 AllPlayersGone = TRUE;
11100 =============================================================================
11101 checkDiagonalPushing()
11102 -----------------------------------------------------------------------------
11103 check if diagonal input device direction results in pushing of object
11104 (by checking if the alternative direction is walkable, diggable, ...)
11105 =============================================================================
11108 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11109 int x, int y, int real_dx, int real_dy)
11111 int jx, jy, dx, dy, xx, yy;
11113 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11116 /* diagonal direction: check alternative direction */
11121 xx = jx + (dx == 0 ? real_dx : 0);
11122 yy = jy + (dy == 0 ? real_dy : 0);
11124 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11128 =============================================================================
11130 -----------------------------------------------------------------------------
11131 x, y: field next to player (non-diagonal) to try to dig to
11132 real_dx, real_dy: direction as read from input device (can be diagonal)
11133 =============================================================================
11136 int DigField(struct PlayerInfo *player,
11137 int oldx, int oldy, int x, int y,
11138 int real_dx, int real_dy, int mode)
11141 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11143 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11144 boolean player_was_pushing = player->is_pushing;
11145 int jx = oldx, jy = oldy;
11146 int dx = x - jx, dy = y - jy;
11147 int nextx = x + dx, nexty = y + dy;
11148 int move_direction = (dx == -1 ? MV_LEFT :
11149 dx == +1 ? MV_RIGHT :
11151 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11152 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11154 int dig_side = MV_DIR_OPPOSITE(move_direction);
11156 static int trigger_sides[4] =
11158 CH_SIDE_RIGHT, /* moving left */
11159 CH_SIDE_LEFT, /* moving right */
11160 CH_SIDE_BOTTOM, /* moving up */
11161 CH_SIDE_TOP, /* moving down */
11163 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11165 int old_element = Feld[jx][jy];
11168 if (is_player) /* function can also be called by EL_PENGUIN */
11170 if (player->MovPos == 0)
11172 player->is_digging = FALSE;
11173 player->is_collecting = FALSE;
11176 if (player->MovPos == 0) /* last pushing move finished */
11177 player->is_pushing = FALSE;
11179 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11181 player->is_switching = FALSE;
11182 #if USE_NEW_PUSH_DELAY
11183 player->push_delay = -1;
11185 player->push_delay = 0;
11188 return MF_NO_ACTION;
11192 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11193 return MF_NO_ACTION;
11198 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11200 if (IS_TUBE(Feld[jx][jy]) ||
11201 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11205 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11206 int tube_leave_directions[][2] =
11208 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11209 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11210 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11211 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11212 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11213 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11214 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11215 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11216 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11217 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11218 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11219 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11222 while (tube_leave_directions[i][0] != tube_element)
11225 if (tube_leave_directions[i][0] == -1) /* should not happen */
11229 if (!(tube_leave_directions[i][1] & move_direction))
11230 return MF_NO_ACTION; /* tube has no opening in this direction */
11235 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11236 old_element = Back[jx][jy];
11240 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11241 return MF_NO_ACTION; /* field has no opening in this direction */
11243 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11244 return MF_NO_ACTION; /* field has no opening in this direction */
11246 element = Feld[x][y];
11248 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11249 return MF_NO_ACTION;
11251 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11252 game.engine_version >= VERSION_IDENT(2,2,0,0))
11253 return MF_NO_ACTION;
11256 if (game.gravity && is_player && !player->is_auto_moving &&
11257 canFallDown(player) && move_direction != MV_DOWN &&
11258 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11259 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11263 if (element == EL_EMPTY_SPACE &&
11264 game.gravity && !player->is_auto_moving &&
11265 canFallDown(player) && move_direction != MV_DOWN)
11266 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11272 case EL_SP_PORT_LEFT:
11273 case EL_SP_PORT_RIGHT:
11274 case EL_SP_PORT_UP:
11275 case EL_SP_PORT_DOWN:
11276 case EL_SP_PORT_HORIZONTAL:
11277 case EL_SP_PORT_VERTICAL:
11278 case EL_SP_PORT_ANY:
11279 case EL_SP_GRAVITY_PORT_LEFT:
11280 case EL_SP_GRAVITY_PORT_RIGHT:
11281 case EL_SP_GRAVITY_PORT_UP:
11282 case EL_SP_GRAVITY_PORT_DOWN:
11284 if (!canEnterSupaplexPort(x, y, dx, dy))
11285 return MF_NO_ACTION;
11288 element != EL_SP_PORT_LEFT &&
11289 element != EL_SP_GRAVITY_PORT_LEFT &&
11290 element != EL_SP_PORT_HORIZONTAL &&
11291 element != EL_SP_PORT_ANY) ||
11293 element != EL_SP_PORT_RIGHT &&
11294 element != EL_SP_GRAVITY_PORT_RIGHT &&
11295 element != EL_SP_PORT_HORIZONTAL &&
11296 element != EL_SP_PORT_ANY) ||
11298 element != EL_SP_PORT_UP &&
11299 element != EL_SP_GRAVITY_PORT_UP &&
11300 element != EL_SP_PORT_VERTICAL &&
11301 element != EL_SP_PORT_ANY) ||
11303 element != EL_SP_PORT_DOWN &&
11304 element != EL_SP_GRAVITY_PORT_DOWN &&
11305 element != EL_SP_PORT_VERTICAL &&
11306 element != EL_SP_PORT_ANY) ||
11307 !IN_LEV_FIELD(nextx, nexty) ||
11308 !IS_FREE(nextx, nexty))
11309 return MF_NO_ACTION;
11312 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11313 element == EL_SP_GRAVITY_PORT_RIGHT ||
11314 element == EL_SP_GRAVITY_PORT_UP ||
11315 element == EL_SP_GRAVITY_PORT_DOWN)
11316 game.gravity = !game.gravity;
11318 /* automatically move to the next field with double speed */
11319 player->programmed_action = move_direction;
11321 if (player->move_delay_reset_counter == 0)
11323 player->move_delay_reset_counter = 2; /* two double speed steps */
11325 DOUBLE_PLAYER_SPEED(player);
11328 player->move_delay_reset_counter = 2;
11330 DOUBLE_PLAYER_SPEED(player);
11334 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11337 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11343 case EL_TUBE_VERTICAL:
11344 case EL_TUBE_HORIZONTAL:
11345 case EL_TUBE_VERTICAL_LEFT:
11346 case EL_TUBE_VERTICAL_RIGHT:
11347 case EL_TUBE_HORIZONTAL_UP:
11348 case EL_TUBE_HORIZONTAL_DOWN:
11349 case EL_TUBE_LEFT_UP:
11350 case EL_TUBE_LEFT_DOWN:
11351 case EL_TUBE_RIGHT_UP:
11352 case EL_TUBE_RIGHT_DOWN:
11355 int tube_enter_directions[][2] =
11357 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11358 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11359 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11360 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11361 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11362 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11363 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11364 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11365 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11366 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11367 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11368 { -1, MV_NO_MOVING }
11371 while (tube_enter_directions[i][0] != element)
11374 if (tube_enter_directions[i][0] == -1) /* should not happen */
11378 if (!(tube_enter_directions[i][1] & move_direction))
11379 return MF_NO_ACTION; /* tube has no opening in this direction */
11381 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11389 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11391 if (IS_WALKABLE(element))
11394 int sound_element = SND_ELEMENT(element);
11395 int sound_action = ACTION_WALKING;
11398 if (!ACCESS_FROM(element, opposite_direction))
11399 return MF_NO_ACTION; /* field not accessible from this direction */
11403 if (element == EL_EMPTY_SPACE &&
11404 game.gravity && !player->is_auto_moving &&
11405 canFallDown(player) && move_direction != MV_DOWN)
11406 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11409 if (IS_RND_GATE(element))
11411 if (!player->key[RND_GATE_NR(element)])
11412 return MF_NO_ACTION;
11414 else if (IS_RND_GATE_GRAY(element))
11416 if (!player->key[RND_GATE_GRAY_NR(element)])
11417 return MF_NO_ACTION;
11419 else if (element == EL_EXIT_OPEN ||
11420 element == EL_SP_EXIT_OPEN ||
11421 element == EL_SP_EXIT_OPENING)
11423 sound_action = ACTION_PASSING; /* player is passing exit */
11425 else if (element == EL_EMPTY)
11427 sound_action = ACTION_MOVING; /* nothing to walk on */
11430 /* play sound from background or player, whatever is available */
11431 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11432 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11434 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11439 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11441 else if (IS_PASSABLE(element))
11445 if (!canPassField(x, y, move_direction))
11446 return MF_NO_ACTION;
11451 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11452 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11453 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11454 return MF_NO_ACTION;
11456 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11457 return MF_NO_ACTION;
11462 if (!ACCESS_FROM(element, opposite_direction))
11463 return MF_NO_ACTION; /* field not accessible from this direction */
11465 if (IS_CUSTOM_ELEMENT(element) &&
11466 !ACCESS_FROM(element, opposite_direction))
11467 return MF_NO_ACTION; /* field not accessible from this direction */
11471 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11472 return MF_NO_ACTION;
11477 if (IS_EM_GATE(element))
11479 if (!player->key[EM_GATE_NR(element)])
11480 return MF_NO_ACTION;
11482 else if (IS_EM_GATE_GRAY(element))
11484 if (!player->key[EM_GATE_GRAY_NR(element)])
11485 return MF_NO_ACTION;
11487 else if (IS_SP_PORT(element))
11489 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11490 element == EL_SP_GRAVITY_PORT_RIGHT ||
11491 element == EL_SP_GRAVITY_PORT_UP ||
11492 element == EL_SP_GRAVITY_PORT_DOWN)
11493 game.gravity = !game.gravity;
11494 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11495 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11496 element == EL_SP_GRAVITY_ON_PORT_UP ||
11497 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11498 game.gravity = TRUE;
11499 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11500 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11501 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11502 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11503 game.gravity = FALSE;
11506 /* automatically move to the next field with double speed */
11507 player->programmed_action = move_direction;
11509 if (player->move_delay_reset_counter == 0)
11511 player->move_delay_reset_counter = 2; /* two double speed steps */
11513 DOUBLE_PLAYER_SPEED(player);
11516 player->move_delay_reset_counter = 2;
11518 DOUBLE_PLAYER_SPEED(player);
11521 PlayLevelSoundAction(x, y, ACTION_PASSING);
11525 else if (IS_DIGGABLE(element))
11529 if (mode != DF_SNAP)
11532 GfxElement[x][y] = GFX_ELEMENT(element);
11535 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11537 player->is_digging = TRUE;
11540 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11542 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11543 player->index_bit, dig_side);
11546 if (mode == DF_SNAP)
11547 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11552 else if (IS_COLLECTIBLE(element))
11556 if (is_player && mode != DF_SNAP)
11558 GfxElement[x][y] = element;
11559 player->is_collecting = TRUE;
11562 if (element == EL_SPEED_PILL)
11563 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11564 else if (element == EL_EXTRA_TIME && level.time > 0)
11567 DrawGameValue_Time(TimeLeft);
11569 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11571 player->shield_normal_time_left += 10;
11572 if (element == EL_SHIELD_DEADLY)
11573 player->shield_deadly_time_left += 10;
11575 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11577 if (player->inventory_size < MAX_INVENTORY_SIZE)
11578 player->inventory_element[player->inventory_size++] = element;
11580 DrawGameValue_Dynamite(local_player->inventory_size);
11582 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11584 player->dynabomb_count++;
11585 player->dynabombs_left++;
11587 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11589 player->dynabomb_size++;
11591 else if (element == EL_DYNABOMB_INCREASE_POWER)
11593 player->dynabomb_xl = TRUE;
11595 else if (IS_KEY(element))
11597 player->key[KEY_NR(element)] = TRUE;
11599 DrawGameValue_Keys(player->key);
11601 redraw_mask |= REDRAW_DOOR_1;
11603 else if (IS_ENVELOPE(element))
11606 player->show_envelope = element;
11608 ShowEnvelope(element - EL_ENVELOPE_1);
11611 else if (IS_DROPPABLE(element) ||
11612 IS_THROWABLE(element)) /* can be collected and dropped */
11616 if (element_info[element].collect_count == 0)
11617 player->inventory_infinite_element = element;
11619 for (i = 0; i < element_info[element].collect_count; i++)
11620 if (player->inventory_size < MAX_INVENTORY_SIZE)
11621 player->inventory_element[player->inventory_size++] = element;
11623 DrawGameValue_Dynamite(local_player->inventory_size);
11625 else if (element_info[element].collect_count > 0)
11627 local_player->gems_still_needed -=
11628 element_info[element].collect_count;
11629 if (local_player->gems_still_needed < 0)
11630 local_player->gems_still_needed = 0;
11632 DrawGameValue_Emeralds(local_player->gems_still_needed);
11635 RaiseScoreElement(element);
11636 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11639 CheckTriggeredElementChangeByPlayer(x, y, element,
11640 CE_OTHER_GETS_COLLECTED,
11641 player->index_bit, dig_side);
11644 if (mode == DF_SNAP)
11645 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11650 else if (IS_PUSHABLE(element))
11652 if (mode == DF_SNAP && element != EL_BD_ROCK)
11653 return MF_NO_ACTION;
11655 if (CAN_FALL(element) && dy)
11656 return MF_NO_ACTION;
11658 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11659 !(element == EL_SPRING && level.use_spring_bug))
11660 return MF_NO_ACTION;
11663 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11664 ((move_direction & MV_VERTICAL &&
11665 ((element_info[element].move_pattern & MV_LEFT &&
11666 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11667 (element_info[element].move_pattern & MV_RIGHT &&
11668 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11669 (move_direction & MV_HORIZONTAL &&
11670 ((element_info[element].move_pattern & MV_UP &&
11671 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11672 (element_info[element].move_pattern & MV_DOWN &&
11673 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11674 return MF_NO_ACTION;
11678 /* do not push elements already moving away faster than player */
11679 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11680 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11681 return MF_NO_ACTION;
11683 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11684 return MF_NO_ACTION;
11690 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11692 if (player->push_delay_value == -1 || !player_was_pushing)
11693 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11695 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11697 if (player->push_delay_value == -1)
11698 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11701 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11703 if (player->push_delay_value == -1 || !player_was_pushing)
11704 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11707 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11709 if (!player->is_pushing)
11710 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11714 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11715 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11716 !player_is_pushing))
11717 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11720 if (!player->is_pushing &&
11721 game.engine_version >= VERSION_IDENT(2,2,0,7))
11722 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11726 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11727 player->push_delay, player->push_delay_value,
11728 FrameCounter, game.engine_version,
11729 player_was_pushing, player->is_pushing,
11730 element, element_info[element].token_name,
11731 GET_NEW_PUSH_DELAY(element));
11734 player->is_pushing = TRUE;
11736 if (!(IN_LEV_FIELD(nextx, nexty) &&
11737 (IS_FREE(nextx, nexty) ||
11738 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11739 IS_SB_ELEMENT(element)))))
11740 return MF_NO_ACTION;
11742 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11743 return MF_NO_ACTION;
11745 #if USE_NEW_PUSH_DELAY
11748 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11749 printf("::: ALERT: %d, %d [%d / %d]\n",
11750 player->push_delay, player->push_delay2,
11751 FrameCounter, FrameCounter / 50);
11754 if (player->push_delay == -1) /* new pushing; restart delay */
11755 player->push_delay = 0;
11757 if (player->push_delay == 0) /* new pushing; restart delay */
11758 player->push_delay = FrameCounter;
11761 #if USE_NEW_PUSH_DELAY
11763 if ( (player->push_delay > 0) != (!xxx_fr) )
11764 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11765 player->push_delay,
11766 xxx_pdv2, player->push_delay2, player->push_delay_value,
11767 FrameCounter, FrameCounter / 50);
11771 if (player->push_delay > 0 &&
11772 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11773 element != EL_SPRING && element != EL_BALLOON)
11776 if (player->push_delay < player->push_delay_value &&
11777 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11778 element != EL_SPRING && element != EL_BALLOON)
11782 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11783 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11784 element != EL_SPRING && element != EL_BALLOON)
11787 /* make sure that there is no move delay before next try to push */
11788 #if USE_NEW_MOVE_DELAY
11789 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11790 player->move_delay = 0;
11792 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11793 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11796 return MF_NO_ACTION;
11800 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11803 if (IS_SB_ELEMENT(element))
11805 if (element == EL_SOKOBAN_FIELD_FULL)
11807 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11808 local_player->sokobanfields_still_needed++;
11811 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11813 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11814 local_player->sokobanfields_still_needed--;
11817 Feld[x][y] = EL_SOKOBAN_OBJECT;
11819 if (Back[x][y] == Back[nextx][nexty])
11820 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11821 else if (Back[x][y] != 0)
11822 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11825 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11828 if (local_player->sokobanfields_still_needed == 0 &&
11829 game.emulation == EMU_SOKOBAN)
11831 player->LevelSolved = player->GameOver = TRUE;
11832 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11836 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11838 InitMovingField(x, y, move_direction);
11839 GfxAction[x][y] = ACTION_PUSHING;
11841 if (mode == DF_SNAP)
11842 ContinueMoving(x, y);
11844 MovPos[x][y] = (dx != 0 ? dx : dy);
11846 Pushed[x][y] = TRUE;
11847 Pushed[nextx][nexty] = TRUE;
11849 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11850 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11852 player->push_delay_value = -1; /* get new value later */
11854 #if USE_PUSH_BUGFIX
11855 /* now: check for element change _after_ element has been pushed! */
11857 if (game.use_bug_change_when_pushing)
11859 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11862 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11863 player->index_bit, dig_side);
11864 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11865 player->index_bit, dig_side);
11871 /* check for element change _after_ element has been pushed! */
11875 /* !!! TEST ONLY !!! */
11876 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11877 player->index_bit, dig_side);
11878 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11879 player->index_bit, dig_side);
11881 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11882 player->index_bit, dig_side);
11883 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11884 player->index_bit, dig_side);
11892 else if (IS_SWITCHABLE(element))
11894 if (PLAYER_SWITCHING(player, x, y))
11896 CheckTriggeredElementChangeByPlayer(x,y, element,
11897 CE_OTHER_GETS_PRESSED,
11898 player->index_bit, dig_side);
11903 player->is_switching = TRUE;
11904 player->switch_x = x;
11905 player->switch_y = y;
11907 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11909 if (element == EL_ROBOT_WHEEL)
11911 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11915 DrawLevelField(x, y);
11917 else if (element == EL_SP_TERMINAL)
11921 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11923 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11925 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11926 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11929 else if (IS_BELT_SWITCH(element))
11931 ToggleBeltSwitch(x, y);
11933 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11934 element == EL_SWITCHGATE_SWITCH_DOWN)
11936 ToggleSwitchgateSwitch(x, y);
11938 else if (element == EL_LIGHT_SWITCH ||
11939 element == EL_LIGHT_SWITCH_ACTIVE)
11941 ToggleLightSwitch(x, y);
11944 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11945 SND_LIGHT_SWITCH_ACTIVATING :
11946 SND_LIGHT_SWITCH_DEACTIVATING);
11949 else if (element == EL_TIMEGATE_SWITCH)
11951 ActivateTimegateSwitch(x, y);
11953 else if (element == EL_BALLOON_SWITCH_LEFT ||
11954 element == EL_BALLOON_SWITCH_RIGHT ||
11955 element == EL_BALLOON_SWITCH_UP ||
11956 element == EL_BALLOON_SWITCH_DOWN ||
11957 element == EL_BALLOON_SWITCH_ANY)
11959 if (element == EL_BALLOON_SWITCH_ANY)
11960 game.balloon_dir = move_direction;
11962 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11963 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11964 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11965 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11968 else if (element == EL_LAMP)
11970 Feld[x][y] = EL_LAMP_ACTIVE;
11971 local_player->lights_still_needed--;
11973 DrawLevelField(x, y);
11975 else if (element == EL_TIME_ORB_FULL)
11977 Feld[x][y] = EL_TIME_ORB_EMPTY;
11979 DrawGameValue_Time(TimeLeft);
11981 DrawLevelField(x, y);
11984 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11988 CheckTriggeredElementChangeByPlayer(x, y, element,
11989 CE_OTHER_IS_SWITCHING,
11990 player->index_bit, dig_side);
11992 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11993 player->index_bit, dig_side);
11999 if (!PLAYER_SWITCHING(player, x, y))
12001 player->is_switching = TRUE;
12002 player->switch_x = x;
12003 player->switch_y = y;
12006 /* !!! TEST ONLY !!! */
12007 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12008 player->index_bit, dig_side);
12009 CheckTriggeredElementChangeByPlayer(x, y, element,
12010 CE_OTHER_IS_SWITCHING,
12011 player->index_bit, dig_side);
12013 CheckTriggeredElementChangeByPlayer(x, y, element,
12014 CE_OTHER_IS_SWITCHING,
12015 player->index_bit, dig_side);
12016 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12017 player->index_bit, dig_side);
12022 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12023 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12024 player->index_bit, dig_side);
12025 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12026 player->index_bit, dig_side);
12028 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12029 player->index_bit, dig_side);
12030 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12031 player->index_bit, dig_side);
12035 return MF_NO_ACTION;
12038 #if USE_NEW_PUSH_DELAY
12039 player->push_delay = -1;
12041 player->push_delay = 0;
12044 if (Feld[x][y] != element) /* really digged/collected something */
12045 player->is_collecting = !player->is_digging;
12050 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12052 int jx = player->jx, jy = player->jy;
12053 int x = jx + dx, y = jy + dy;
12054 int snap_direction = (dx == -1 ? MV_LEFT :
12055 dx == +1 ? MV_RIGHT :
12057 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12060 if (player->MovPos != 0)
12063 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12067 if (!player->active || !IN_LEV_FIELD(x, y))
12075 if (player->MovPos == 0)
12076 player->is_pushing = FALSE;
12078 player->is_snapping = FALSE;
12080 if (player->MovPos == 0)
12082 player->is_moving = FALSE;
12083 player->is_digging = FALSE;
12084 player->is_collecting = FALSE;
12090 if (player->is_snapping)
12093 player->MovDir = snap_direction;
12096 if (player->MovPos == 0)
12099 player->is_moving = FALSE;
12100 player->is_digging = FALSE;
12101 player->is_collecting = FALSE;
12104 player->is_dropping = FALSE;
12106 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12109 player->is_snapping = TRUE;
12112 if (player->MovPos == 0)
12115 player->is_moving = FALSE;
12116 player->is_digging = FALSE;
12117 player->is_collecting = FALSE;
12121 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12122 DrawLevelField(player->last_jx, player->last_jy);
12125 DrawLevelField(x, y);
12134 boolean DropElement(struct PlayerInfo *player)
12136 int old_element, new_element;
12137 int dropx = player->jx, dropy = player->jy;
12138 int drop_direction = player->MovDir;
12140 int drop_side = drop_direction;
12142 static int trigger_sides[4] =
12144 CH_SIDE_LEFT, /* dropping left */
12145 CH_SIDE_RIGHT, /* dropping right */
12146 CH_SIDE_TOP, /* dropping up */
12147 CH_SIDE_BOTTOM, /* dropping down */
12149 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12151 int drop_element = (player->inventory_size > 0 ?
12152 player->inventory_element[player->inventory_size - 1] :
12153 player->inventory_infinite_element != EL_UNDEFINED ?
12154 player->inventory_infinite_element :
12155 player->dynabombs_left > 0 ?
12156 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12159 if (IS_THROWABLE(drop_element))
12161 dropx += GET_DX_FROM_DIR(drop_direction);
12162 dropy += GET_DY_FROM_DIR(drop_direction);
12164 if (!IN_LEV_FIELD(dropx, dropy))
12168 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12169 new_element = drop_element; /* default: no change when dropping */
12171 /* check if player is active, not moving and ready to drop */
12172 if (!player->active || player->MovPos || player->drop_delay > 0)
12175 /* check if player has anything that can be dropped */
12177 if (new_element == EL_UNDEFINED)
12180 if (player->inventory_size == 0 &&
12181 player->inventory_infinite_element == EL_UNDEFINED &&
12182 player->dynabombs_left == 0)
12186 /* check if anything can be dropped at the current position */
12187 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12190 /* collected custom elements can only be dropped on empty fields */
12192 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12195 if (player->inventory_size > 0 &&
12196 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12197 && old_element != EL_EMPTY)
12201 if (old_element != EL_EMPTY)
12202 Back[dropx][dropy] = old_element; /* store old element on this field */
12204 ResetGfxAnimation(dropx, dropy);
12205 ResetRandomAnimationValue(dropx, dropy);
12207 if (player->inventory_size > 0 ||
12208 player->inventory_infinite_element != EL_UNDEFINED)
12210 if (player->inventory_size > 0)
12212 player->inventory_size--;
12215 new_element = player->inventory_element[player->inventory_size];
12218 DrawGameValue_Dynamite(local_player->inventory_size);
12220 if (new_element == EL_DYNAMITE)
12221 new_element = EL_DYNAMITE_ACTIVE;
12222 else if (new_element == EL_SP_DISK_RED)
12223 new_element = EL_SP_DISK_RED_ACTIVE;
12226 Feld[dropx][dropy] = new_element;
12228 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12229 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12230 el2img(Feld[dropx][dropy]), 0);
12232 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12235 /* needed if previous element just changed to "empty" in the last frame */
12236 Changed[dropx][dropy] = 0; /* allow another change */
12240 /* !!! TEST ONLY !!! */
12241 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12242 player->index_bit, drop_side);
12243 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12244 CE_OTHER_GETS_DROPPED,
12245 player->index_bit, drop_side);
12247 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12248 CE_OTHER_GETS_DROPPED,
12249 player->index_bit, drop_side);
12250 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12251 player->index_bit, drop_side);
12254 TestIfElementTouchesCustomElement(dropx, dropy);
12256 else /* player is dropping a dyna bomb */
12258 player->dynabombs_left--;
12261 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12264 Feld[dropx][dropy] = new_element;
12266 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12267 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12268 el2img(Feld[dropx][dropy]), 0);
12270 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12277 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12280 InitField_WithBug1(dropx, dropy, FALSE);
12282 InitField(dropx, dropy, FALSE);
12283 if (CAN_MOVE(Feld[dropx][dropy]))
12284 InitMovDir(dropx, dropy);
12288 new_element = Feld[dropx][dropy]; /* element might have changed */
12290 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12291 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12294 int move_stepsize = element_info[new_element].move_stepsize;
12296 int move_direction, nextx, nexty;
12298 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12299 MovDir[dropx][dropy] = drop_direction;
12301 move_direction = MovDir[dropx][dropy];
12302 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12303 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12306 Changed[dropx][dropy] = 0; /* allow another change */
12307 CheckCollision[dropx][dropy] = 2;
12310 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12313 WasJustMoving[dropx][dropy] = 3;
12316 InitMovingField(dropx, dropy, move_direction);
12317 ContinueMoving(dropx, dropy);
12322 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12325 Changed[dropx][dropy] = 0; /* allow another change */
12328 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12330 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12331 CE_HITTING_SOMETHING, move_direction);
12339 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12344 player->drop_delay = 8 + 8 + 8;
12348 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12353 player->is_dropping = TRUE;
12359 /* ------------------------------------------------------------------------- */
12360 /* game sound playing functions */
12361 /* ------------------------------------------------------------------------- */
12363 static int *loop_sound_frame = NULL;
12364 static int *loop_sound_volume = NULL;
12366 void InitPlayLevelSound()
12368 int num_sounds = getSoundListSize();
12370 checked_free(loop_sound_frame);
12371 checked_free(loop_sound_volume);
12373 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12374 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12377 static void PlayLevelSound(int x, int y, int nr)
12379 int sx = SCREENX(x), sy = SCREENY(y);
12380 int volume, stereo_position;
12381 int max_distance = 8;
12382 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12384 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12385 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12388 if (!IN_LEV_FIELD(x, y) ||
12389 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12390 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12393 volume = SOUND_MAX_VOLUME;
12395 if (!IN_SCR_FIELD(sx, sy))
12397 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12398 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12400 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12403 stereo_position = (SOUND_MAX_LEFT +
12404 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12405 (SCR_FIELDX + 2 * max_distance));
12407 if (IS_LOOP_SOUND(nr))
12409 /* This assures that quieter loop sounds do not overwrite louder ones,
12410 while restarting sound volume comparison with each new game frame. */
12412 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12415 loop_sound_volume[nr] = volume;
12416 loop_sound_frame[nr] = FrameCounter;
12419 PlaySoundExt(nr, volume, stereo_position, type);
12422 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12424 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12425 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12426 y < LEVELY(BY1) ? LEVELY(BY1) :
12427 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12431 static void PlayLevelSoundAction(int x, int y, int action)
12433 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12436 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12438 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12440 if (sound_effect != SND_UNDEFINED)
12441 PlayLevelSound(x, y, sound_effect);
12444 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12447 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12449 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12450 PlayLevelSound(x, y, sound_effect);
12453 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12455 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12457 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12458 PlayLevelSound(x, y, sound_effect);
12461 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12463 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12465 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12466 StopSound(sound_effect);
12469 static void PlayLevelMusic()
12471 if (levelset.music[level_nr] != MUS_UNDEFINED)
12472 PlayMusic(levelset.music[level_nr]); /* from config file */
12474 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12477 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12479 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12482 if (sample == SAMPLE_bug)
12483 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12489 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12493 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12497 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12501 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12505 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12509 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12513 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12516 case SAMPLE_android_clone:
12517 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12520 case SAMPLE_android_move:
12521 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12524 case SAMPLE_spring:
12525 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12529 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12533 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12536 case SAMPLE_eater_eat:
12537 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12541 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12544 case SAMPLE_collect:
12545 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12548 case SAMPLE_diamond:
12549 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12552 case SAMPLE_squash:
12553 /* !!! CHECK THIS !!! */
12555 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12557 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12561 case SAMPLE_wonderfall:
12562 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12566 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12570 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12574 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12578 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12582 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12586 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12589 case SAMPLE_wonder:
12590 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12594 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12597 case SAMPLE_exit_open:
12598 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12601 case SAMPLE_exit_leave:
12602 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12605 case SAMPLE_dynamite:
12606 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12610 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12614 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12618 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12622 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12626 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12630 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12634 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12639 void RaiseScore(int value)
12641 local_player->score += value;
12643 DrawGameValue_Score(local_player->score);
12646 void RaiseScoreElement(int element)
12651 case EL_BD_DIAMOND:
12652 case EL_EMERALD_YELLOW:
12653 case EL_EMERALD_RED:
12654 case EL_EMERALD_PURPLE:
12655 case EL_SP_INFOTRON:
12656 RaiseScore(level.score[SC_EMERALD]);
12659 RaiseScore(level.score[SC_DIAMOND]);
12662 RaiseScore(level.score[SC_CRYSTAL]);
12665 RaiseScore(level.score[SC_PEARL]);
12668 case EL_BD_BUTTERFLY:
12669 case EL_SP_ELECTRON:
12670 RaiseScore(level.score[SC_BUG]);
12673 case EL_BD_FIREFLY:
12674 case EL_SP_SNIKSNAK:
12675 RaiseScore(level.score[SC_SPACESHIP]);
12678 case EL_DARK_YAMYAM:
12679 RaiseScore(level.score[SC_YAMYAM]);
12682 RaiseScore(level.score[SC_ROBOT]);
12685 RaiseScore(level.score[SC_PACMAN]);
12688 RaiseScore(level.score[SC_NUT]);
12691 case EL_SP_DISK_RED:
12692 case EL_DYNABOMB_INCREASE_NUMBER:
12693 case EL_DYNABOMB_INCREASE_SIZE:
12694 case EL_DYNABOMB_INCREASE_POWER:
12695 RaiseScore(level.score[SC_DYNAMITE]);
12697 case EL_SHIELD_NORMAL:
12698 case EL_SHIELD_DEADLY:
12699 RaiseScore(level.score[SC_SHIELD]);
12701 case EL_EXTRA_TIME:
12702 RaiseScore(level.score[SC_TIME_BONUS]);
12716 RaiseScore(level.score[SC_KEY]);
12719 RaiseScore(element_info[element].collect_score);
12724 void RequestQuitGame(boolean ask_if_really_quit)
12726 if (AllPlayersGone ||
12727 !ask_if_really_quit ||
12728 level_editor_test_game ||
12729 Request("Do you really want to quit the game ?",
12730 REQ_ASK | REQ_STAY_CLOSED))
12732 #if defined(NETWORK_AVALIABLE)
12733 if (options.network)
12734 SendToServer_StopPlaying();
12738 game_status = GAME_MODE_MAIN;
12746 if (tape.playing && tape.deactivate_display)
12747 TapeDeactivateDisplayOff(TRUE);
12750 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12753 if (tape.playing && tape.deactivate_display)
12754 TapeDeactivateDisplayOn();
12761 /* ---------- new game button stuff ---------------------------------------- */
12763 /* graphic position values for game buttons */
12764 #define GAME_BUTTON_XSIZE 30
12765 #define GAME_BUTTON_YSIZE 30
12766 #define GAME_BUTTON_XPOS 5
12767 #define GAME_BUTTON_YPOS 215
12768 #define SOUND_BUTTON_XPOS 5
12769 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12771 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12772 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12773 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12774 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12775 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12776 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12783 } gamebutton_info[NUM_GAME_BUTTONS] =
12786 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12791 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12792 GAME_CTRL_ID_PAUSE,
12796 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12801 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12802 SOUND_CTRL_ID_MUSIC,
12803 "background music on/off"
12806 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12807 SOUND_CTRL_ID_LOOPS,
12808 "sound loops on/off"
12811 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12812 SOUND_CTRL_ID_SIMPLE,
12813 "normal sounds on/off"
12817 void CreateGameButtons()
12821 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12823 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12824 struct GadgetInfo *gi;
12827 unsigned long event_mask;
12828 int gd_xoffset, gd_yoffset;
12829 int gd_x1, gd_x2, gd_y1, gd_y2;
12832 gd_xoffset = gamebutton_info[i].x;
12833 gd_yoffset = gamebutton_info[i].y;
12834 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12835 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12837 if (id == GAME_CTRL_ID_STOP ||
12838 id == GAME_CTRL_ID_PAUSE ||
12839 id == GAME_CTRL_ID_PLAY)
12841 button_type = GD_TYPE_NORMAL_BUTTON;
12843 event_mask = GD_EVENT_RELEASED;
12844 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12845 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12849 button_type = GD_TYPE_CHECK_BUTTON;
12851 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12852 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12853 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12854 event_mask = GD_EVENT_PRESSED;
12855 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12856 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12859 gi = CreateGadget(GDI_CUSTOM_ID, id,
12860 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12861 GDI_X, DX + gd_xoffset,
12862 GDI_Y, DY + gd_yoffset,
12863 GDI_WIDTH, GAME_BUTTON_XSIZE,
12864 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12865 GDI_TYPE, button_type,
12866 GDI_STATE, GD_BUTTON_UNPRESSED,
12867 GDI_CHECKED, checked,
12868 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12869 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12870 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12871 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12872 GDI_EVENT_MASK, event_mask,
12873 GDI_CALLBACK_ACTION, HandleGameButtons,
12877 Error(ERR_EXIT, "cannot create gadget");
12879 game_gadget[id] = gi;
12883 void FreeGameButtons()
12887 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12888 FreeGadget(game_gadget[i]);
12891 static void MapGameButtons()
12895 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12896 MapGadget(game_gadget[i]);
12899 void UnmapGameButtons()
12903 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12904 UnmapGadget(game_gadget[i]);
12907 static void HandleGameButtons(struct GadgetInfo *gi)
12909 int id = gi->custom_id;
12911 if (game_status != GAME_MODE_PLAYING)
12916 case GAME_CTRL_ID_STOP:
12917 RequestQuitGame(TRUE);
12920 case GAME_CTRL_ID_PAUSE:
12921 if (options.network)
12923 #if defined(NETWORK_AVALIABLE)
12925 SendToServer_ContinuePlaying();
12927 SendToServer_PausePlaying();
12931 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12934 case GAME_CTRL_ID_PLAY:
12937 #if defined(NETWORK_AVALIABLE)
12938 if (options.network)
12939 SendToServer_ContinuePlaying();
12943 tape.pausing = FALSE;
12944 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12949 case SOUND_CTRL_ID_MUSIC:
12950 if (setup.sound_music)
12952 setup.sound_music = FALSE;
12955 else if (audio.music_available)
12957 setup.sound = setup.sound_music = TRUE;
12959 SetAudioMode(setup.sound);
12965 case SOUND_CTRL_ID_LOOPS:
12966 if (setup.sound_loops)
12967 setup.sound_loops = FALSE;
12968 else if (audio.loops_available)
12970 setup.sound = setup.sound_loops = TRUE;
12971 SetAudioMode(setup.sound);
12975 case SOUND_CTRL_ID_SIMPLE:
12976 if (setup.sound_simple)
12977 setup.sound_simple = FALSE;
12978 else if (audio.sound_available)
12980 setup.sound = setup.sound_simple = TRUE;
12981 SetAudioMode(setup.sound);