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[4])
1069 for (i = 0; i < MAX_KEYS; i++)
1071 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1072 el2edimg(EL_KEY_1 + i));
1075 inline void DrawGameValue_Score(int value)
1077 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1080 inline void DrawGameValue_Time(int value)
1083 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1085 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1088 inline void DrawGameValue_Level(int value)
1091 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1094 /* misuse area for displaying emeralds to draw bigger level number */
1095 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1096 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1098 /* now copy it to the area for displaying level number */
1099 BlitBitmap(drawto, drawto,
1100 DX_EMERALDS, DY_EMERALDS + 1,
1101 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1102 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1103 DX_LEVEL - 1, DY_LEVEL + 1);
1105 /* restore the area for displaying emeralds */
1106 DrawGameValue_Emeralds(local_player->gems_still_needed);
1108 /* yes, this is all really ugly :-) */
1112 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1118 for (i = 0; i < MAX_KEYS; i++)
1119 key[i] = key_bits & (1 << i);
1121 DrawGameValue_Emeralds(emeralds);
1122 DrawGameValue_Dynamite(dynamite);
1123 DrawGameValue_Score(score);
1124 DrawGameValue_Time(time);
1126 DrawGameValue_Keys(key);
1129 void DrawGameDoorValues()
1133 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1135 DrawGameDoorValues_EM();
1140 DrawGameValue_Level(level_nr);
1142 DrawGameValue_Emeralds(local_player->gems_still_needed);
1143 DrawGameValue_Dynamite(local_player->inventory_size);
1144 DrawGameValue_Score(local_player->score);
1145 DrawGameValue_Time(TimeLeft);
1147 for (i = 0; i < MAX_PLAYERS; i++)
1148 DrawGameValue_Keys(stored_player[i].key);
1151 static void resolve_group_element(int group_element, int recursion_depth)
1153 static int group_nr;
1154 static struct ElementGroupInfo *group;
1155 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1158 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1160 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1161 group_element - EL_GROUP_START + 1);
1163 /* replace element which caused too deep recursion by question mark */
1164 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1169 if (recursion_depth == 0) /* initialization */
1171 group = element_info[group_element].group;
1172 group_nr = group_element - EL_GROUP_START;
1174 group->num_elements_resolved = 0;
1175 group->choice_pos = 0;
1178 for (i = 0; i < actual_group->num_elements; i++)
1180 int element = actual_group->element[i];
1182 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1185 if (IS_GROUP_ELEMENT(element))
1186 resolve_group_element(element, recursion_depth + 1);
1189 group->element_resolved[group->num_elements_resolved++] = element;
1190 element_info[element].in_group[group_nr] = TRUE;
1195 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1197 printf("::: group %d: %d resolved elements\n",
1198 group_element - EL_GROUP_START, group->num_elements_resolved);
1199 for (i = 0; i < group->num_elements_resolved; i++)
1200 printf("::: - %d ['%s']\n", group->element_resolved[i],
1201 element_info[group->element_resolved[i]].token_name);
1208 =============================================================================
1210 -----------------------------------------------------------------------------
1211 initialize game engine due to level / tape version number
1212 =============================================================================
1215 static void InitGameEngine()
1219 /* set game engine from tape file when re-playing, else from level file */
1220 game.engine_version = (tape.playing ? tape.engine_version :
1221 level.game_version);
1223 /* ---------------------------------------------------------------------- */
1224 /* set flags for bugs and changes according to active game engine version */
1225 /* ---------------------------------------------------------------------- */
1229 Before 3.1.0, custom elements that "change when pushing" changed directly
1230 after the player started pushing them (until then handled in "DigField()").
1231 Since 3.1.0, these custom elements are not changed until the "pushing"
1232 move of the element is finished (now handled in "ContinueMoving()").
1234 Affected levels/tapes:
1235 The first condition is generally needed for all levels/tapes before version
1236 3.1.0, which might use the old behaviour before it was changed; known tapes
1237 that are affected are some tapes from the level set "Walpurgis Gardens" by
1239 The second condition is an exception from the above case and is needed for
1240 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1241 above (including some development versions of 3.1.0), but before it was
1242 known that this change would break tapes like the above and was fixed in
1243 3.1.1, so that the changed behaviour was active although the engine version
1244 while recording maybe was before 3.1.0. There is at least one tape that is
1245 affected by this exception, which is the tape for the one-level set "Bug
1246 Machine" by Juergen Bonhagen.
1249 game.use_bug_change_when_pushing =
1250 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1252 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1253 tape.game_version < VERSION_IDENT(3,1,1,0)));
1255 /* ---------------------------------------------------------------------- */
1257 /* dynamically adjust element properties according to game engine version */
1258 InitElementPropertiesEngine(game.engine_version);
1261 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1262 printf(" tape version == %06d [%s] [file: %06d]\n",
1263 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1265 printf(" => game.engine_version == %06d\n", game.engine_version);
1268 /* ---------- recursively resolve group elements ------------------------- */
1270 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1271 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1272 element_info[i].in_group[j] = FALSE;
1274 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1275 resolve_group_element(EL_GROUP_START + i, 0);
1277 /* ---------- initialize player's initial move delay --------------------- */
1279 #if USE_NEW_MOVE_DELAY
1280 /* dynamically adjust player properties according to level information */
1281 game.initial_move_delay_value =
1282 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1284 /* dynamically adjust player properties according to game engine version */
1285 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1286 game.initial_move_delay_value : 0);
1288 /* dynamically adjust player properties according to game engine version */
1289 game.initial_move_delay =
1290 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1291 INITIAL_MOVE_DELAY_OFF);
1293 /* dynamically adjust player properties according to level information */
1294 game.initial_move_delay_value =
1295 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1298 /* ---------- initialize player's initial push delay --------------------- */
1300 /* dynamically adjust player properties according to game engine version */
1301 game.initial_push_delay_value =
1302 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1304 /* ---------- initialize changing elements ------------------------------- */
1306 /* initialize changing elements information */
1307 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1309 struct ElementInfo *ei = &element_info[i];
1311 /* this pointer might have been changed in the level editor */
1312 ei->change = &ei->change_page[0];
1314 if (!IS_CUSTOM_ELEMENT(i))
1316 ei->change->target_element = EL_EMPTY_SPACE;
1317 ei->change->delay_fixed = 0;
1318 ei->change->delay_random = 0;
1319 ei->change->delay_frames = 1;
1322 ei->change_events = CE_BITMASK_DEFAULT;
1323 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1325 ei->event_page_nr[j] = 0;
1326 ei->event_page[j] = &ei->change_page[0];
1330 /* add changing elements from pre-defined list */
1331 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1333 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1334 struct ElementInfo *ei = &element_info[ch_delay->element];
1336 ei->change->target_element = ch_delay->target_element;
1337 ei->change->delay_fixed = ch_delay->change_delay;
1339 ei->change->pre_change_function = ch_delay->pre_change_function;
1340 ei->change->change_function = ch_delay->change_function;
1341 ei->change->post_change_function = ch_delay->post_change_function;
1343 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1346 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1351 /* add change events from custom element configuration */
1352 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1354 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1356 for (j = 0; j < ei->num_change_pages; j++)
1358 if (!ei->change_page[j].can_change)
1361 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1363 /* only add event page for the first page found with this event */
1364 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1365 !(ei->change_events & CH_EVENT_BIT(k)))
1367 ei->change_events |= CH_EVENT_BIT(k);
1368 ei->event_page_nr[k] = j;
1369 ei->event_page[k] = &ei->change_page[j];
1377 /* add change events from custom element configuration */
1378 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1380 int element = EL_CUSTOM_START + i;
1382 /* only add custom elements that change after fixed/random frame delay */
1383 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1384 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1388 /* ---------- initialize run-time trigger player and element ------------- */
1390 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1392 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1394 for (j = 0; j < ei->num_change_pages; j++)
1396 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1397 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1401 /* ---------- initialize trigger events ---------------------------------- */
1403 /* initialize trigger events information */
1404 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1405 trigger_events[i] = EP_BITMASK_DEFAULT;
1408 /* add trigger events from element change event properties */
1409 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1411 struct ElementInfo *ei = &element_info[i];
1413 for (j = 0; j < ei->num_change_pages; j++)
1415 if (!ei->change_page[j].can_change)
1418 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1420 int trigger_element = ei->change_page[j].trigger_element;
1422 if (IS_GROUP_ELEMENT(trigger_element))
1424 struct ElementGroupInfo *group = element_info[trigger_element].group;
1426 for (k = 0; k < group->num_elements_resolved; k++)
1427 trigger_events[group->element_resolved[k]]
1428 |= ei->change_page[j].events;
1431 trigger_events[trigger_element] |= ei->change_page[j].events;
1436 /* add trigger events from element change event properties */
1437 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1438 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1439 trigger_events[element_info[i].change->trigger_element] |=
1440 element_info[i].change->events;
1443 /* ---------- initialize push delay -------------------------------------- */
1445 /* initialize push delay values to default */
1446 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1448 if (!IS_CUSTOM_ELEMENT(i))
1450 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1451 element_info[i].push_delay_random = game.default_push_delay_random;
1455 /* set push delay value for certain elements from pre-defined list */
1456 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1458 int e = push_delay_list[i].element;
1460 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1461 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1464 /* set push delay value for Supaplex elements for newer engine versions */
1465 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1467 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1469 if (IS_SP_ELEMENT(i))
1471 #if USE_NEW_MOVE_STYLE
1472 /* set SP push delay to just enough to push under a falling zonk */
1473 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1475 element_info[i].push_delay_fixed = delay;
1476 element_info[i].push_delay_random = 0;
1478 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1479 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1485 /* ---------- initialize move stepsize ----------------------------------- */
1487 /* initialize move stepsize values to default */
1488 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1489 if (!IS_CUSTOM_ELEMENT(i))
1490 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1492 /* set move stepsize value for certain elements from pre-defined list */
1493 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1495 int e = move_stepsize_list[i].element;
1497 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1501 /* ---------- initialize move dig/leave ---------------------------------- */
1503 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1505 element_info[i].can_leave_element = FALSE;
1506 element_info[i].can_leave_element_last = FALSE;
1510 /* ---------- initialize gem count --------------------------------------- */
1512 /* initialize gem count values for each element */
1513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1514 if (!IS_CUSTOM_ELEMENT(i))
1515 element_info[i].collect_count = 0;
1517 /* add gem count values for all elements from pre-defined list */
1518 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1519 element_info[collect_count_list[i].element].collect_count =
1520 collect_count_list[i].count;
1522 /* ---------- initialize access direction -------------------------------- */
1524 /* initialize access direction values to default (access from every side) */
1525 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1526 if (!IS_CUSTOM_ELEMENT(i))
1527 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1529 /* set access direction value for certain elements from pre-defined list */
1530 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1531 element_info[access_direction_list[i].element].access_direction =
1532 access_direction_list[i].direction;
1537 =============================================================================
1539 -----------------------------------------------------------------------------
1540 initialize and start new game
1541 =============================================================================
1546 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1547 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1548 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1555 #if USE_NEW_AMOEBA_CODE
1556 printf("Using new amoeba code.\n");
1558 printf("Using old amoeba code.\n");
1563 /* don't play tapes over network */
1564 network_playing = (options.network && !tape.playing);
1566 for (i = 0; i < MAX_PLAYERS; i++)
1568 struct PlayerInfo *player = &stored_player[i];
1570 player->index_nr = i;
1571 player->index_bit = (1 << i);
1572 player->element_nr = EL_PLAYER_1 + i;
1574 player->present = FALSE;
1575 player->active = FALSE;
1578 player->effective_action = 0;
1579 player->programmed_action = 0;
1582 player->gems_still_needed = level.gems_needed;
1583 player->sokobanfields_still_needed = 0;
1584 player->lights_still_needed = 0;
1585 player->friends_still_needed = 0;
1587 for (j = 0; j < MAX_KEYS; j++)
1588 player->key[j] = FALSE;
1590 player->dynabomb_count = 0;
1591 player->dynabomb_size = 1;
1592 player->dynabombs_left = 0;
1593 player->dynabomb_xl = FALSE;
1595 player->MovDir = MV_NO_MOVING;
1598 player->GfxDir = MV_NO_MOVING;
1599 player->GfxAction = ACTION_DEFAULT;
1601 player->StepFrame = 0;
1603 player->use_murphy_graphic = FALSE;
1605 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1606 player->block_delay = -1; /* initialized in InitPlayerField() */
1608 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1610 player->actual_frame_counter = 0;
1612 player->step_counter = 0;
1614 player->last_move_dir = MV_NO_MOVING;
1616 player->is_waiting = FALSE;
1617 player->is_moving = FALSE;
1618 player->is_auto_moving = FALSE;
1619 player->is_digging = FALSE;
1620 player->is_snapping = FALSE;
1621 player->is_collecting = FALSE;
1622 player->is_pushing = FALSE;
1623 player->is_switching = FALSE;
1624 player->is_dropping = FALSE;
1626 player->is_bored = FALSE;
1627 player->is_sleeping = FALSE;
1629 player->frame_counter_bored = -1;
1630 player->frame_counter_sleeping = -1;
1632 player->anim_delay_counter = 0;
1633 player->post_delay_counter = 0;
1635 player->action_waiting = ACTION_DEFAULT;
1636 player->last_action_waiting = ACTION_DEFAULT;
1637 player->special_action_bored = ACTION_DEFAULT;
1638 player->special_action_sleeping = ACTION_DEFAULT;
1640 player->num_special_action_bored = 0;
1641 player->num_special_action_sleeping = 0;
1643 /* determine number of special actions for bored and sleeping animation */
1644 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1646 boolean found = FALSE;
1648 for (k = 0; k < NUM_DIRECTIONS; k++)
1649 if (el_act_dir2img(player->element_nr, j, k) !=
1650 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1654 player->num_special_action_bored++;
1658 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1660 boolean found = FALSE;
1662 for (k = 0; k < NUM_DIRECTIONS; k++)
1663 if (el_act_dir2img(player->element_nr, j, k) !=
1664 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1668 player->num_special_action_sleeping++;
1673 player->switch_x = -1;
1674 player->switch_y = -1;
1676 player->show_envelope = 0;
1678 player->move_delay = game.initial_move_delay;
1679 player->move_delay_value = game.initial_move_delay_value;
1681 player->move_delay_reset_counter = 0;
1683 #if USE_NEW_PUSH_DELAY
1684 player->push_delay = -1; /* initialized when pushing starts */
1685 player->push_delay_value = game.initial_push_delay_value;
1687 player->push_delay = 0;
1688 player->push_delay_value = game.initial_push_delay_value;
1691 player->drop_delay = 0;
1693 player->last_jx = player->last_jy = 0;
1694 player->jx = player->jy = 0;
1696 player->shield_normal_time_left = 0;
1697 player->shield_deadly_time_left = 0;
1699 player->inventory_infinite_element = EL_UNDEFINED;
1700 player->inventory_size = 0;
1702 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1703 SnapField(player, 0, 0);
1705 player->LevelSolved = FALSE;
1706 player->GameOver = FALSE;
1709 network_player_action_received = FALSE;
1711 #if defined(NETWORK_AVALIABLE)
1712 /* initial null action */
1713 if (network_playing)
1714 SendToServer_MovePlayer(MV_NO_MOVING);
1723 TimeLeft = level.time;
1726 ScreenMovDir = MV_NO_MOVING;
1730 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1732 AllPlayersGone = FALSE;
1734 game.yamyam_content_nr = 0;
1735 game.magic_wall_active = FALSE;
1736 game.magic_wall_time_left = 0;
1737 game.light_time_left = 0;
1738 game.timegate_time_left = 0;
1739 game.switchgate_pos = 0;
1740 game.balloon_dir = MV_NO_MOVING;
1741 game.gravity = level.initial_gravity;
1742 game.explosions_delayed = TRUE;
1744 game.envelope_active = FALSE;
1746 for (i = 0; i < NUM_BELTS; i++)
1748 game.belt_dir[i] = MV_NO_MOVING;
1749 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1752 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1753 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1755 for (x = 0; x < lev_fieldx; x++)
1757 for (y = 0; y < lev_fieldy; y++)
1759 Feld[x][y] = level.field[x][y];
1760 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1761 ChangeDelay[x][y] = 0;
1762 ChangePage[x][y] = -1;
1763 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1765 WasJustMoving[x][y] = 0;
1766 WasJustFalling[x][y] = 0;
1767 CheckCollision[x][y] = 0;
1769 Pushed[x][y] = FALSE;
1771 Changed[x][y] = CE_BITMASK_DEFAULT;
1772 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1774 ExplodePhase[x][y] = 0;
1775 ExplodeDelay[x][y] = 0;
1776 ExplodeField[x][y] = EX_TYPE_NONE;
1778 RunnerVisit[x][y] = 0;
1779 PlayerVisit[x][y] = 0;
1782 GfxRandom[x][y] = INIT_GFX_RANDOM();
1783 GfxElement[x][y] = EL_UNDEFINED;
1784 GfxAction[x][y] = ACTION_DEFAULT;
1785 GfxDir[x][y] = MV_NO_MOVING;
1789 for (y = 0; y < lev_fieldy; y++)
1791 for (x = 0; x < lev_fieldx; x++)
1793 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1795 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1797 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1800 InitField(x, y, TRUE);
1806 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1807 emulate_sb ? EMU_SOKOBAN :
1808 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1810 /* initialize explosion and ignition delay */
1811 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1813 if (!IS_CUSTOM_ELEMENT(i))
1816 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1817 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1818 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1819 int last_phase = (num_phase + 1) * delay;
1820 int half_phase = (num_phase / 2) * delay;
1822 element_info[i].explosion_delay = last_phase - 1;
1823 element_info[i].ignition_delay = half_phase;
1826 if (i == EL_BLACK_ORB)
1827 element_info[i].ignition_delay = 0;
1829 if (i == EL_BLACK_ORB)
1830 element_info[i].ignition_delay = 1;
1835 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1836 element_info[i].explosion_delay = 1;
1838 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1839 element_info[i].ignition_delay = 1;
1843 /* correct non-moving belts to start moving left */
1844 for (i = 0; i < NUM_BELTS; i++)
1845 if (game.belt_dir[i] == MV_NO_MOVING)
1846 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1848 /* check if any connected player was not found in playfield */
1849 for (i = 0; i < MAX_PLAYERS; i++)
1851 struct PlayerInfo *player = &stored_player[i];
1853 if (player->connected && !player->present)
1855 for (j = 0; j < MAX_PLAYERS; j++)
1857 struct PlayerInfo *some_player = &stored_player[j];
1858 int jx = some_player->jx, jy = some_player->jy;
1860 /* assign first free player found that is present in the playfield */
1861 if (some_player->present && !some_player->connected)
1863 player->present = TRUE;
1864 player->active = TRUE;
1866 some_player->present = FALSE;
1867 some_player->active = FALSE;
1870 player->element_nr = some_player->element_nr;
1873 #if USE_NEW_BLOCK_STYLE
1874 player->block_last_field = some_player->block_last_field;
1875 player->block_delay = some_player->block_delay;
1878 StorePlayer[jx][jy] = player->element_nr;
1879 player->jx = player->last_jx = jx;
1880 player->jy = player->last_jy = jy;
1890 /* when playing a tape, eliminate all players which do not participate */
1892 for (i = 0; i < MAX_PLAYERS; i++)
1894 if (stored_player[i].active && !tape.player_participates[i])
1896 struct PlayerInfo *player = &stored_player[i];
1897 int jx = player->jx, jy = player->jy;
1899 player->active = FALSE;
1900 StorePlayer[jx][jy] = 0;
1901 Feld[jx][jy] = EL_EMPTY;
1905 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1907 /* when in single player mode, eliminate all but the first active player */
1909 for (i = 0; i < MAX_PLAYERS; i++)
1911 if (stored_player[i].active)
1913 for (j = i + 1; j < MAX_PLAYERS; j++)
1915 if (stored_player[j].active)
1917 struct PlayerInfo *player = &stored_player[j];
1918 int jx = player->jx, jy = player->jy;
1920 player->active = FALSE;
1921 player->present = FALSE;
1923 StorePlayer[jx][jy] = 0;
1924 Feld[jx][jy] = EL_EMPTY;
1931 /* when recording the game, store which players take part in the game */
1934 for (i = 0; i < MAX_PLAYERS; i++)
1935 if (stored_player[i].active)
1936 tape.player_participates[i] = TRUE;
1941 for (i = 0; i < MAX_PLAYERS; i++)
1943 struct PlayerInfo *player = &stored_player[i];
1945 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1950 if (local_player == player)
1951 printf("Player %d is local player.\n", i+1);
1955 if (BorderElement == EL_EMPTY)
1958 SBX_Right = lev_fieldx - SCR_FIELDX;
1960 SBY_Lower = lev_fieldy - SCR_FIELDY;
1965 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1967 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1970 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1971 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1973 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1974 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1976 /* if local player not found, look for custom element that might create
1977 the player (make some assumptions about the right custom element) */
1978 if (!local_player->present)
1980 int start_x = 0, start_y = 0;
1981 int found_rating = 0;
1982 int found_element = EL_UNDEFINED;
1984 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1986 int element = Feld[x][y];
1991 if (!IS_CUSTOM_ELEMENT(element))
1994 if (CAN_CHANGE(element))
1996 for (i = 0; i < element_info[element].num_change_pages; i++)
1998 content = element_info[element].change_page[i].target_element;
1999 is_player = ELEM_IS_PLAYER(content);
2001 if (is_player && (found_rating < 3 || element < found_element))
2007 found_element = element;
2012 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2014 content = element_info[element].content[xx][yy];
2015 is_player = ELEM_IS_PLAYER(content);
2017 if (is_player && (found_rating < 2 || element < found_element))
2019 start_x = x + xx - 1;
2020 start_y = y + yy - 1;
2023 found_element = element;
2026 if (!CAN_CHANGE(element))
2029 for (i = 0; i < element_info[element].num_change_pages; i++)
2031 content= element_info[element].change_page[i].target_content[xx][yy];
2032 is_player = ELEM_IS_PLAYER(content);
2034 if (is_player && (found_rating < 1 || element < found_element))
2036 start_x = x + xx - 1;
2037 start_y = y + yy - 1;
2040 found_element = element;
2046 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2047 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2050 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2051 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2057 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2058 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2059 local_player->jx - MIDPOSX);
2061 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2062 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2063 local_player->jy - MIDPOSY);
2065 scroll_x = SBX_Left;
2066 scroll_y = SBY_Upper;
2067 if (local_player->jx >= SBX_Left + MIDPOSX)
2068 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2069 local_player->jx - MIDPOSX :
2071 if (local_player->jy >= SBY_Upper + MIDPOSY)
2072 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2073 local_player->jy - MIDPOSY :
2078 CloseDoor(DOOR_CLOSE_1);
2080 /* !!! FIX THIS (START) !!! */
2081 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2083 InitGameEngine_EM();
2090 /* after drawing the level, correct some elements */
2091 if (game.timegate_time_left == 0)
2092 CloseAllOpenTimegates();
2094 if (setup.soft_scrolling)
2095 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2097 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2100 /* !!! FIX THIS (END) !!! */
2102 /* copy default game door content to main double buffer */
2103 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2104 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2106 DrawGameDoorValues();
2110 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2111 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2112 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2116 /* copy actual game door content to door double buffer for OpenDoor() */
2117 BlitBitmap(drawto, bitmap_db_door,
2118 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2120 OpenDoor(DOOR_OPEN_ALL);
2122 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2124 if (setup.sound_music)
2127 KeyboardAutoRepeatOffUnlessAutoplay();
2131 for (i = 0; i < MAX_PLAYERS; i++)
2132 printf("Player %d %sactive.\n",
2133 i + 1, (stored_player[i].active ? "" : "not "));
2137 printf("::: starting game [%d]\n", FrameCounter);
2141 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2143 /* this is used for non-R'n'D game engines to update certain engine values */
2145 /* needed to determine if sounds are played within the visible screen area */
2146 scroll_x = actual_scroll_x;
2147 scroll_y = actual_scroll_y;
2150 void InitMovDir(int x, int y)
2152 int i, element = Feld[x][y];
2153 static int xy[4][2] =
2160 static int direction[3][4] =
2162 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2163 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2164 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2173 Feld[x][y] = EL_BUG;
2174 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2177 case EL_SPACESHIP_RIGHT:
2178 case EL_SPACESHIP_UP:
2179 case EL_SPACESHIP_LEFT:
2180 case EL_SPACESHIP_DOWN:
2181 Feld[x][y] = EL_SPACESHIP;
2182 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2185 case EL_BD_BUTTERFLY_RIGHT:
2186 case EL_BD_BUTTERFLY_UP:
2187 case EL_BD_BUTTERFLY_LEFT:
2188 case EL_BD_BUTTERFLY_DOWN:
2189 Feld[x][y] = EL_BD_BUTTERFLY;
2190 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2193 case EL_BD_FIREFLY_RIGHT:
2194 case EL_BD_FIREFLY_UP:
2195 case EL_BD_FIREFLY_LEFT:
2196 case EL_BD_FIREFLY_DOWN:
2197 Feld[x][y] = EL_BD_FIREFLY;
2198 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2201 case EL_PACMAN_RIGHT:
2203 case EL_PACMAN_LEFT:
2204 case EL_PACMAN_DOWN:
2205 Feld[x][y] = EL_PACMAN;
2206 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2209 case EL_SP_SNIKSNAK:
2210 MovDir[x][y] = MV_UP;
2213 case EL_SP_ELECTRON:
2214 MovDir[x][y] = MV_LEFT;
2221 Feld[x][y] = EL_MOLE;
2222 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2226 if (IS_CUSTOM_ELEMENT(element))
2228 struct ElementInfo *ei = &element_info[element];
2229 int move_direction_initial = ei->move_direction_initial;
2230 int move_pattern = ei->move_pattern;
2232 if (move_direction_initial == MV_START_PREVIOUS)
2234 if (MovDir[x][y] != MV_NO_MOVING)
2237 move_direction_initial = MV_START_AUTOMATIC;
2240 if (move_direction_initial == MV_START_RANDOM)
2241 MovDir[x][y] = 1 << RND(4);
2242 else if (move_direction_initial & MV_ANY_DIRECTION)
2243 MovDir[x][y] = move_direction_initial;
2244 else if (move_pattern == MV_ALL_DIRECTIONS ||
2245 move_pattern == MV_TURNING_LEFT ||
2246 move_pattern == MV_TURNING_RIGHT ||
2247 move_pattern == MV_TURNING_LEFT_RIGHT ||
2248 move_pattern == MV_TURNING_RIGHT_LEFT ||
2249 move_pattern == MV_TURNING_RANDOM)
2250 MovDir[x][y] = 1 << RND(4);
2251 else if (move_pattern == MV_HORIZONTAL)
2252 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2253 else if (move_pattern == MV_VERTICAL)
2254 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2255 else if (move_pattern & MV_ANY_DIRECTION)
2256 MovDir[x][y] = element_info[element].move_pattern;
2257 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2258 move_pattern == MV_ALONG_RIGHT_SIDE)
2261 /* use random direction as default start direction */
2262 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2263 MovDir[x][y] = 1 << RND(4);
2266 for (i = 0; i < NUM_DIRECTIONS; i++)
2268 int x1 = x + xy[i][0];
2269 int y1 = y + xy[i][1];
2271 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2273 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2274 MovDir[x][y] = direction[0][i];
2276 MovDir[x][y] = direction[1][i];
2285 MovDir[x][y] = 1 << RND(4);
2287 if (element != EL_BUG &&
2288 element != EL_SPACESHIP &&
2289 element != EL_BD_BUTTERFLY &&
2290 element != EL_BD_FIREFLY)
2293 for (i = 0; i < NUM_DIRECTIONS; i++)
2295 int x1 = x + xy[i][0];
2296 int y1 = y + xy[i][1];
2298 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2300 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2302 MovDir[x][y] = direction[0][i];
2305 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2306 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2308 MovDir[x][y] = direction[1][i];
2317 GfxDir[x][y] = MovDir[x][y];
2320 void InitAmoebaNr(int x, int y)
2323 int group_nr = AmoebeNachbarNr(x, y);
2327 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2329 if (AmoebaCnt[i] == 0)
2337 AmoebaNr[x][y] = group_nr;
2338 AmoebaCnt[group_nr]++;
2339 AmoebaCnt2[group_nr]++;
2345 boolean raise_level = FALSE;
2347 if (local_player->MovPos)
2351 if (tape.auto_play) /* tape might already be stopped here */
2352 tape.auto_play_level_solved = TRUE;
2354 if (tape.playing && tape.auto_play)
2355 tape.auto_play_level_solved = TRUE;
2358 local_player->LevelSolved = FALSE;
2360 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2364 if (!tape.playing && setup.sound_loops)
2365 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2366 SND_CTRL_PLAY_LOOP);
2368 while (TimeLeft > 0)
2370 if (!tape.playing && !setup.sound_loops)
2371 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2372 if (TimeLeft > 0 && !(TimeLeft % 10))
2373 RaiseScore(level.score[SC_TIME_BONUS]);
2374 if (TimeLeft > 100 && !(TimeLeft % 10))
2379 DrawGameValue_Time(TimeLeft);
2387 if (!tape.playing && setup.sound_loops)
2388 StopSound(SND_GAME_LEVELTIME_BONUS);
2390 else if (level.time == 0) /* level without time limit */
2392 if (!tape.playing && setup.sound_loops)
2393 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2394 SND_CTRL_PLAY_LOOP);
2396 while (TimePlayed < 999)
2398 if (!tape.playing && !setup.sound_loops)
2399 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2400 if (TimePlayed < 999 && !(TimePlayed % 10))
2401 RaiseScore(level.score[SC_TIME_BONUS]);
2402 if (TimePlayed < 900 && !(TimePlayed % 10))
2407 DrawGameValue_Time(TimePlayed);
2415 if (!tape.playing && setup.sound_loops)
2416 StopSound(SND_GAME_LEVELTIME_BONUS);
2419 /* close exit door after last player */
2420 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2421 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2422 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2424 int element = Feld[ExitX][ExitY];
2426 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2427 EL_SP_EXIT_CLOSING);
2429 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2432 /* Hero disappears */
2433 if (ExitX >= 0 && ExitY >= 0)
2434 DrawLevelField(ExitX, ExitY);
2441 CloseDoor(DOOR_CLOSE_1);
2446 SaveTape(tape.level_nr); /* Ask to save tape */
2449 if (level_nr == leveldir_current->handicap_level)
2451 leveldir_current->handicap_level++;
2452 SaveLevelSetup_SeriesInfo();
2455 if (level_editor_test_game)
2456 local_player->score = -1; /* no highscore when playing from editor */
2457 else if (level_nr < leveldir_current->last_level)
2458 raise_level = TRUE; /* advance to next level */
2460 if ((hi_pos = NewHiScore()) >= 0)
2462 game_status = GAME_MODE_SCORES;
2463 DrawHallOfFame(hi_pos);
2472 game_status = GAME_MODE_MAIN;
2489 LoadScore(level_nr);
2491 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2492 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2495 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2497 if (local_player->score > highscore[k].Score)
2499 /* player has made it to the hall of fame */
2501 if (k < MAX_SCORE_ENTRIES - 1)
2503 int m = MAX_SCORE_ENTRIES - 1;
2506 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2507 if (!strcmp(setup.player_name, highscore[l].Name))
2509 if (m == k) /* player's new highscore overwrites his old one */
2513 for (l = m; l > k; l--)
2515 strcpy(highscore[l].Name, highscore[l - 1].Name);
2516 highscore[l].Score = highscore[l - 1].Score;
2523 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2524 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2525 highscore[k].Score = local_player->score;
2531 else if (!strncmp(setup.player_name, highscore[k].Name,
2532 MAX_PLAYER_NAME_LEN))
2533 break; /* player already there with a higher score */
2539 SaveScore(level_nr);
2544 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2546 if (player->GfxAction != action || player->GfxDir != dir)
2549 printf("Player frame reset! (%d => %d, %d => %d)\n",
2550 player->GfxAction, action, player->GfxDir, dir);
2553 player->GfxAction = action;
2554 player->GfxDir = dir;
2556 player->StepFrame = 0;
2560 static void ResetRandomAnimationValue(int x, int y)
2562 GfxRandom[x][y] = INIT_GFX_RANDOM();
2565 static void ResetGfxAnimation(int x, int y)
2568 GfxAction[x][y] = ACTION_DEFAULT;
2569 GfxDir[x][y] = MovDir[x][y];
2572 void InitMovingField(int x, int y, int direction)
2574 int element = Feld[x][y];
2575 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2576 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2580 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2581 ResetGfxAnimation(x, y);
2583 MovDir[newx][newy] = MovDir[x][y] = direction;
2584 GfxDir[x][y] = direction;
2586 if (Feld[newx][newy] == EL_EMPTY)
2587 Feld[newx][newy] = EL_BLOCKED;
2589 if (direction == MV_DOWN && CAN_FALL(element))
2590 GfxAction[x][y] = ACTION_FALLING;
2592 GfxAction[x][y] = ACTION_MOVING;
2594 GfxFrame[newx][newy] = GfxFrame[x][y];
2595 GfxRandom[newx][newy] = GfxRandom[x][y];
2596 GfxAction[newx][newy] = GfxAction[x][y];
2597 GfxDir[newx][newy] = GfxDir[x][y];
2600 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2602 int direction = MovDir[x][y];
2603 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2604 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2610 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2612 int oldx = x, oldy = y;
2613 int direction = MovDir[x][y];
2615 if (direction == MV_LEFT)
2617 else if (direction == MV_RIGHT)
2619 else if (direction == MV_UP)
2621 else if (direction == MV_DOWN)
2624 *comes_from_x = oldx;
2625 *comes_from_y = oldy;
2628 int MovingOrBlocked2Element(int x, int y)
2630 int element = Feld[x][y];
2632 if (element == EL_BLOCKED)
2636 Blocked2Moving(x, y, &oldx, &oldy);
2637 return Feld[oldx][oldy];
2643 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2645 /* like MovingOrBlocked2Element(), but if element is moving
2646 and (x,y) is the field the moving element is just leaving,
2647 return EL_BLOCKED instead of the element value */
2648 int element = Feld[x][y];
2650 if (IS_MOVING(x, y))
2652 if (element == EL_BLOCKED)
2656 Blocked2Moving(x, y, &oldx, &oldy);
2657 return Feld[oldx][oldy];
2666 static void RemoveField(int x, int y)
2668 Feld[x][y] = EL_EMPTY;
2675 ChangeDelay[x][y] = 0;
2676 ChangePage[x][y] = -1;
2677 Pushed[x][y] = FALSE;
2680 ExplodeField[x][y] = EX_TYPE_NONE;
2683 GfxElement[x][y] = EL_UNDEFINED;
2684 GfxAction[x][y] = ACTION_DEFAULT;
2685 GfxDir[x][y] = MV_NO_MOVING;
2688 void RemoveMovingField(int x, int y)
2690 int oldx = x, oldy = y, newx = x, newy = y;
2691 int element = Feld[x][y];
2692 int next_element = EL_UNDEFINED;
2694 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2697 if (IS_MOVING(x, y))
2699 Moving2Blocked(x, y, &newx, &newy);
2701 if (Feld[newx][newy] != EL_BLOCKED)
2704 if (Feld[newx][newy] != EL_BLOCKED)
2706 /* element is moving, but target field is not free (blocked), but
2707 already occupied by something different (example: acid pool);
2708 in this case, only remove the moving field, but not the target */
2710 RemoveField(oldx, oldy);
2712 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2714 DrawLevelField(oldx, oldy);
2720 else if (element == EL_BLOCKED)
2722 Blocked2Moving(x, y, &oldx, &oldy);
2723 if (!IS_MOVING(oldx, oldy))
2727 if (element == EL_BLOCKED &&
2728 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2729 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2730 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2731 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2732 next_element = get_next_element(Feld[oldx][oldy]);
2734 RemoveField(oldx, oldy);
2735 RemoveField(newx, newy);
2737 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2739 if (next_element != EL_UNDEFINED)
2740 Feld[oldx][oldy] = next_element;
2742 DrawLevelField(oldx, oldy);
2743 DrawLevelField(newx, newy);
2746 void DrawDynamite(int x, int y)
2748 int sx = SCREENX(x), sy = SCREENY(y);
2749 int graphic = el2img(Feld[x][y]);
2752 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2755 if (IS_WALKABLE_INSIDE(Back[x][y]))
2759 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2760 else if (Store[x][y])
2761 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2763 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2766 if (Back[x][y] || Store[x][y])
2767 DrawGraphicThruMask(sx, sy, graphic, frame);
2769 DrawGraphic(sx, sy, graphic, frame);
2771 if (game.emulation == EMU_SUPAPLEX)
2772 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2773 else if (Store[x][y])
2774 DrawGraphicThruMask(sx, sy, graphic, frame);
2776 DrawGraphic(sx, sy, graphic, frame);
2780 void CheckDynamite(int x, int y)
2782 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2786 if (MovDelay[x][y] != 0)
2789 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2796 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2798 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2799 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2800 StopSound(SND_DYNAMITE_ACTIVE);
2802 StopSound(SND_DYNABOMB_ACTIVE);
2808 void DrawRelocatePlayer(struct PlayerInfo *player)
2810 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2811 boolean no_delay = (tape.warp_forward);
2812 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2813 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2814 int jx = player->jx;
2815 int jy = player->jy;
2817 if (level.instant_relocation)
2820 int offset = (setup.scroll_delay ? 3 : 0);
2822 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2824 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2825 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2826 local_player->jx - MIDPOSX);
2828 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2829 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2830 local_player->jy - MIDPOSY);
2834 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2835 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2836 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2838 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2839 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2840 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2842 /* don't scroll over playfield boundaries */
2843 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2844 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2846 /* don't scroll over playfield boundaries */
2847 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2848 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2851 scroll_x += (local_player->jx - old_jx);
2852 scroll_y += (local_player->jy - old_jy);
2854 /* don't scroll over playfield boundaries */
2855 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2856 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2858 /* don't scroll over playfield boundaries */
2859 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2860 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2863 RedrawPlayfield(TRUE, 0,0,0,0);
2869 int offset = (setup.scroll_delay ? 3 : 0);
2871 int scroll_xx = -999, scroll_yy = -999;
2873 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2875 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2878 int fx = FX, fy = FY;
2880 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2881 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2882 local_player->jx - MIDPOSX);
2884 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2885 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2886 local_player->jy - MIDPOSY);
2888 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2889 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2892 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2895 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2902 fx += dx * TILEX / 2;
2903 fy += dy * TILEY / 2;
2905 ScrollLevel(dx, dy);
2908 /* scroll in two steps of half tile size to make things smoother */
2909 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2911 Delay(wait_delay_value);
2913 /* scroll second step to align at full tile size */
2915 Delay(wait_delay_value);
2918 int scroll_xx = -999, scroll_yy = -999;
2920 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2922 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2925 int fx = FX, fy = FY;
2927 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2928 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2929 local_player->jx - MIDPOSX);
2931 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2932 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2933 local_player->jy - MIDPOSY);
2935 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2936 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2939 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2942 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2949 fx += dx * TILEX / 2;
2950 fy += dy * TILEY / 2;
2952 ScrollLevel(dx, dy);
2955 /* scroll in two steps of half tile size to make things smoother */
2956 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2958 Delay(wait_delay_value);
2960 /* scroll second step to align at full tile size */
2962 Delay(wait_delay_value);
2968 Delay(wait_delay_value);
2972 void RelocatePlayer(int jx, int jy, int el_player_raw)
2975 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2977 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2979 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2980 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2981 boolean no_delay = (tape.warp_forward);
2982 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2983 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2984 int old_jx = player->jx;
2985 int old_jy = player->jy;
2986 int old_element = Feld[old_jx][old_jy];
2987 int element = Feld[jx][jy];
2988 boolean player_relocated = (old_jx != jx || old_jy != jy);
2990 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2991 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2993 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2994 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2995 int leave_side_horiz = move_dir_horiz;
2996 int leave_side_vert = move_dir_vert;
2998 static int trigger_sides[4][2] =
3000 /* enter side leave side */
3001 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3002 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3003 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3004 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3006 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3007 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3008 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3009 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3011 int enter_side = enter_side_horiz | enter_side_vert;
3012 int leave_side = leave_side_horiz | leave_side_vert;
3014 if (player->GameOver) /* do not reanimate dead player */
3017 if (!player_relocated) /* no need to relocate the player */
3020 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3022 RemoveField(jx, jy); /* temporarily remove newly placed player */
3023 DrawLevelField(jx, jy);
3026 if (player->present)
3028 while (player->MovPos)
3030 ScrollPlayer(player, SCROLL_GO_ON);
3031 ScrollScreen(NULL, SCROLL_GO_ON);
3033 #if USE_NEW_MOVE_DELAY
3034 AdvanceFrameAndPlayerCounters(player->index_nr);
3042 Delay(wait_delay_value);
3045 DrawPlayer(player); /* needed here only to cleanup last field */
3046 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3048 player->is_moving = FALSE;
3052 if (IS_CUSTOM_ELEMENT(old_element))
3053 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3055 player->index_bit, leave_side);
3057 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3059 player->index_bit, leave_side);
3062 Feld[jx][jy] = el_player;
3063 InitPlayerField(jx, jy, el_player, TRUE);
3065 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3067 Feld[jx][jy] = element;
3068 InitField(jx, jy, FALSE);
3072 if (player == local_player) /* only visually relocate local player */
3073 DrawRelocatePlayer(player);
3077 TestIfHeroTouchesBadThing(jx, jy);
3078 TestIfPlayerTouchesCustomElement(jx, jy);
3082 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3087 /* needed to allow change of walkable custom element by entering player */
3088 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3089 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3091 /* needed to allow change of walkable custom element by entering player */
3092 Changed[jx][jy] = 0; /* allow another change */
3097 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3098 enter_side == MV_LEFT ? "left" :
3099 enter_side == MV_RIGHT ? "right" :
3100 enter_side == MV_UP ? "top" :
3101 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3105 if (IS_CUSTOM_ELEMENT(element))
3106 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3107 player->index_bit, enter_side);
3109 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3110 CE_OTHER_GETS_ENTERED,
3111 player->index_bit, enter_side);
3115 void Explode(int ex, int ey, int phase, int mode)
3122 /* !!! eliminate this variable !!! */
3123 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3128 int last_phase = num_phase * delay;
3129 int half_phase = (num_phase / 2) * delay;
3130 int first_phase_after_start = EX_PHASE_START + 1;
3134 if (game.explosions_delayed)
3136 ExplodeField[ex][ey] = mode;
3140 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3142 int center_element = Feld[ex][ey];
3145 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3149 /* --- This is only really needed (and now handled) in "Impact()". --- */
3150 /* do not explode moving elements that left the explode field in time */
3151 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3152 center_element == EL_EMPTY &&
3153 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3158 if (mode == EX_TYPE_NORMAL ||
3159 mode == EX_TYPE_CENTER ||
3160 mode == EX_TYPE_CROSS)
3161 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3163 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3164 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3167 /* remove things displayed in background while burning dynamite */
3168 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3171 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3173 /* put moving element to center field (and let it explode there) */
3174 center_element = MovingOrBlocked2Element(ex, ey);
3175 RemoveMovingField(ex, ey);
3176 Feld[ex][ey] = center_element;
3182 last_phase = element_info[center_element].explosion_delay + 1;
3184 last_phase = element_info[center_element].explosion_delay;
3188 printf("::: %d -> %d\n", center_element, last_phase);
3192 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3194 int xx = x - ex + 1;
3195 int yy = y - ey + 1;
3200 if (!IN_LEV_FIELD(x, y) ||
3201 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3202 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3205 if (!IN_LEV_FIELD(x, y) ||
3206 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3210 if (!IN_LEV_FIELD(x, y) ||
3211 ((mode != EX_TYPE_NORMAL ||
3212 center_element == EL_AMOEBA_TO_DIAMOND) &&
3213 (x != ex || y != ey)))
3217 element = Feld[x][y];
3219 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3221 element = MovingOrBlocked2Element(x, y);
3223 if (!IS_EXPLOSION_PROOF(element))
3224 RemoveMovingField(x, y);
3230 if (IS_EXPLOSION_PROOF(element))
3233 /* indestructible elements can only explode in center (but not flames) */
3235 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3236 mode == EX_TYPE_BORDER)) ||
3237 element == EL_FLAMES)
3240 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3241 element == EL_FLAMES)
3247 if ((IS_INDESTRUCTIBLE(element) &&
3248 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3249 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3250 element == EL_FLAMES)
3255 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3256 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3257 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3259 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3262 if (IS_ACTIVE_BOMB(element))
3264 /* re-activate things under the bomb like gate or penguin */
3266 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3269 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3274 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3275 element_info[Feld[x][y]].token_name,
3276 Store[x][y], Store2[x][y]);
3283 /* save walkable background elements while explosion on same tile */
3285 if (IS_INDESTRUCTIBLE(element))
3286 Back[x][y] = element;
3290 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3291 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3292 Back[x][y] = element;
3294 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3295 (x != ex || y != ey))
3296 Back[x][y] = element;
3299 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3300 Back[x][y] = element;
3304 /* ignite explodable elements reached by other explosion */
3305 if (element == EL_EXPLOSION)
3306 element = Store2[x][y];
3309 if (AmoebaNr[x][y] &&
3310 (element == EL_AMOEBA_FULL ||
3311 element == EL_BD_AMOEBA ||
3312 element == EL_AMOEBA_GROWING))
3314 AmoebaCnt[AmoebaNr[x][y]]--;
3315 AmoebaCnt2[AmoebaNr[x][y]]--;
3321 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3323 switch(StorePlayer[ex][ey])
3326 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3329 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3332 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3336 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3341 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3342 Store[x][y] = EL_EMPTY;
3344 if (game.emulation == EMU_SUPAPLEX)
3345 Store[x][y] = EL_EMPTY;
3348 else if (center_element == EL_MOLE)
3349 Store[x][y] = EL_EMERALD_RED;
3350 else if (center_element == EL_PENGUIN)
3351 Store[x][y] = EL_EMERALD_PURPLE;
3352 else if (center_element == EL_BUG)
3353 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3354 else if (center_element == EL_BD_BUTTERFLY)
3355 Store[x][y] = EL_BD_DIAMOND;
3356 else if (center_element == EL_SP_ELECTRON)
3357 Store[x][y] = EL_SP_INFOTRON;
3358 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3359 Store[x][y] = level.amoeba_content;
3360 else if (center_element == EL_YAMYAM)
3361 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3362 else if (IS_CUSTOM_ELEMENT(center_element) &&
3363 element_info[center_element].content[xx][yy] != EL_EMPTY)
3364 Store[x][y] = element_info[center_element].content[xx][yy];
3365 else if (element == EL_WALL_EMERALD)
3366 Store[x][y] = EL_EMERALD;
3367 else if (element == EL_WALL_DIAMOND)
3368 Store[x][y] = EL_DIAMOND;
3369 else if (element == EL_WALL_BD_DIAMOND)
3370 Store[x][y] = EL_BD_DIAMOND;
3371 else if (element == EL_WALL_EMERALD_YELLOW)
3372 Store[x][y] = EL_EMERALD_YELLOW;
3373 else if (element == EL_WALL_EMERALD_RED)
3374 Store[x][y] = EL_EMERALD_RED;
3375 else if (element == EL_WALL_EMERALD_PURPLE)
3376 Store[x][y] = EL_EMERALD_PURPLE;
3377 else if (element == EL_WALL_PEARL)
3378 Store[x][y] = EL_PEARL;
3379 else if (element == EL_WALL_CRYSTAL)
3380 Store[x][y] = EL_CRYSTAL;
3381 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3382 Store[x][y] = element_info[element].content[1][1];
3384 Store[x][y] = EL_EMPTY;
3386 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3387 center_element == EL_AMOEBA_TO_DIAMOND)
3388 Store2[x][y] = element;
3391 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3392 element_info[Store2[x][y]].token_name);
3396 if (AmoebaNr[x][y] &&
3397 (element == EL_AMOEBA_FULL ||
3398 element == EL_BD_AMOEBA ||
3399 element == EL_AMOEBA_GROWING))
3401 AmoebaCnt[AmoebaNr[x][y]]--;
3402 AmoebaCnt2[AmoebaNr[x][y]]--;
3408 MovDir[x][y] = MovPos[x][y] = 0;
3409 GfxDir[x][y] = MovDir[x][y];
3414 Feld[x][y] = EL_EXPLOSION;
3416 GfxElement[x][y] = center_element;
3418 GfxElement[x][y] = EL_UNDEFINED;
3421 ExplodePhase[x][y] = 1;
3423 ExplodeDelay[x][y] = last_phase;
3428 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3430 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3437 if (center_element == EL_YAMYAM)
3438 game.yamyam_content_nr =
3439 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3442 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3443 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3457 GfxFrame[x][y] = 0; /* restart explosion animation */
3461 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3465 last_phase = ExplodeDelay[x][y];
3468 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3472 /* activate this even in non-DEBUG version until cause for crash in
3473 getGraphicAnimationFrame() (see below) is found and eliminated */
3477 if (GfxElement[x][y] == EL_UNDEFINED)
3480 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3481 printf("Explode(): This should never happen!\n");
3484 GfxElement[x][y] = EL_EMPTY;
3490 border_element = Store2[x][y];
3492 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3493 border_element = StorePlayer[x][y];
3495 if (IS_PLAYER(x, y))
3496 border_element = StorePlayer[x][y];
3500 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3501 element_info[border_element].token_name, Store2[x][y]);
3505 printf("::: phase == %d\n", phase);
3508 if (phase == element_info[border_element].ignition_delay ||
3509 phase == last_phase)
3511 boolean border_explosion = FALSE;
3515 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3516 !PLAYER_EXPLOSION_PROTECTED(x, y))
3518 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3521 if (IS_PLAYER(x, y))
3524 KillHeroUnlessExplosionProtected(x, y);
3525 border_explosion = TRUE;
3528 if (phase == last_phase)
3529 printf("::: IS_PLAYER\n");
3532 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3535 printf("::: %d,%d: %d %s\n", x, y, border_element,
3536 element_info[border_element].token_name);
3539 Feld[x][y] = Store2[x][y];
3542 border_explosion = TRUE;
3545 if (phase == last_phase)
3546 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3549 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3551 AmoebeUmwandeln(x, y);
3553 border_explosion = TRUE;
3556 if (phase == last_phase)
3557 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3558 element_info[border_element].explosion_delay,
3559 element_info[border_element].ignition_delay,
3565 /* if an element just explodes due to another explosion (chain-reaction),
3566 do not immediately end the new explosion when it was the last frame of
3567 the explosion (as it would be done in the following "if"-statement!) */
3568 if (border_explosion && phase == last_phase)
3575 if (phase == first_phase_after_start)
3577 int element = Store2[x][y];
3579 if (element == EL_BLACK_ORB)
3581 Feld[x][y] = Store2[x][y];
3586 else if (phase == half_phase)
3588 int element = Store2[x][y];
3590 if (IS_PLAYER(x, y))
3591 KillHeroUnlessExplosionProtected(x, y);
3592 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3594 Feld[x][y] = Store2[x][y];
3598 else if (element == EL_AMOEBA_TO_DIAMOND)
3599 AmoebeUmwandeln(x, y);
3603 if (phase == last_phase)
3608 printf("::: done: phase == %d\n", phase);
3612 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3615 element = Feld[x][y] = Store[x][y];
3616 Store[x][y] = Store2[x][y] = 0;
3617 GfxElement[x][y] = EL_UNDEFINED;
3619 /* player can escape from explosions and might therefore be still alive */
3620 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3621 element <= EL_PLAYER_IS_EXPLODING_4)
3622 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3624 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3625 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3626 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3629 /* restore probably existing indestructible background element */
3630 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3631 element = Feld[x][y] = Back[x][y];
3634 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3635 GfxDir[x][y] = MV_NO_MOVING;
3636 ChangeDelay[x][y] = 0;
3637 ChangePage[x][y] = -1;
3640 InitField_WithBug2(x, y, FALSE);
3642 InitField(x, y, FALSE);
3644 /* !!! not needed !!! */
3646 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3647 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3650 if (CAN_MOVE(element))
3655 DrawLevelField(x, y);
3657 TestIfElementTouchesCustomElement(x, y);
3659 if (GFX_CRUMBLED(element))
3660 DrawLevelFieldCrumbledSandNeighbours(x, y);
3662 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3663 StorePlayer[x][y] = 0;
3665 if (ELEM_IS_PLAYER(element))
3666 RelocatePlayer(x, y, element);
3669 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3671 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3675 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3677 int stored = Store[x][y];
3678 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3679 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3683 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3685 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3689 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3693 printf("::: %d / %d [%d - %d]\n",
3694 GfxFrame[x][y], phase - delay, phase, delay);
3698 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3699 element_info[GfxElement[x][y]].token_name,
3704 DrawLevelFieldCrumbledSand(x, y);
3706 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3708 DrawLevelElement(x, y, Back[x][y]);
3709 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3711 else if (IS_WALKABLE_UNDER(Back[x][y]))
3713 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3714 DrawLevelElementThruMask(x, y, Back[x][y]);
3716 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3717 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3721 void DynaExplode(int ex, int ey)
3724 int dynabomb_element = Feld[ex][ey];
3725 int dynabomb_size = 1;
3726 boolean dynabomb_xl = FALSE;
3727 struct PlayerInfo *player;
3728 static int xy[4][2] =
3736 if (IS_ACTIVE_BOMB(dynabomb_element))
3738 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3739 dynabomb_size = player->dynabomb_size;
3740 dynabomb_xl = player->dynabomb_xl;
3741 player->dynabombs_left++;
3744 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3746 for (i = 0; i < NUM_DIRECTIONS; i++)
3748 for (j = 1; j <= dynabomb_size; j++)
3750 int x = ex + j * xy[i][0];
3751 int y = ey + j * xy[i][1];
3754 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3757 element = Feld[x][y];
3759 /* do not restart explosions of fields with active bombs */
3760 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3763 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3767 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3768 !IS_DIGGABLE(element) && !dynabomb_xl)
3771 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3772 !CAN_GROW_INTO(element) && !dynabomb_xl)
3776 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3777 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3778 element != EL_SAND && !dynabomb_xl)
3785 void Bang(int x, int y)
3788 int element = MovingOrBlocked2Element(x, y);
3790 int element = Feld[x][y];
3794 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3796 if (IS_PLAYER(x, y))
3799 struct PlayerInfo *player = PLAYERINFO(x, y);
3801 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3802 player->element_nr);
3807 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3809 if (game.emulation == EMU_SUPAPLEX)
3810 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3812 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3817 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3825 case EL_BD_BUTTERFLY:
3828 case EL_DARK_YAMYAM:
3832 RaiseScoreElement(element);
3833 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3835 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3836 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3837 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3838 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3839 case EL_DYNABOMB_INCREASE_NUMBER:
3840 case EL_DYNABOMB_INCREASE_SIZE:
3841 case EL_DYNABOMB_INCREASE_POWER:
3846 case EL_LAMP_ACTIVE:
3848 case EL_AMOEBA_TO_DIAMOND:
3850 if (IS_PLAYER(x, y))
3851 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3853 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3857 if (element_info[element].explosion_type == EXPLODES_CROSS)
3859 if (CAN_EXPLODE_CROSS(element))
3862 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3867 else if (element_info[element].explosion_type == EXPLODES_1X1)
3869 else if (CAN_EXPLODE_1X1(element))
3871 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3873 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3877 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3880 void SplashAcid(int x, int y)
3883 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3884 (!IN_LEV_FIELD(x - 1, y - 2) ||
3885 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3886 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3888 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3889 (!IN_LEV_FIELD(x + 1, y - 2) ||
3890 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3891 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3893 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3895 /* input: position of element entering acid (obsolete) */
3897 int element = Feld[x][y];
3899 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3902 if (element != EL_ACID_SPLASH_LEFT &&
3903 element != EL_ACID_SPLASH_RIGHT)
3905 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3907 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3908 (!IN_LEV_FIELD(x - 1, y - 1) ||
3909 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3910 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3912 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3913 (!IN_LEV_FIELD(x + 1, y - 1) ||
3914 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3915 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3920 static void InitBeltMovement()
3922 static int belt_base_element[4] =
3924 EL_CONVEYOR_BELT_1_LEFT,
3925 EL_CONVEYOR_BELT_2_LEFT,
3926 EL_CONVEYOR_BELT_3_LEFT,
3927 EL_CONVEYOR_BELT_4_LEFT
3929 static int belt_base_active_element[4] =
3931 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3932 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3933 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3934 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3939 /* set frame order for belt animation graphic according to belt direction */
3940 for (i = 0; i < NUM_BELTS; i++)
3944 for (j = 0; j < NUM_BELT_PARTS; j++)
3946 int element = belt_base_active_element[belt_nr] + j;
3947 int graphic = el2img(element);
3949 if (game.belt_dir[i] == MV_LEFT)
3950 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3952 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3956 for (y = 0; y < lev_fieldy; y++)
3958 for (x = 0; x < lev_fieldx; x++)
3960 int element = Feld[x][y];
3962 for (i = 0; i < NUM_BELTS; i++)
3964 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3966 int e_belt_nr = getBeltNrFromBeltElement(element);
3969 if (e_belt_nr == belt_nr)
3971 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3973 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3981 static void ToggleBeltSwitch(int x, int y)
3983 static int belt_base_element[4] =
3985 EL_CONVEYOR_BELT_1_LEFT,
3986 EL_CONVEYOR_BELT_2_LEFT,
3987 EL_CONVEYOR_BELT_3_LEFT,
3988 EL_CONVEYOR_BELT_4_LEFT
3990 static int belt_base_active_element[4] =
3992 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3993 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3994 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3995 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3997 static int belt_base_switch_element[4] =
3999 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4000 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4001 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4002 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4004 static int belt_move_dir[4] =
4012 int element = Feld[x][y];
4013 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4014 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4015 int belt_dir = belt_move_dir[belt_dir_nr];
4018 if (!IS_BELT_SWITCH(element))
4021 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4022 game.belt_dir[belt_nr] = belt_dir;
4024 if (belt_dir_nr == 3)
4027 /* set frame order for belt animation graphic according to belt direction */
4028 for (i = 0; i < NUM_BELT_PARTS; i++)
4030 int element = belt_base_active_element[belt_nr] + i;
4031 int graphic = el2img(element);
4033 if (belt_dir == MV_LEFT)
4034 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4036 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4039 for (yy = 0; yy < lev_fieldy; yy++)
4041 for (xx = 0; xx < lev_fieldx; xx++)
4043 int element = Feld[xx][yy];
4045 if (IS_BELT_SWITCH(element))
4047 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4049 if (e_belt_nr == belt_nr)
4051 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4052 DrawLevelField(xx, yy);
4055 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4057 int e_belt_nr = getBeltNrFromBeltElement(element);
4059 if (e_belt_nr == belt_nr)
4061 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4063 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4064 DrawLevelField(xx, yy);
4067 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4069 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4071 if (e_belt_nr == belt_nr)
4073 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4075 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4076 DrawLevelField(xx, yy);
4083 static void ToggleSwitchgateSwitch(int x, int y)
4087 game.switchgate_pos = !game.switchgate_pos;
4089 for (yy = 0; yy < lev_fieldy; yy++)
4091 for (xx = 0; xx < lev_fieldx; xx++)
4093 int element = Feld[xx][yy];
4095 if (element == EL_SWITCHGATE_SWITCH_UP ||
4096 element == EL_SWITCHGATE_SWITCH_DOWN)
4098 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4099 DrawLevelField(xx, yy);
4101 else if (element == EL_SWITCHGATE_OPEN ||
4102 element == EL_SWITCHGATE_OPENING)
4104 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4106 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4108 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4111 else if (element == EL_SWITCHGATE_CLOSED ||
4112 element == EL_SWITCHGATE_CLOSING)
4114 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4116 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4118 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4125 static int getInvisibleActiveFromInvisibleElement(int element)
4127 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4128 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4129 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4133 static int getInvisibleFromInvisibleActiveElement(int element)
4135 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4136 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4137 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4141 static void RedrawAllLightSwitchesAndInvisibleElements()
4145 for (y = 0; y < lev_fieldy; y++)
4147 for (x = 0; x < lev_fieldx; x++)
4149 int element = Feld[x][y];
4151 if (element == EL_LIGHT_SWITCH &&
4152 game.light_time_left > 0)
4154 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4155 DrawLevelField(x, y);
4157 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4158 game.light_time_left == 0)
4160 Feld[x][y] = EL_LIGHT_SWITCH;
4161 DrawLevelField(x, y);
4163 else if (element == EL_INVISIBLE_STEELWALL ||
4164 element == EL_INVISIBLE_WALL ||
4165 element == EL_INVISIBLE_SAND)
4167 if (game.light_time_left > 0)
4168 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4170 DrawLevelField(x, y);
4172 /* uncrumble neighbour fields, if needed */
4173 if (element == EL_INVISIBLE_SAND)
4174 DrawLevelFieldCrumbledSandNeighbours(x, y);
4176 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4177 element == EL_INVISIBLE_WALL_ACTIVE ||
4178 element == EL_INVISIBLE_SAND_ACTIVE)
4180 if (game.light_time_left == 0)
4181 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4183 DrawLevelField(x, y);
4185 /* re-crumble neighbour fields, if needed */
4186 if (element == EL_INVISIBLE_SAND)
4187 DrawLevelFieldCrumbledSandNeighbours(x, y);
4193 static void ToggleLightSwitch(int x, int y)
4195 int element = Feld[x][y];
4197 game.light_time_left =
4198 (element == EL_LIGHT_SWITCH ?
4199 level.time_light * FRAMES_PER_SECOND : 0);
4201 RedrawAllLightSwitchesAndInvisibleElements();
4204 static void ActivateTimegateSwitch(int x, int y)
4208 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4210 for (yy = 0; yy < lev_fieldy; yy++)
4212 for (xx = 0; xx < lev_fieldx; xx++)
4214 int element = Feld[xx][yy];
4216 if (element == EL_TIMEGATE_CLOSED ||
4217 element == EL_TIMEGATE_CLOSING)
4219 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4220 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4224 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4226 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4227 DrawLevelField(xx, yy);
4234 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4237 inline static int getElementMoveStepsize(int x, int y)
4239 int element = Feld[x][y];
4240 int direction = MovDir[x][y];
4241 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4242 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4243 int horiz_move = (dx != 0);
4244 int sign = (horiz_move ? dx : dy);
4245 int step = sign * element_info[element].move_stepsize;
4247 /* special values for move stepsize for spring and things on conveyor belt */
4251 if (element == EL_SPRING)
4252 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4253 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4254 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4255 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4257 if (CAN_FALL(element) &&
4258 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4259 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4260 else if (element == EL_SPRING)
4261 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4268 void Impact(int x, int y)
4270 boolean lastline = (y == lev_fieldy-1);
4271 boolean object_hit = FALSE;
4272 boolean impact = (lastline || object_hit);
4273 int element = Feld[x][y];
4274 int smashed = EL_STEELWALL;
4276 if (!lastline) /* check if element below was hit */
4278 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4281 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4282 MovDir[x][y + 1] != MV_DOWN ||
4283 MovPos[x][y + 1] <= TILEY / 2));
4286 object_hit = !IS_FREE(x, y + 1);
4289 /* do not smash moving elements that left the smashed field in time */
4290 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4291 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4295 smashed = MovingOrBlocked2Element(x, y + 1);
4297 impact = (lastline || object_hit);
4300 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4302 SplashAcid(x, y + 1);
4306 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4307 /* only reset graphic animation if graphic really changes after impact */
4309 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4311 ResetGfxAnimation(x, y);
4312 DrawLevelField(x, y);
4315 if (impact && CAN_EXPLODE_IMPACT(element))
4320 else if (impact && element == EL_PEARL)
4322 ResetGfxAnimation(x, y);
4324 Feld[x][y] = EL_PEARL_BREAKING;
4325 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4328 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4330 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4335 if (impact && element == EL_AMOEBA_DROP)
4337 if (object_hit && IS_PLAYER(x, y + 1))
4338 KillHeroUnlessEnemyProtected(x, y + 1);
4339 else if (object_hit && smashed == EL_PENGUIN)
4343 Feld[x][y] = EL_AMOEBA_GROWING;
4344 Store[x][y] = EL_AMOEBA_WET;
4346 ResetRandomAnimationValue(x, y);
4351 if (object_hit) /* check which object was hit */
4353 if (CAN_PASS_MAGIC_WALL(element) &&
4354 (smashed == EL_MAGIC_WALL ||
4355 smashed == EL_BD_MAGIC_WALL))
4358 int activated_magic_wall =
4359 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4360 EL_BD_MAGIC_WALL_ACTIVE);
4362 /* activate magic wall / mill */
4363 for (yy = 0; yy < lev_fieldy; yy++)
4364 for (xx = 0; xx < lev_fieldx; xx++)
4365 if (Feld[xx][yy] == smashed)
4366 Feld[xx][yy] = activated_magic_wall;
4368 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4369 game.magic_wall_active = TRUE;
4371 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4372 SND_MAGIC_WALL_ACTIVATING :
4373 SND_BD_MAGIC_WALL_ACTIVATING));
4376 if (IS_PLAYER(x, y + 1))
4378 if (CAN_SMASH_PLAYER(element))
4380 KillHeroUnlessEnemyProtected(x, y + 1);
4384 else if (smashed == EL_PENGUIN)
4386 if (CAN_SMASH_PLAYER(element))
4392 else if (element == EL_BD_DIAMOND)
4394 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4400 else if (((element == EL_SP_INFOTRON ||
4401 element == EL_SP_ZONK) &&
4402 (smashed == EL_SP_SNIKSNAK ||
4403 smashed == EL_SP_ELECTRON ||
4404 smashed == EL_SP_DISK_ORANGE)) ||
4405 (element == EL_SP_INFOTRON &&
4406 smashed == EL_SP_DISK_YELLOW))
4412 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4418 else if (CAN_SMASH_EVERYTHING(element))
4420 if (IS_CLASSIC_ENEMY(smashed) ||
4421 CAN_EXPLODE_SMASHED(smashed))
4426 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4428 if (smashed == EL_LAMP ||
4429 smashed == EL_LAMP_ACTIVE)
4434 else if (smashed == EL_NUT)
4436 Feld[x][y + 1] = EL_NUT_BREAKING;
4437 PlayLevelSound(x, y, SND_NUT_BREAKING);
4438 RaiseScoreElement(EL_NUT);
4441 else if (smashed == EL_PEARL)
4443 ResetGfxAnimation(x, y);
4445 Feld[x][y + 1] = EL_PEARL_BREAKING;
4446 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4449 else if (smashed == EL_DIAMOND)
4451 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4452 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4455 else if (IS_BELT_SWITCH(smashed))
4457 ToggleBeltSwitch(x, y + 1);
4459 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4460 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4462 ToggleSwitchgateSwitch(x, y + 1);
4464 else if (smashed == EL_LIGHT_SWITCH ||
4465 smashed == EL_LIGHT_SWITCH_ACTIVE)
4467 ToggleLightSwitch(x, y + 1);
4472 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4475 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4478 /* !!! TEST ONLY !!! */
4479 CheckElementChangeBySide(x, y + 1, smashed, element,
4480 CE_SWITCHED, CH_SIDE_TOP);
4481 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4482 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4484 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4485 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4486 CheckElementChangeBySide(x, y + 1, smashed, element,
4487 CE_SWITCHED, CH_SIDE_TOP);
4493 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4498 /* play sound of magic wall / mill */
4500 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4501 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4503 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4504 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4505 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4506 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4511 /* play sound of object that hits the ground */
4512 if (lastline || object_hit)
4513 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4516 inline static void TurnRoundExt(int x, int y)
4528 { 0, 0 }, { 0, 0 }, { 0, 0 },
4533 int left, right, back;
4537 { MV_DOWN, MV_UP, MV_RIGHT },
4538 { MV_UP, MV_DOWN, MV_LEFT },
4540 { MV_LEFT, MV_RIGHT, MV_DOWN },
4544 { MV_RIGHT, MV_LEFT, MV_UP }
4547 int element = Feld[x][y];
4548 int move_pattern = element_info[element].move_pattern;
4550 int old_move_dir = MovDir[x][y];
4551 int left_dir = turn[old_move_dir].left;
4552 int right_dir = turn[old_move_dir].right;
4553 int back_dir = turn[old_move_dir].back;
4555 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4556 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4557 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4558 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4560 int left_x = x + left_dx, left_y = y + left_dy;
4561 int right_x = x + right_dx, right_y = y + right_dy;
4562 int move_x = x + move_dx, move_y = y + move_dy;
4566 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4568 TestIfBadThingTouchesOtherBadThing(x, y);
4570 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4571 MovDir[x][y] = right_dir;
4572 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4573 MovDir[x][y] = left_dir;
4575 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4577 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4581 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4582 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4584 TestIfBadThingTouchesOtherBadThing(x, y);
4586 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4587 MovDir[x][y] = left_dir;
4588 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4589 MovDir[x][y] = right_dir;
4591 if ((element == EL_SPACESHIP ||
4592 element == EL_SP_SNIKSNAK ||
4593 element == EL_SP_ELECTRON)
4594 && MovDir[x][y] != old_move_dir)
4596 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4600 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4602 TestIfBadThingTouchesOtherBadThing(x, y);
4604 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4605 MovDir[x][y] = left_dir;
4606 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4607 MovDir[x][y] = right_dir;
4609 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4611 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4614 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4616 TestIfBadThingTouchesOtherBadThing(x, y);
4618 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4619 MovDir[x][y] = left_dir;
4620 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4621 MovDir[x][y] = right_dir;
4623 if (MovDir[x][y] != old_move_dir)
4627 else if (element == EL_YAMYAM)
4629 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4630 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4632 if (can_turn_left && can_turn_right)
4633 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4634 else if (can_turn_left)
4635 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4636 else if (can_turn_right)
4637 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4639 MovDir[x][y] = back_dir;
4641 MovDelay[x][y] = 16 + 16 * RND(3);
4643 else if (element == EL_DARK_YAMYAM)
4645 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4647 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4650 if (can_turn_left && can_turn_right)
4651 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4652 else if (can_turn_left)
4653 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4654 else if (can_turn_right)
4655 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4657 MovDir[x][y] = back_dir;
4659 MovDelay[x][y] = 16 + 16 * RND(3);
4661 else if (element == EL_PACMAN)
4663 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4664 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4666 if (can_turn_left && can_turn_right)
4667 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4668 else if (can_turn_left)
4669 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4670 else if (can_turn_right)
4671 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4673 MovDir[x][y] = back_dir;
4675 MovDelay[x][y] = 6 + RND(40);
4677 else if (element == EL_PIG)
4679 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4680 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4681 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4682 boolean should_turn_left, should_turn_right, should_move_on;
4684 int rnd = RND(rnd_value);
4686 should_turn_left = (can_turn_left &&
4688 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4689 y + back_dy + left_dy)));
4690 should_turn_right = (can_turn_right &&
4692 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4693 y + back_dy + right_dy)));
4694 should_move_on = (can_move_on &&
4697 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4698 y + move_dy + left_dy) ||
4699 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4700 y + move_dy + right_dy)));
4702 if (should_turn_left || should_turn_right || should_move_on)
4704 if (should_turn_left && should_turn_right && should_move_on)
4705 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4706 rnd < 2 * rnd_value / 3 ? right_dir :
4708 else if (should_turn_left && should_turn_right)
4709 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4710 else if (should_turn_left && should_move_on)
4711 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4712 else if (should_turn_right && should_move_on)
4713 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4714 else if (should_turn_left)
4715 MovDir[x][y] = left_dir;
4716 else if (should_turn_right)
4717 MovDir[x][y] = right_dir;
4718 else if (should_move_on)
4719 MovDir[x][y] = old_move_dir;
4721 else if (can_move_on && rnd > rnd_value / 8)
4722 MovDir[x][y] = old_move_dir;
4723 else if (can_turn_left && can_turn_right)
4724 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4725 else if (can_turn_left && rnd > rnd_value / 8)
4726 MovDir[x][y] = left_dir;
4727 else if (can_turn_right && rnd > rnd_value/8)
4728 MovDir[x][y] = right_dir;
4730 MovDir[x][y] = back_dir;
4732 xx = x + move_xy[MovDir[x][y]].x;
4733 yy = y + move_xy[MovDir[x][y]].y;
4736 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4737 if (!IN_LEV_FIELD(xx, yy) ||
4738 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4739 MovDir[x][y] = old_move_dir;
4741 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4742 MovDir[x][y] = old_move_dir;
4747 else if (element == EL_DRAGON)
4749 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4750 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4751 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4753 int rnd = RND(rnd_value);
4756 if (FrameCounter < 1 && x == 0 && y == 29)
4757 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4760 if (can_move_on && rnd > rnd_value / 8)
4761 MovDir[x][y] = old_move_dir;
4762 else if (can_turn_left && can_turn_right)
4763 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4764 else if (can_turn_left && rnd > rnd_value / 8)
4765 MovDir[x][y] = left_dir;
4766 else if (can_turn_right && rnd > rnd_value / 8)
4767 MovDir[x][y] = right_dir;
4769 MovDir[x][y] = back_dir;
4771 xx = x + move_xy[MovDir[x][y]].x;
4772 yy = y + move_xy[MovDir[x][y]].y;
4775 if (FrameCounter < 1 && x == 0 && y == 29)
4776 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4777 xx, yy, Feld[xx][yy],
4782 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4783 MovDir[x][y] = old_move_dir;
4785 if (!IS_FREE(xx, yy))
4786 MovDir[x][y] = old_move_dir;
4790 if (FrameCounter < 1 && x == 0 && y == 29)
4791 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4796 else if (element == EL_MOLE)
4798 boolean can_move_on =
4799 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4800 IS_AMOEBOID(Feld[move_x][move_y]) ||
4801 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4804 boolean can_turn_left =
4805 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4806 IS_AMOEBOID(Feld[left_x][left_y])));
4808 boolean can_turn_right =
4809 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4810 IS_AMOEBOID(Feld[right_x][right_y])));
4812 if (can_turn_left && can_turn_right)
4813 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4814 else if (can_turn_left)
4815 MovDir[x][y] = left_dir;
4817 MovDir[x][y] = right_dir;
4820 if (MovDir[x][y] != old_move_dir)
4823 else if (element == EL_BALLOON)
4825 MovDir[x][y] = game.balloon_dir;
4828 else if (element == EL_SPRING)
4831 if (MovDir[x][y] & MV_HORIZONTAL &&
4832 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4833 MovDir[x][y] = MV_NO_MOVING;
4835 if (MovDir[x][y] & MV_HORIZONTAL &&
4836 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4837 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4838 MovDir[x][y] = MV_NO_MOVING;
4843 else if (element == EL_ROBOT ||
4844 element == EL_SATELLITE ||
4845 element == EL_PENGUIN)
4847 int attr_x = -1, attr_y = -1;
4858 for (i = 0; i < MAX_PLAYERS; i++)
4860 struct PlayerInfo *player = &stored_player[i];
4861 int jx = player->jx, jy = player->jy;
4863 if (!player->active)
4867 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4876 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4877 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4878 game.engine_version < VERSION_IDENT(3,1,0,0)))
4880 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4887 if (element == EL_PENGUIN)
4890 static int xy[4][2] =
4898 for (i = 0; i < NUM_DIRECTIONS; i++)
4900 int ex = x + xy[i][0];
4901 int ey = y + xy[i][1];
4903 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4912 MovDir[x][y] = MV_NO_MOVING;
4914 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4915 else if (attr_x > x)
4916 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4918 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4919 else if (attr_y > y)
4920 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4922 if (element == EL_ROBOT)
4926 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4927 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4928 Moving2Blocked(x, y, &newx, &newy);
4930 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4931 MovDelay[x][y] = 8 + 8 * !RND(3);
4933 MovDelay[x][y] = 16;
4935 else if (element == EL_PENGUIN)
4941 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4943 boolean first_horiz = RND(2);
4944 int new_move_dir = MovDir[x][y];
4947 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4948 Moving2Blocked(x, y, &newx, &newy);
4950 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4954 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4955 Moving2Blocked(x, y, &newx, &newy);
4957 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4960 MovDir[x][y] = old_move_dir;
4964 else /* (element == EL_SATELLITE) */
4970 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4972 boolean first_horiz = RND(2);
4973 int new_move_dir = MovDir[x][y];
4976 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4977 Moving2Blocked(x, y, &newx, &newy);
4979 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4983 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4984 Moving2Blocked(x, y, &newx, &newy);
4986 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4989 MovDir[x][y] = old_move_dir;
4994 else if (move_pattern == MV_TURNING_LEFT ||
4995 move_pattern == MV_TURNING_RIGHT ||
4996 move_pattern == MV_TURNING_LEFT_RIGHT ||
4997 move_pattern == MV_TURNING_RIGHT_LEFT ||
4998 move_pattern == MV_TURNING_RANDOM ||
4999 move_pattern == MV_ALL_DIRECTIONS)
5001 boolean can_turn_left =
5002 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5003 boolean can_turn_right =
5004 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5006 if (move_pattern == MV_TURNING_LEFT)
5007 MovDir[x][y] = left_dir;
5008 else if (move_pattern == MV_TURNING_RIGHT)
5009 MovDir[x][y] = right_dir;
5010 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5011 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5012 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5013 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5014 else if (move_pattern == MV_TURNING_RANDOM)
5015 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5016 can_turn_right && !can_turn_left ? right_dir :
5017 RND(2) ? left_dir : right_dir);
5018 else if (can_turn_left && can_turn_right)
5019 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5020 else if (can_turn_left)
5021 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5022 else if (can_turn_right)
5023 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5025 MovDir[x][y] = back_dir;
5027 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5029 else if (move_pattern == MV_HORIZONTAL ||
5030 move_pattern == MV_VERTICAL)
5032 if (move_pattern & old_move_dir)
5033 MovDir[x][y] = back_dir;
5034 else if (move_pattern == MV_HORIZONTAL)
5035 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5036 else if (move_pattern == MV_VERTICAL)
5037 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5039 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5041 else if (move_pattern & MV_ANY_DIRECTION)
5043 MovDir[x][y] = move_pattern;
5044 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5046 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5048 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5049 MovDir[x][y] = left_dir;
5050 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5051 MovDir[x][y] = right_dir;
5053 if (MovDir[x][y] != old_move_dir)
5054 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5056 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5058 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5059 MovDir[x][y] = right_dir;
5060 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5061 MovDir[x][y] = left_dir;
5063 if (MovDir[x][y] != old_move_dir)
5064 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5066 else if (move_pattern == MV_TOWARDS_PLAYER ||
5067 move_pattern == MV_AWAY_FROM_PLAYER)
5069 int attr_x = -1, attr_y = -1;
5071 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5082 for (i = 0; i < MAX_PLAYERS; i++)
5084 struct PlayerInfo *player = &stored_player[i];
5085 int jx = player->jx, jy = player->jy;
5087 if (!player->active)
5091 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5099 MovDir[x][y] = MV_NO_MOVING;
5101 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5102 else if (attr_x > x)
5103 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5105 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5106 else if (attr_y > y)
5107 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5109 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5111 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5113 boolean first_horiz = RND(2);
5114 int new_move_dir = MovDir[x][y];
5117 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5118 Moving2Blocked(x, y, &newx, &newy);
5120 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5124 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5125 Moving2Blocked(x, y, &newx, &newy);
5127 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5130 MovDir[x][y] = old_move_dir;
5133 else if (move_pattern == MV_WHEN_PUSHED ||
5134 move_pattern == MV_WHEN_DROPPED)
5136 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5137 MovDir[x][y] = MV_NO_MOVING;
5141 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5143 static int test_xy[7][2] =
5153 static int test_dir[7] =
5163 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5164 int move_preference = -1000000; /* start with very low preference */
5165 int new_move_dir = MV_NO_MOVING;
5166 int start_test = RND(4);
5169 for (i = 0; i < NUM_DIRECTIONS; i++)
5171 int move_dir = test_dir[start_test + i];
5172 int move_dir_preference;
5174 xx = x + test_xy[start_test + i][0];
5175 yy = y + test_xy[start_test + i][1];
5177 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5178 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5180 new_move_dir = move_dir;
5185 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5188 move_dir_preference = -1 * RunnerVisit[xx][yy];
5189 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5190 move_dir_preference = PlayerVisit[xx][yy];
5192 if (move_dir_preference > move_preference)
5194 /* prefer field that has not been visited for the longest time */
5195 move_preference = move_dir_preference;
5196 new_move_dir = move_dir;
5198 else if (move_dir_preference == move_preference &&
5199 move_dir == old_move_dir)
5201 /* prefer last direction when all directions are preferred equally */
5202 move_preference = move_dir_preference;
5203 new_move_dir = move_dir;
5207 MovDir[x][y] = new_move_dir;
5208 if (old_move_dir != new_move_dir)
5211 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5219 static void TurnRound(int x, int y)
5221 int direction = MovDir[x][y];
5224 GfxDir[x][y] = MovDir[x][y];
5230 GfxDir[x][y] = MovDir[x][y];
5233 if (direction != MovDir[x][y])
5238 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5241 GfxAction[x][y] = ACTION_WAITING;
5245 static boolean JustBeingPushed(int x, int y)
5249 for (i = 0; i < MAX_PLAYERS; i++)
5251 struct PlayerInfo *player = &stored_player[i];
5253 if (player->active && player->is_pushing && player->MovPos)
5255 int next_jx = player->jx + (player->jx - player->last_jx);
5256 int next_jy = player->jy + (player->jy - player->last_jy);
5258 if (x == next_jx && y == next_jy)
5266 void StartMoving(int x, int y)
5269 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5271 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5272 int element = Feld[x][y];
5278 if (MovDelay[x][y] == 0)
5279 GfxAction[x][y] = ACTION_DEFAULT;
5281 /* !!! this should be handled more generic (not only for mole) !!! */
5282 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5283 GfxAction[x][y] = ACTION_DEFAULT;
5286 if (CAN_FALL(element) && y < lev_fieldy - 1)
5288 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5289 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5290 if (JustBeingPushed(x, y))
5293 if (element == EL_QUICKSAND_FULL)
5295 if (IS_FREE(x, y + 1))
5297 InitMovingField(x, y, MV_DOWN);
5298 started_moving = TRUE;
5300 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5301 Store[x][y] = EL_ROCK;
5303 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5305 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5308 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5310 if (!MovDelay[x][y])
5311 MovDelay[x][y] = TILEY + 1;
5320 Feld[x][y] = EL_QUICKSAND_EMPTY;
5321 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5322 Store[x][y + 1] = Store[x][y];
5325 PlayLevelSoundAction(x, y, ACTION_FILLING);
5327 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5331 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5332 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5334 InitMovingField(x, y, MV_DOWN);
5335 started_moving = TRUE;
5337 Feld[x][y] = EL_QUICKSAND_FILLING;
5338 Store[x][y] = element;
5340 PlayLevelSoundAction(x, y, ACTION_FILLING);
5342 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5345 else if (element == EL_MAGIC_WALL_FULL)
5347 if (IS_FREE(x, y + 1))
5349 InitMovingField(x, y, MV_DOWN);
5350 started_moving = TRUE;
5352 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5353 Store[x][y] = EL_CHANGED(Store[x][y]);
5355 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5357 if (!MovDelay[x][y])
5358 MovDelay[x][y] = TILEY/4 + 1;
5367 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5368 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5369 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5373 else if (element == EL_BD_MAGIC_WALL_FULL)
5375 if (IS_FREE(x, y + 1))
5377 InitMovingField(x, y, MV_DOWN);
5378 started_moving = TRUE;
5380 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5381 Store[x][y] = EL_CHANGED2(Store[x][y]);
5383 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5385 if (!MovDelay[x][y])
5386 MovDelay[x][y] = TILEY/4 + 1;
5395 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5396 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5397 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5401 else if (CAN_PASS_MAGIC_WALL(element) &&
5402 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5403 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5405 InitMovingField(x, y, MV_DOWN);
5406 started_moving = TRUE;
5409 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5410 EL_BD_MAGIC_WALL_FILLING);
5411 Store[x][y] = element;
5414 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5416 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5419 SplashAcid(x, y + 1);
5421 InitMovingField(x, y, MV_DOWN);
5422 started_moving = TRUE;
5424 Store[x][y] = EL_ACID;
5426 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5427 GfxAction[x][y + 1] = ACTION_ACTIVE;
5431 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5432 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5434 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5435 CAN_SMASH(element) && WasJustFalling[x][y] &&
5436 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5438 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5439 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5440 (Feld[x][y + 1] == EL_BLOCKED)))
5444 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5445 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5446 WasJustMoving[x][y] && !Pushed[x][y + 1])
5448 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5449 WasJustMoving[x][y])
5454 /* this is needed for a special case not covered by calling "Impact()"
5455 from "ContinueMoving()": if an element moves to a tile directly below
5456 another element which was just falling on that tile (which was empty
5457 in the previous frame), the falling element above would just stop
5458 instead of smashing the element below (in previous version, the above
5459 element was just checked for "moving" instead of "falling", resulting
5460 in incorrect smashes caused by horizontal movement of the above
5461 element; also, the case of the player being the element to smash was
5462 simply not covered here... :-/ ) */
5465 WasJustMoving[x][y] = 0;
5466 WasJustFalling[x][y] = 0;
5469 CheckCollision[x][y] = 0;
5472 if (IS_PLAYER(x, y + 1))
5473 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5478 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5480 if (MovDir[x][y] == MV_NO_MOVING)
5482 InitMovingField(x, y, MV_DOWN);
5483 started_moving = TRUE;
5486 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5488 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5489 MovDir[x][y] = MV_DOWN;
5491 InitMovingField(x, y, MV_DOWN);
5492 started_moving = TRUE;
5494 else if (element == EL_AMOEBA_DROP)
5496 Feld[x][y] = EL_AMOEBA_GROWING;
5497 Store[x][y] = EL_AMOEBA_WET;
5499 /* Store[x][y + 1] must be zero, because:
5500 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5503 #if OLD_GAME_BEHAVIOUR
5504 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5506 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5507 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5508 element != EL_DX_SUPABOMB)
5511 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5512 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5513 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5514 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5517 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5518 (IS_FREE(x - 1, y + 1) ||
5519 Feld[x - 1][y + 1] == EL_ACID));
5520 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5521 (IS_FREE(x + 1, y + 1) ||
5522 Feld[x + 1][y + 1] == EL_ACID));
5523 boolean can_fall_any = (can_fall_left || can_fall_right);
5524 boolean can_fall_both = (can_fall_left && can_fall_right);
5526 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5528 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5530 if (slippery_type == SLIPPERY_ONLY_LEFT)
5531 can_fall_right = FALSE;
5532 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5533 can_fall_left = FALSE;
5534 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5535 can_fall_right = FALSE;
5536 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5537 can_fall_left = FALSE;
5539 can_fall_any = (can_fall_left || can_fall_right);
5540 can_fall_both = (can_fall_left && can_fall_right);
5543 #if USE_NEW_SP_SLIPPERY
5544 /* !!! better use the same properties as for custom elements here !!! */
5545 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5546 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5548 can_fall_right = FALSE; /* slip down on left side */
5549 can_fall_both = FALSE;
5556 if (game.emulation == EMU_BOULDERDASH ||
5557 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5558 can_fall_right = FALSE; /* slip down on left side */
5560 can_fall_left = !(can_fall_right = RND(2));
5562 can_fall_both = FALSE;
5569 if (can_fall_both &&
5570 (game.emulation != EMU_BOULDERDASH &&
5571 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5572 can_fall_left = !(can_fall_right = RND(2));
5575 /* if not determined otherwise, prefer left side for slipping down */
5576 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5577 started_moving = TRUE;
5581 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5583 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5586 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5587 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5588 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5589 int belt_dir = game.belt_dir[belt_nr];
5591 if ((belt_dir == MV_LEFT && left_is_free) ||
5592 (belt_dir == MV_RIGHT && right_is_free))
5595 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5598 InitMovingField(x, y, belt_dir);
5599 started_moving = TRUE;
5602 Pushed[x][y] = TRUE;
5603 Pushed[nextx][y] = TRUE;
5606 GfxAction[x][y] = ACTION_DEFAULT;
5610 MovDir[x][y] = 0; /* if element was moving, stop it */
5615 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5617 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5619 if (CAN_MOVE(element) && !started_moving)
5622 int move_pattern = element_info[element].move_pattern;
5627 if (MovDir[x][y] == MV_NO_MOVING)
5629 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5630 x, y, element, element_info[element].token_name);
5631 printf("StartMoving(): This should never happen!\n");
5636 Moving2Blocked(x, y, &newx, &newy);
5639 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5642 if ((element == EL_SATELLITE ||
5643 element == EL_BALLOON ||
5644 element == EL_SPRING)
5645 && JustBeingPushed(x, y))
5652 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5653 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5655 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5656 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5657 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5661 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5662 element, element_info[element].token_name,
5663 WasJustMoving[x][y],
5664 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5665 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5666 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5667 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5671 WasJustMoving[x][y] = 0;
5674 CheckCollision[x][y] = 0;
5676 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5679 if (Feld[x][y] != element) /* element has changed */
5681 element = Feld[x][y];
5682 move_pattern = element_info[element].move_pattern;
5684 if (!CAN_MOVE(element))
5688 if (Feld[x][y] != element) /* element has changed */
5696 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5697 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5699 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5701 Moving2Blocked(x, y, &newx, &newy);
5702 if (Feld[newx][newy] == EL_BLOCKED)
5703 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5709 if (FrameCounter < 1 && x == 0 && y == 29)
5710 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5713 if (!MovDelay[x][y]) /* start new movement phase */
5715 /* all objects that can change their move direction after each step
5716 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5718 if (element != EL_YAMYAM &&
5719 element != EL_DARK_YAMYAM &&
5720 element != EL_PACMAN &&
5721 !(move_pattern & MV_ANY_DIRECTION) &&
5722 move_pattern != MV_TURNING_LEFT &&
5723 move_pattern != MV_TURNING_RIGHT &&
5724 move_pattern != MV_TURNING_LEFT_RIGHT &&
5725 move_pattern != MV_TURNING_RIGHT_LEFT &&
5726 move_pattern != MV_TURNING_RANDOM)
5731 if (FrameCounter < 1 && x == 0 && y == 29)
5732 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5735 if (MovDelay[x][y] && (element == EL_BUG ||
5736 element == EL_SPACESHIP ||
5737 element == EL_SP_SNIKSNAK ||
5738 element == EL_SP_ELECTRON ||
5739 element == EL_MOLE))
5740 DrawLevelField(x, y);
5744 if (MovDelay[x][y]) /* wait some time before next movement */
5749 if (element == EL_YAMYAM)
5752 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5753 DrawLevelElementAnimation(x, y, element);
5757 if (MovDelay[x][y]) /* element still has to wait some time */
5760 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5761 ResetGfxAnimation(x, y);
5765 if (GfxAction[x][y] != ACTION_WAITING)
5766 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5768 GfxAction[x][y] = ACTION_WAITING;
5772 if (element == EL_ROBOT ||
5774 element == EL_PACMAN ||
5776 element == EL_YAMYAM ||
5777 element == EL_DARK_YAMYAM)
5780 DrawLevelElementAnimation(x, y, element);
5782 DrawLevelElementAnimationIfNeeded(x, y, element);
5784 PlayLevelSoundAction(x, y, ACTION_WAITING);
5786 else if (element == EL_SP_ELECTRON)
5787 DrawLevelElementAnimationIfNeeded(x, y, element);
5788 else if (element == EL_DRAGON)
5791 int dir = MovDir[x][y];
5792 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5793 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5794 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5795 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5796 dir == MV_UP ? IMG_FLAMES_1_UP :
5797 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5798 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5801 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5804 GfxAction[x][y] = ACTION_ATTACKING;
5806 if (IS_PLAYER(x, y))
5807 DrawPlayerField(x, y);
5809 DrawLevelField(x, y);
5811 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5813 for (i = 1; i <= 3; i++)
5815 int xx = x + i * dx;
5816 int yy = y + i * dy;
5817 int sx = SCREENX(xx);
5818 int sy = SCREENY(yy);
5819 int flame_graphic = graphic + (i - 1);
5821 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5826 int flamed = MovingOrBlocked2Element(xx, yy);
5830 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5832 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5833 RemoveMovingField(xx, yy);
5835 RemoveField(xx, yy);
5837 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5840 RemoveMovingField(xx, yy);
5844 if (ChangeDelay[xx][yy])
5845 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5846 Feld[xx][yy] == EL_BLOCKED));
5850 ChangeDelay[xx][yy] = 0;
5852 Feld[xx][yy] = EL_FLAMES;
5853 if (IN_SCR_FIELD(sx, sy))
5855 DrawLevelFieldCrumbledSand(xx, yy);
5856 DrawGraphic(sx, sy, flame_graphic, frame);
5861 if (Feld[xx][yy] == EL_FLAMES)
5862 Feld[xx][yy] = EL_EMPTY;
5863 DrawLevelField(xx, yy);
5868 if (MovDelay[x][y]) /* element still has to wait some time */
5870 PlayLevelSoundAction(x, y, ACTION_WAITING);
5876 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5877 for all other elements GfxAction will be set by InitMovingField() */
5878 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5879 GfxAction[x][y] = ACTION_MOVING;
5883 /* now make next step */
5885 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5887 if (DONT_COLLIDE_WITH(element) &&
5888 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5889 !PLAYER_ENEMY_PROTECTED(newx, newy))
5892 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5896 /* player killed by element which is deadly when colliding with */
5898 KillHero(PLAYERINFO(newx, newy));
5905 else if (CAN_MOVE_INTO_ACID(element) &&
5906 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5907 (MovDir[x][y] == MV_DOWN ||
5908 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5910 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5911 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5915 else if ((element == EL_PENGUIN ||
5916 element == EL_ROBOT ||
5917 element == EL_SATELLITE ||
5918 element == EL_BALLOON ||
5919 IS_CUSTOM_ELEMENT(element)) &&
5920 IN_LEV_FIELD(newx, newy) &&
5921 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5924 SplashAcid(newx, newy);
5925 Store[x][y] = EL_ACID;
5927 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5929 if (Feld[newx][newy] == EL_EXIT_OPEN)
5933 DrawLevelField(x, y);
5935 Feld[x][y] = EL_EMPTY;
5936 DrawLevelField(x, y);
5939 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5940 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5941 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5943 local_player->friends_still_needed--;
5944 if (!local_player->friends_still_needed &&
5945 !local_player->GameOver && AllPlayersGone)
5946 local_player->LevelSolved = local_player->GameOver = TRUE;
5950 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5952 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5953 DrawLevelField(newx, newy);
5955 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5957 else if (!IS_FREE(newx, newy))
5959 GfxAction[x][y] = ACTION_WAITING;
5961 if (IS_PLAYER(x, y))
5962 DrawPlayerField(x, y);
5964 DrawLevelField(x, y);
5969 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5971 if (IS_FOOD_PIG(Feld[newx][newy]))
5973 if (IS_MOVING(newx, newy))
5974 RemoveMovingField(newx, newy);
5977 Feld[newx][newy] = EL_EMPTY;
5978 DrawLevelField(newx, newy);
5981 PlayLevelSound(x, y, SND_PIG_DIGGING);
5983 else if (!IS_FREE(newx, newy))
5985 if (IS_PLAYER(x, y))
5986 DrawPlayerField(x, y);
5988 DrawLevelField(x, y);
5997 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6000 else if (IS_CUSTOM_ELEMENT(element) &&
6001 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6005 !IS_FREE(newx, newy)
6010 int new_element = Feld[newx][newy];
6013 printf("::: '%s' digs '%s' [%d]\n",
6014 element_info[element].token_name,
6015 element_info[Feld[newx][newy]].token_name,
6016 StorePlayer[newx][newy]);
6019 if (!IS_FREE(newx, newy))
6021 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6022 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6025 /* no element can dig solid indestructible elements */
6026 if (IS_INDESTRUCTIBLE(new_element) &&
6027 !IS_DIGGABLE(new_element) &&
6028 !IS_COLLECTIBLE(new_element))
6031 if (AmoebaNr[newx][newy] &&
6032 (new_element == EL_AMOEBA_FULL ||
6033 new_element == EL_BD_AMOEBA ||
6034 new_element == EL_AMOEBA_GROWING))
6036 AmoebaCnt[AmoebaNr[newx][newy]]--;
6037 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6040 if (IS_MOVING(newx, newy))
6041 RemoveMovingField(newx, newy);
6044 RemoveField(newx, newy);
6045 DrawLevelField(newx, newy);
6048 /* if digged element was about to explode, prevent the explosion */
6049 ExplodeField[newx][newy] = EX_TYPE_NONE;
6051 PlayLevelSoundAction(x, y, action);
6056 Store[newx][newy] = EL_EMPTY;
6057 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6058 Store[newx][newy] = element_info[element].move_leave_element;
6060 Store[newx][newy] = EL_EMPTY;
6061 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6062 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6063 Store[newx][newy] = element_info[element].move_leave_element;
6066 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6067 element_info[element].can_leave_element = TRUE;
6070 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6072 RunnerVisit[x][y] = FrameCounter;
6073 PlayerVisit[x][y] /= 8; /* expire player visit path */
6079 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6081 if (!IS_FREE(newx, newy))
6083 if (IS_PLAYER(x, y))
6084 DrawPlayerField(x, y);
6086 DrawLevelField(x, y);
6092 boolean wanna_flame = !RND(10);
6093 int dx = newx - x, dy = newy - y;
6094 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6095 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6096 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6097 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6098 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6099 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6102 IS_CLASSIC_ENEMY(element1) ||
6103 IS_CLASSIC_ENEMY(element2)) &&
6104 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6105 element1 != EL_FLAMES && element2 != EL_FLAMES)
6108 ResetGfxAnimation(x, y);
6109 GfxAction[x][y] = ACTION_ATTACKING;
6112 if (IS_PLAYER(x, y))
6113 DrawPlayerField(x, y);
6115 DrawLevelField(x, y);
6117 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6119 MovDelay[x][y] = 50;
6123 RemoveField(newx, newy);
6125 Feld[newx][newy] = EL_FLAMES;
6126 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6129 RemoveField(newx1, newy1);
6131 Feld[newx1][newy1] = EL_FLAMES;
6133 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6136 RemoveField(newx2, newy2);
6138 Feld[newx2][newy2] = EL_FLAMES;
6145 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6146 Feld[newx][newy] == EL_DIAMOND)
6148 if (IS_MOVING(newx, newy))
6149 RemoveMovingField(newx, newy);
6152 Feld[newx][newy] = EL_EMPTY;
6153 DrawLevelField(newx, newy);
6156 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6158 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6159 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6161 if (AmoebaNr[newx][newy])
6163 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6164 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6165 Feld[newx][newy] == EL_BD_AMOEBA)
6166 AmoebaCnt[AmoebaNr[newx][newy]]--;
6171 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6173 if (IS_MOVING(newx, newy))
6176 RemoveMovingField(newx, newy);
6180 Feld[newx][newy] = EL_EMPTY;
6181 DrawLevelField(newx, newy);
6184 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6186 else if ((element == EL_PACMAN || element == EL_MOLE)
6187 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6189 if (AmoebaNr[newx][newy])
6191 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6192 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6193 Feld[newx][newy] == EL_BD_AMOEBA)
6194 AmoebaCnt[AmoebaNr[newx][newy]]--;
6197 if (element == EL_MOLE)
6199 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6200 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6202 ResetGfxAnimation(x, y);
6203 GfxAction[x][y] = ACTION_DIGGING;
6204 DrawLevelField(x, y);
6206 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6208 return; /* wait for shrinking amoeba */
6210 else /* element == EL_PACMAN */
6212 Feld[newx][newy] = EL_EMPTY;
6213 DrawLevelField(newx, newy);
6214 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6217 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6218 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6219 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6221 /* wait for shrinking amoeba to completely disappear */
6224 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6226 /* object was running against a wall */
6231 if (move_pattern & MV_ANY_DIRECTION &&
6232 move_pattern == MovDir[x][y])
6234 int blocking_element =
6235 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6238 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6239 element_info[element].token_name,
6240 element_info[blocking_element].token_name,
6244 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6247 element = Feld[x][y]; /* element might have changed */
6252 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6253 DrawLevelElementAnimation(x, y, element);
6255 if (element == EL_BUG ||
6256 element == EL_SPACESHIP ||
6257 element == EL_SP_SNIKSNAK)
6258 DrawLevelField(x, y);
6259 else if (element == EL_MOLE)
6260 DrawLevelField(x, y);
6261 else if (element == EL_BD_BUTTERFLY ||
6262 element == EL_BD_FIREFLY)
6263 DrawLevelElementAnimationIfNeeded(x, y, element);
6264 else if (element == EL_SATELLITE)
6265 DrawLevelElementAnimationIfNeeded(x, y, element);
6266 else if (element == EL_SP_ELECTRON)
6267 DrawLevelElementAnimationIfNeeded(x, y, element);
6270 if (DONT_TOUCH(element))
6271 TestIfBadThingTouchesHero(x, y);
6274 PlayLevelSoundAction(x, y, ACTION_WAITING);
6280 InitMovingField(x, y, MovDir[x][y]);
6282 PlayLevelSoundAction(x, y, ACTION_MOVING);
6286 ContinueMoving(x, y);
6289 void ContinueMoving(int x, int y)
6291 int element = Feld[x][y];
6292 int stored = Store[x][y];
6293 struct ElementInfo *ei = &element_info[element];
6294 int direction = MovDir[x][y];
6295 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6296 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6297 int newx = x + dx, newy = y + dy;
6299 int nextx = newx + dx, nexty = newy + dy;
6302 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6303 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6305 boolean pushed_by_player = Pushed[x][y];
6308 MovPos[x][y] += getElementMoveStepsize(x, y);
6311 if (pushed_by_player && IS_PLAYER(x, y))
6313 /* special case: moving object pushed by player */
6314 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6317 if (pushed_by_player) /* special case: moving object pushed by player */
6318 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6321 if (ABS(MovPos[x][y]) < TILEX)
6323 DrawLevelField(x, y);
6325 return; /* element is still moving */
6328 /* element reached destination field */
6330 Feld[x][y] = EL_EMPTY;
6331 Feld[newx][newy] = element;
6332 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6335 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6337 element = Feld[newx][newy] = EL_ACID;
6340 else if (element == EL_MOLE)
6342 Feld[x][y] = EL_SAND;
6344 DrawLevelFieldCrumbledSandNeighbours(x, y);
6346 else if (element == EL_QUICKSAND_FILLING)
6348 element = Feld[newx][newy] = get_next_element(element);
6349 Store[newx][newy] = Store[x][y];
6351 else if (element == EL_QUICKSAND_EMPTYING)
6353 Feld[x][y] = get_next_element(element);
6354 element = Feld[newx][newy] = Store[x][y];
6356 else if (element == EL_MAGIC_WALL_FILLING)
6358 element = Feld[newx][newy] = get_next_element(element);
6359 if (!game.magic_wall_active)
6360 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6361 Store[newx][newy] = Store[x][y];
6363 else if (element == EL_MAGIC_WALL_EMPTYING)
6365 Feld[x][y] = get_next_element(element);
6366 if (!game.magic_wall_active)
6367 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6368 element = Feld[newx][newy] = Store[x][y];
6370 else if (element == EL_BD_MAGIC_WALL_FILLING)
6372 element = Feld[newx][newy] = get_next_element(element);
6373 if (!game.magic_wall_active)
6374 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6375 Store[newx][newy] = Store[x][y];
6377 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6379 Feld[x][y] = get_next_element(element);
6380 if (!game.magic_wall_active)
6381 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6382 element = Feld[newx][newy] = Store[x][y];
6384 else if (element == EL_AMOEBA_DROPPING)
6386 Feld[x][y] = get_next_element(element);
6387 element = Feld[newx][newy] = Store[x][y];
6389 else if (element == EL_SOKOBAN_OBJECT)
6392 Feld[x][y] = Back[x][y];
6394 if (Back[newx][newy])
6395 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6397 Back[x][y] = Back[newx][newy] = 0;
6400 else if (Store[x][y] == EL_ACID)
6402 element = Feld[newx][newy] = EL_ACID;
6406 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6407 ei->move_leave_element != EL_EMPTY &&
6408 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6409 Store[x][y] != EL_EMPTY))
6411 /* some elements can leave other elements behind after moving */
6413 Feld[x][y] = ei->move_leave_element;
6414 InitField(x, y, FALSE);
6416 if (GFX_CRUMBLED(Feld[x][y]))
6417 DrawLevelFieldCrumbledSandNeighbours(x, y);
6421 Store[x][y] = EL_EMPTY;
6422 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6423 MovDelay[newx][newy] = 0;
6425 if (CAN_CHANGE(element))
6427 /* copy element change control values to new field */
6428 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6429 ChangePage[newx][newy] = ChangePage[x][y];
6430 Changed[newx][newy] = Changed[x][y];
6431 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6434 ChangeDelay[x][y] = 0;
6435 ChangePage[x][y] = -1;
6436 Changed[x][y] = CE_BITMASK_DEFAULT;
6437 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6439 /* copy animation control values to new field */
6440 GfxFrame[newx][newy] = GfxFrame[x][y];
6441 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6442 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6443 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6445 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6447 ResetGfxAnimation(x, y); /* reset animation values for old field */
6450 /* some elements can leave other elements behind after moving */
6452 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6453 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6454 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6456 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6457 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6461 int move_leave_element = ei->move_leave_element;
6463 Feld[x][y] = move_leave_element;
6464 InitField(x, y, FALSE);
6466 if (GFX_CRUMBLED(Feld[x][y]))
6467 DrawLevelFieldCrumbledSandNeighbours(x, y);
6469 if (ELEM_IS_PLAYER(move_leave_element))
6470 RelocatePlayer(x, y, move_leave_element);
6475 /* some elements can leave other elements behind after moving */
6476 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6477 ei->move_leave_element != EL_EMPTY &&
6478 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6479 ei->can_leave_element_last))
6481 Feld[x][y] = ei->move_leave_element;
6482 InitField(x, y, FALSE);
6484 if (GFX_CRUMBLED(Feld[x][y]))
6485 DrawLevelFieldCrumbledSandNeighbours(x, y);
6488 ei->can_leave_element_last = ei->can_leave_element;
6489 ei->can_leave_element = FALSE;
6493 /* 2.1.1 (does not work correctly for spring) */
6494 if (!CAN_MOVE(element))
6495 MovDir[newx][newy] = 0;
6499 /* (does not work for falling objects that slide horizontally) */
6500 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6501 MovDir[newx][newy] = 0;
6504 if (!CAN_MOVE(element) ||
6505 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6506 MovDir[newx][newy] = 0;
6510 if (!CAN_MOVE(element) ||
6511 (CAN_FALL(element) && direction == MV_DOWN))
6512 GfxDir[x][y] = MovDir[newx][newy] = 0;
6514 if (!CAN_MOVE(element) ||
6515 (CAN_FALL(element) && direction == MV_DOWN &&
6516 (element == EL_SPRING ||
6517 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6518 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6519 GfxDir[x][y] = MovDir[newx][newy] = 0;
6525 DrawLevelField(x, y);
6526 DrawLevelField(newx, newy);
6528 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6530 /* prevent pushed element from moving on in pushed direction */
6531 if (pushed_by_player && CAN_MOVE(element) &&
6532 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6533 !(element_info[element].move_pattern & direction))
6534 TurnRound(newx, newy);
6537 /* prevent elements on conveyor belt from moving on in last direction */
6538 if (pushed_by_conveyor && CAN_FALL(element) &&
6539 direction & MV_HORIZONTAL)
6542 if (CAN_MOVE(element))
6543 InitMovDir(newx, newy);
6545 MovDir[newx][newy] = 0;
6547 MovDir[newx][newy] = 0;
6552 if (!pushed_by_player)
6554 int nextx = newx + dx, nexty = newy + dy;
6555 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6557 WasJustMoving[newx][newy] = 3;
6559 if (CAN_FALL(element) && direction == MV_DOWN)
6560 WasJustFalling[newx][newy] = 3;
6562 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6563 CheckCollision[newx][newy] = 2;
6566 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6568 TestIfBadThingTouchesHero(newx, newy);
6569 TestIfBadThingTouchesFriend(newx, newy);
6571 if (!IS_CUSTOM_ELEMENT(element))
6572 TestIfBadThingTouchesOtherBadThing(newx, newy);
6574 else if (element == EL_PENGUIN)
6575 TestIfFriendTouchesBadThing(newx, newy);
6577 #if USE_NEW_MOVE_STYLE
6579 if (CAN_FALL(element) && direction == MV_DOWN &&
6580 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6581 IS_PLAYER(x, newy + 1))
6582 printf("::: we would now kill the player [%d]\n", FrameCounter);
6585 /* give the player one last chance (one more frame) to move away */
6586 if (CAN_FALL(element) && direction == MV_DOWN &&
6587 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6588 (!IS_PLAYER(x, newy + 1) ||
6589 game.engine_version < VERSION_IDENT(3,1,1,0)))
6592 if (CAN_FALL(element) && direction == MV_DOWN &&
6593 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6601 if (pushed_by_player && !game.use_bug_change_when_pushing)
6603 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6606 if (pushed_by_player)
6611 int dig_side = MV_DIR_OPPOSITE(direction);
6613 static int trigger_sides[4] =
6615 CH_SIDE_RIGHT, /* moving left */
6616 CH_SIDE_LEFT, /* moving right */
6617 CH_SIDE_BOTTOM, /* moving up */
6618 CH_SIDE_TOP, /* moving down */
6620 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6622 struct PlayerInfo *player = PLAYERINFO(x, y);
6624 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6625 player->index_bit, dig_side);
6626 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6627 player->index_bit, dig_side);
6632 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6636 if (ChangePage[newx][newy] != -1) /* delayed change */
6637 ChangeElement(newx, newy, ChangePage[newx][newy]);
6642 TestIfElementHitsCustomElement(newx, newy, direction);
6646 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6648 int hitting_element = Feld[newx][newy];
6650 /* !!! fix side (direction) orientation here and elsewhere !!! */
6651 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6655 if (IN_LEV_FIELD(nextx, nexty))
6657 int opposite_direction = MV_DIR_OPPOSITE(direction);
6658 int hitting_side = direction;
6659 int touched_side = opposite_direction;
6660 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6661 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6662 MovDir[nextx][nexty] != direction ||
6663 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6669 CheckElementChangeBySide(nextx, nexty, touched_element,
6670 CE_HIT_BY_SOMETHING, opposite_direction);
6672 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6673 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6675 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6677 struct ElementChangeInfo *change =
6678 &element_info[hitting_element].change_page[i];
6680 if (change->can_change &&
6681 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6682 change->trigger_side & touched_side &&
6683 change->trigger_element == touched_element)
6685 CheckElementChangeByPage(newx, newy, hitting_element,
6686 touched_element, CE_OTHER_IS_HITTING,i);
6692 if (IS_CUSTOM_ELEMENT(touched_element) &&
6693 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6695 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6697 struct ElementChangeInfo *change =
6698 &element_info[touched_element].change_page[i];
6700 if (change->can_change &&
6701 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6702 change->trigger_side & hitting_side &&
6703 change->trigger_element == hitting_element)
6705 CheckElementChangeByPage(nextx, nexty, touched_element,
6706 hitting_element, CE_OTHER_GETS_HIT, i);
6717 TestIfPlayerTouchesCustomElement(newx, newy);
6718 TestIfElementTouchesCustomElement(newx, newy);
6721 int AmoebeNachbarNr(int ax, int ay)
6724 int element = Feld[ax][ay];
6726 static int xy[4][2] =
6734 for (i = 0; i < NUM_DIRECTIONS; i++)
6736 int x = ax + xy[i][0];
6737 int y = ay + xy[i][1];
6739 if (!IN_LEV_FIELD(x, y))
6742 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6743 group_nr = AmoebaNr[x][y];
6749 void AmoebenVereinigen(int ax, int ay)
6751 int i, x, y, xx, yy;
6752 int new_group_nr = AmoebaNr[ax][ay];
6753 static int xy[4][2] =
6761 if (new_group_nr == 0)
6764 for (i = 0; i < NUM_DIRECTIONS; i++)
6769 if (!IN_LEV_FIELD(x, y))
6772 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6773 Feld[x][y] == EL_BD_AMOEBA ||
6774 Feld[x][y] == EL_AMOEBA_DEAD) &&
6775 AmoebaNr[x][y] != new_group_nr)
6777 int old_group_nr = AmoebaNr[x][y];
6779 if (old_group_nr == 0)
6782 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6783 AmoebaCnt[old_group_nr] = 0;
6784 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6785 AmoebaCnt2[old_group_nr] = 0;
6787 for (yy = 0; yy < lev_fieldy; yy++)
6789 for (xx = 0; xx < lev_fieldx; xx++)
6791 if (AmoebaNr[xx][yy] == old_group_nr)
6792 AmoebaNr[xx][yy] = new_group_nr;
6799 void AmoebeUmwandeln(int ax, int ay)
6803 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6805 int group_nr = AmoebaNr[ax][ay];
6810 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6811 printf("AmoebeUmwandeln(): This should never happen!\n");
6816 for (y = 0; y < lev_fieldy; y++)
6818 for (x = 0; x < lev_fieldx; x++)
6820 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6823 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6827 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6828 SND_AMOEBA_TURNING_TO_GEM :
6829 SND_AMOEBA_TURNING_TO_ROCK));
6834 static int xy[4][2] =
6842 for (i = 0; i < NUM_DIRECTIONS; i++)
6847 if (!IN_LEV_FIELD(x, y))
6850 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6852 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6853 SND_AMOEBA_TURNING_TO_GEM :
6854 SND_AMOEBA_TURNING_TO_ROCK));
6861 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6864 int group_nr = AmoebaNr[ax][ay];
6865 boolean done = FALSE;
6870 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6871 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6876 for (y = 0; y < lev_fieldy; y++)
6878 for (x = 0; x < lev_fieldx; x++)
6880 if (AmoebaNr[x][y] == group_nr &&
6881 (Feld[x][y] == EL_AMOEBA_DEAD ||
6882 Feld[x][y] == EL_BD_AMOEBA ||
6883 Feld[x][y] == EL_AMOEBA_GROWING))
6886 Feld[x][y] = new_element;
6887 InitField(x, y, FALSE);
6888 DrawLevelField(x, y);
6895 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6896 SND_BD_AMOEBA_TURNING_TO_ROCK :
6897 SND_BD_AMOEBA_TURNING_TO_GEM));
6900 void AmoebeWaechst(int x, int y)
6902 static unsigned long sound_delay = 0;
6903 static unsigned long sound_delay_value = 0;
6905 if (!MovDelay[x][y]) /* start new growing cycle */
6909 if (DelayReached(&sound_delay, sound_delay_value))
6912 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6914 if (Store[x][y] == EL_BD_AMOEBA)
6915 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6917 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6919 sound_delay_value = 30;
6923 if (MovDelay[x][y]) /* wait some time before growing bigger */
6926 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6928 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6929 6 - MovDelay[x][y]);
6931 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6934 if (!MovDelay[x][y])
6936 Feld[x][y] = Store[x][y];
6938 DrawLevelField(x, y);
6943 void AmoebaDisappearing(int x, int y)
6945 static unsigned long sound_delay = 0;
6946 static unsigned long sound_delay_value = 0;
6948 if (!MovDelay[x][y]) /* start new shrinking cycle */
6952 if (DelayReached(&sound_delay, sound_delay_value))
6953 sound_delay_value = 30;
6956 if (MovDelay[x][y]) /* wait some time before shrinking */
6959 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6961 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6962 6 - MovDelay[x][y]);
6964 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6967 if (!MovDelay[x][y])
6969 Feld[x][y] = EL_EMPTY;
6970 DrawLevelField(x, y);
6972 /* don't let mole enter this field in this cycle;
6973 (give priority to objects falling to this field from above) */
6979 void AmoebeAbleger(int ax, int ay)
6982 int element = Feld[ax][ay];
6983 int graphic = el2img(element);
6984 int newax = ax, neway = ay;
6985 static int xy[4][2] =
6993 if (!level.amoeba_speed)
6995 Feld[ax][ay] = EL_AMOEBA_DEAD;
6996 DrawLevelField(ax, ay);
7000 if (IS_ANIMATED(graphic))
7001 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7003 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7004 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7006 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7009 if (MovDelay[ax][ay])
7013 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7016 int x = ax + xy[start][0];
7017 int y = ay + xy[start][1];
7019 if (!IN_LEV_FIELD(x, y))
7023 if (IS_FREE(x, y) ||
7024 CAN_GROW_INTO(Feld[x][y]) ||
7025 Feld[x][y] == EL_QUICKSAND_EMPTY)
7031 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7032 if (IS_FREE(x, y) ||
7033 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7040 if (newax == ax && neway == ay)
7043 else /* normal or "filled" (BD style) amoeba */
7046 boolean waiting_for_player = FALSE;
7048 for (i = 0; i < NUM_DIRECTIONS; i++)
7050 int j = (start + i) % 4;
7051 int x = ax + xy[j][0];
7052 int y = ay + xy[j][1];
7054 if (!IN_LEV_FIELD(x, y))
7058 if (IS_FREE(x, y) ||
7059 CAN_GROW_INTO(Feld[x][y]) ||
7060 Feld[x][y] == EL_QUICKSAND_EMPTY)
7067 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7068 if (IS_FREE(x, y) ||
7069 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7076 else if (IS_PLAYER(x, y))
7077 waiting_for_player = TRUE;
7080 if (newax == ax && neway == ay) /* amoeba cannot grow */
7083 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7085 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7088 Feld[ax][ay] = EL_AMOEBA_DEAD;
7089 DrawLevelField(ax, ay);
7090 AmoebaCnt[AmoebaNr[ax][ay]]--;
7092 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7094 if (element == EL_AMOEBA_FULL)
7095 AmoebeUmwandeln(ax, ay);
7096 else if (element == EL_BD_AMOEBA)
7097 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7102 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7104 /* amoeba gets larger by growing in some direction */
7106 int new_group_nr = AmoebaNr[ax][ay];
7109 if (new_group_nr == 0)
7111 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7112 printf("AmoebeAbleger(): This should never happen!\n");
7117 AmoebaNr[newax][neway] = new_group_nr;
7118 AmoebaCnt[new_group_nr]++;
7119 AmoebaCnt2[new_group_nr]++;
7121 /* if amoeba touches other amoeba(s) after growing, unify them */
7122 AmoebenVereinigen(newax, neway);
7124 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7126 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7132 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7133 (neway == lev_fieldy - 1 && newax != ax))
7135 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7136 Store[newax][neway] = element;
7138 else if (neway == ay)
7140 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7142 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7144 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7149 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7150 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7151 Store[ax][ay] = EL_AMOEBA_DROP;
7152 ContinueMoving(ax, ay);
7156 DrawLevelField(newax, neway);
7159 void Life(int ax, int ay)
7162 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7164 int element = Feld[ax][ay];
7165 int graphic = el2img(element);
7166 boolean changed = FALSE;
7168 if (IS_ANIMATED(graphic))
7169 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7174 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7175 MovDelay[ax][ay] = life_time;
7177 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7180 if (MovDelay[ax][ay])
7184 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7186 int xx = ax+x1, yy = ay+y1;
7189 if (!IN_LEV_FIELD(xx, yy))
7192 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7194 int x = xx+x2, y = yy+y2;
7196 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7199 if (((Feld[x][y] == element ||
7200 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7202 (IS_FREE(x, y) && Stop[x][y]))
7206 if (xx == ax && yy == ay) /* field in the middle */
7208 if (nachbarn < life[0] || nachbarn > life[1])
7210 Feld[xx][yy] = EL_EMPTY;
7212 DrawLevelField(xx, yy);
7213 Stop[xx][yy] = TRUE;
7218 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7219 { /* free border field */
7220 if (nachbarn >= life[2] && nachbarn <= life[3])
7222 Feld[xx][yy] = element;
7223 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7225 DrawLevelField(xx, yy);
7226 Stop[xx][yy] = TRUE;
7231 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7232 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7233 { /* free border field */
7234 if (nachbarn >= life[2] && nachbarn <= life[3])
7236 Feld[xx][yy] = element;
7237 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7239 DrawLevelField(xx, yy);
7240 Stop[xx][yy] = TRUE;
7248 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7249 SND_GAME_OF_LIFE_GROWING);
7252 static void InitRobotWheel(int x, int y)
7254 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7257 static void RunRobotWheel(int x, int y)
7259 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7262 static void StopRobotWheel(int x, int y)
7264 if (ZX == x && ZY == y)
7268 static void InitTimegateWheel(int x, int y)
7271 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7273 /* another brainless, "type style" bug ... :-( */
7274 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7278 static void RunTimegateWheel(int x, int y)
7280 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7283 void CheckExit(int x, int y)
7285 if (local_player->gems_still_needed > 0 ||
7286 local_player->sokobanfields_still_needed > 0 ||
7287 local_player->lights_still_needed > 0)
7289 int element = Feld[x][y];
7290 int graphic = el2img(element);
7292 if (IS_ANIMATED(graphic))
7293 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7298 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7301 Feld[x][y] = EL_EXIT_OPENING;
7303 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7306 void CheckExitSP(int x, int y)
7308 if (local_player->gems_still_needed > 0)
7310 int element = Feld[x][y];
7311 int graphic = el2img(element);
7313 if (IS_ANIMATED(graphic))
7314 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7319 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7322 Feld[x][y] = EL_SP_EXIT_OPENING;
7324 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7327 static void CloseAllOpenTimegates()
7331 for (y = 0; y < lev_fieldy; y++)
7333 for (x = 0; x < lev_fieldx; x++)
7335 int element = Feld[x][y];
7337 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7339 Feld[x][y] = EL_TIMEGATE_CLOSING;
7341 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7343 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7350 void EdelsteinFunkeln(int x, int y)
7352 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7355 if (Feld[x][y] == EL_BD_DIAMOND)
7358 if (MovDelay[x][y] == 0) /* next animation frame */
7359 MovDelay[x][y] = 11 * !SimpleRND(500);
7361 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7365 if (setup.direct_draw && MovDelay[x][y])
7366 SetDrawtoField(DRAW_BUFFERED);
7368 DrawLevelElementAnimation(x, y, Feld[x][y]);
7370 if (MovDelay[x][y] != 0)
7372 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7373 10 - MovDelay[x][y]);
7375 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7377 if (setup.direct_draw)
7381 dest_x = FX + SCREENX(x) * TILEX;
7382 dest_y = FY + SCREENY(y) * TILEY;
7384 BlitBitmap(drawto_field, window,
7385 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7386 SetDrawtoField(DRAW_DIRECT);
7392 void MauerWaechst(int x, int y)
7396 if (!MovDelay[x][y]) /* next animation frame */
7397 MovDelay[x][y] = 3 * delay;
7399 if (MovDelay[x][y]) /* wait some time before next frame */
7403 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7405 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7406 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7408 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7411 if (!MovDelay[x][y])
7413 if (MovDir[x][y] == MV_LEFT)
7415 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7416 DrawLevelField(x - 1, y);
7418 else if (MovDir[x][y] == MV_RIGHT)
7420 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7421 DrawLevelField(x + 1, y);
7423 else if (MovDir[x][y] == MV_UP)
7425 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7426 DrawLevelField(x, y - 1);
7430 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7431 DrawLevelField(x, y + 1);
7434 Feld[x][y] = Store[x][y];
7436 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7437 DrawLevelField(x, y);
7442 void MauerAbleger(int ax, int ay)
7444 int element = Feld[ax][ay];
7445 int graphic = el2img(element);
7446 boolean oben_frei = FALSE, unten_frei = FALSE;
7447 boolean links_frei = FALSE, rechts_frei = FALSE;
7448 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7449 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7450 boolean new_wall = FALSE;
7452 if (IS_ANIMATED(graphic))
7453 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7455 if (!MovDelay[ax][ay]) /* start building new wall */
7456 MovDelay[ax][ay] = 6;
7458 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7461 if (MovDelay[ax][ay])
7465 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7467 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7469 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7471 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7474 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7475 element == EL_EXPANDABLE_WALL_ANY)
7479 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7480 Store[ax][ay-1] = element;
7481 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7482 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7483 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7484 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7489 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7490 Store[ax][ay+1] = element;
7491 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7492 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7493 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7494 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7499 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7500 element == EL_EXPANDABLE_WALL_ANY ||
7501 element == EL_EXPANDABLE_WALL)
7505 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7506 Store[ax-1][ay] = element;
7507 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7508 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7509 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7510 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7516 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7517 Store[ax+1][ay] = element;
7518 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7519 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7520 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7521 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7526 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7527 DrawLevelField(ax, ay);
7529 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7531 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7532 unten_massiv = TRUE;
7533 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7534 links_massiv = TRUE;
7535 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7536 rechts_massiv = TRUE;
7538 if (((oben_massiv && unten_massiv) ||
7539 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7540 element == EL_EXPANDABLE_WALL) &&
7541 ((links_massiv && rechts_massiv) ||
7542 element == EL_EXPANDABLE_WALL_VERTICAL))
7543 Feld[ax][ay] = EL_WALL;
7547 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7549 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7553 void CheckForDragon(int x, int y)
7556 boolean dragon_found = FALSE;
7557 static int xy[4][2] =
7565 for (i = 0; i < NUM_DIRECTIONS; i++)
7567 for (j = 0; j < 4; j++)
7569 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7571 if (IN_LEV_FIELD(xx, yy) &&
7572 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7574 if (Feld[xx][yy] == EL_DRAGON)
7575 dragon_found = TRUE;
7584 for (i = 0; i < NUM_DIRECTIONS; i++)
7586 for (j = 0; j < 3; j++)
7588 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7590 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7592 Feld[xx][yy] = EL_EMPTY;
7593 DrawLevelField(xx, yy);
7602 static void InitBuggyBase(int x, int y)
7604 int element = Feld[x][y];
7605 int activating_delay = FRAMES_PER_SECOND / 4;
7608 (element == EL_SP_BUGGY_BASE ?
7609 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7610 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7612 element == EL_SP_BUGGY_BASE_ACTIVE ?
7613 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7616 static void WarnBuggyBase(int x, int y)
7619 static int xy[4][2] =
7627 for (i = 0; i < NUM_DIRECTIONS; i++)
7629 int xx = x + xy[i][0], yy = y + xy[i][1];
7631 if (IS_PLAYER(xx, yy))
7633 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7640 static void InitTrap(int x, int y)
7642 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7645 static void ActivateTrap(int x, int y)
7647 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7650 static void ChangeActiveTrap(int x, int y)
7652 int graphic = IMG_TRAP_ACTIVE;
7654 /* if new animation frame was drawn, correct crumbled sand border */
7655 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7656 DrawLevelFieldCrumbledSand(x, y);
7659 static void ChangeElementNowExt(int x, int y, int target_element)
7661 int previous_move_direction = MovDir[x][y];
7663 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7664 IS_WALKABLE(Feld[x][y]));
7666 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7667 IS_WALKABLE(Feld[x][y]) &&
7671 /* check if element under player changes from accessible to unaccessible
7672 (needed for special case of dropping element which then changes) */
7673 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7674 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7677 printf("::: BOOOM! [%d, '%s']\n", target_element,
7678 element_info[target_element].token_name);
7690 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7691 RemoveMovingField(x, y);
7695 Feld[x][y] = target_element;
7698 Feld[x][y] = target_element;
7701 ResetGfxAnimation(x, y);
7702 ResetRandomAnimationValue(x, y);
7704 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7705 MovDir[x][y] = previous_move_direction;
7708 InitField_WithBug1(x, y, FALSE);
7710 InitField(x, y, FALSE);
7711 if (CAN_MOVE(Feld[x][y]))
7715 DrawLevelField(x, y);
7717 if (GFX_CRUMBLED(Feld[x][y]))
7718 DrawLevelFieldCrumbledSandNeighbours(x, y);
7722 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7726 TestIfBadThingTouchesHero(x, y);
7727 TestIfPlayerTouchesCustomElement(x, y);
7728 TestIfElementTouchesCustomElement(x, y);
7731 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7732 if (ELEM_IS_PLAYER(target_element))
7733 RelocatePlayer(x, y, target_element);
7736 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7740 TestIfBadThingTouchesHero(x, y);
7741 TestIfPlayerTouchesCustomElement(x, y);
7742 TestIfElementTouchesCustomElement(x, y);
7746 static boolean ChangeElementNow(int x, int y, int element, int page)
7748 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7750 int old_element = Feld[x][y];
7752 /* always use default change event to prevent running into a loop */
7753 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7754 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7756 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7758 /* reset actual trigger element and player */
7759 change->actual_trigger_element = EL_EMPTY;
7760 change->actual_trigger_player = EL_PLAYER_1;
7763 /* do not change already changed elements with same change event */
7765 if (Changed[x][y] & ChangeEvent[x][y])
7772 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7775 /* !!! indirect change before direct change !!! */
7776 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7779 if (change->explode)
7786 if (change->use_target_content)
7788 boolean complete_replace = TRUE;
7789 boolean can_replace[3][3];
7792 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7795 boolean is_walkable;
7796 boolean is_diggable;
7797 boolean is_collectible;
7798 boolean is_removable;
7799 boolean is_destructible;
7800 int ex = x + xx - 1;
7801 int ey = y + yy - 1;
7802 int content_element = change->target_content[xx][yy];
7805 can_replace[xx][yy] = TRUE;
7807 if (ex == x && ey == y) /* do not check changing element itself */
7810 if (content_element == EL_EMPTY_SPACE)
7812 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7817 if (!IN_LEV_FIELD(ex, ey))
7819 can_replace[xx][yy] = FALSE;
7820 complete_replace = FALSE;
7826 if (Changed[ex][ey]) /* do not change already changed elements */
7828 can_replace[xx][yy] = FALSE;
7829 complete_replace = FALSE;
7837 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7838 e = MovingOrBlocked2Element(ex, ey);
7843 is_empty = (IS_FREE(ex, ey) ||
7844 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7845 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7846 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7850 is_empty = (IS_FREE(ex, ey) ||
7851 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7853 is_empty = (IS_FREE(ex, ey) ||
7854 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7859 is_walkable = (is_empty || IS_WALKABLE(e));
7860 is_diggable = (is_empty || IS_DIGGABLE(e));
7861 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7862 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7863 is_removable = (is_diggable || is_collectible);
7865 can_replace[xx][yy] =
7866 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7867 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7868 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7869 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7870 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7871 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7872 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7874 if (!can_replace[xx][yy])
7875 complete_replace = FALSE;
7877 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7878 IS_WALKABLE(content_element)));
7880 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7882 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7885 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7886 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7887 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7889 can_replace[xx][yy] = FALSE;
7890 complete_replace = FALSE;
7895 if (!change->only_if_complete || complete_replace)
7897 boolean something_has_changed = FALSE;
7899 if (change->only_if_complete && change->use_random_replace &&
7900 RND(100) < change->random_percentage)
7903 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7905 int ex = x + xx - 1;
7906 int ey = y + yy - 1;
7907 int content_element;
7909 if (can_replace[xx][yy] && (!change->use_random_replace ||
7910 RND(100) < change->random_percentage))
7912 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7913 RemoveMovingField(ex, ey);
7915 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7917 content_element = change->target_content[xx][yy];
7918 target_element = GET_TARGET_ELEMENT(content_element, change);
7920 ChangeElementNowExt(ex, ey, target_element);
7922 something_has_changed = TRUE;
7924 /* for symmetry reasons, freeze newly created border elements */
7925 if (ex != x || ey != y)
7926 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7930 if (something_has_changed)
7931 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7936 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7938 ChangeElementNowExt(x, y, target_element);
7940 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7944 /* this uses direct change before indirect change */
7945 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7951 static void ChangeElement(int x, int y, int page)
7953 int element = MovingOrBlocked2Element(x, y);
7954 struct ElementInfo *ei = &element_info[element];
7955 struct ElementChangeInfo *change = &ei->change_page[page];
7958 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7961 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7962 x, y, element, element_info[element].token_name);
7963 printf("ChangeElement(): This should never happen!\n");
7968 /* this can happen with classic bombs on walkable, changing elements */
7969 if (!CAN_CHANGE(element))
7972 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7973 ChangeDelay[x][y] = 0;
7979 if (ChangeDelay[x][y] == 0) /* initialize element change */
7981 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7982 RND(change->delay_random * change->delay_frames)) + 1;
7984 ResetGfxAnimation(x, y);
7985 ResetRandomAnimationValue(x, y);
7987 if (change->pre_change_function)
7988 change->pre_change_function(x, y);
7991 ChangeDelay[x][y]--;
7993 if (ChangeDelay[x][y] != 0) /* continue element change */
7995 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7997 if (IS_ANIMATED(graphic))
7998 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8000 if (change->change_function)
8001 change->change_function(x, y);
8003 else /* finish element change */
8005 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8007 page = ChangePage[x][y];
8008 ChangePage[x][y] = -1;
8010 change = &ei->change_page[page];
8014 if (IS_MOVING(x, y) && !change->explode)
8016 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8019 ChangeDelay[x][y] = 1; /* try change after next move step */
8020 ChangePage[x][y] = page; /* remember page to use for change */
8025 if (ChangeElementNow(x, y, element, page))
8027 if (change->post_change_function)
8028 change->post_change_function(x, y);
8033 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8034 int trigger_element,
8041 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8043 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8046 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8048 int element = EL_CUSTOM_START + i;
8050 boolean change_element = FALSE;
8053 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8056 for (j = 0; j < element_info[element].num_change_pages; j++)
8058 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8060 if (change->can_change &&
8061 change->events & CH_EVENT_BIT(trigger_event) &&
8062 change->trigger_side & trigger_side &&
8063 change->trigger_player & trigger_player &&
8064 change->trigger_page & trigger_page_bits &&
8065 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8068 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8069 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8070 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8073 change_element = TRUE;
8076 change->actual_trigger_element = trigger_element;
8077 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8083 if (!change_element)
8086 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8089 if (x == lx && y == ly) /* do not change trigger element itself */
8093 if (Feld[x][y] == element)
8095 ChangeDelay[x][y] = 1;
8096 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8097 ChangeElement(x, y, page);
8105 static boolean CheckElementChangeExt(int x, int y,
8107 int trigger_element,
8113 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8116 if (Feld[x][y] == EL_BLOCKED)
8118 Blocked2Moving(x, y, &x, &y);
8119 element = Feld[x][y];
8123 if (Feld[x][y] != element) /* check if element has already changed */
8126 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8127 Feld[x][y], element_info[Feld[x][y]].token_name,
8128 element, element_info[element].token_name,
8137 if (trigger_page < 0)
8139 boolean change_element = FALSE;
8142 for (i = 0; i < element_info[element].num_change_pages; i++)
8144 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8146 if (change->can_change &&
8147 change->events & CH_EVENT_BIT(trigger_event) &&
8148 change->trigger_side & trigger_side &&
8149 change->trigger_player & trigger_player)
8151 change_element = TRUE;
8154 change->actual_trigger_element = trigger_element;
8155 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8161 if (!change_element)
8166 struct ElementInfo *ei = &element_info[element];
8167 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8169 change->actual_trigger_element = trigger_element;
8170 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8175 /* !!! this check misses pages with same event, but different side !!! */
8177 if (trigger_page < 0)
8178 trigger_page = element_info[element].event_page_nr[trigger_event];
8180 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8184 ChangeDelay[x][y] = 1;
8185 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8186 ChangeElement(x, y, trigger_page);
8191 static void PlayPlayerSound(struct PlayerInfo *player)
8193 int jx = player->jx, jy = player->jy;
8194 int element = player->element_nr;
8195 int last_action = player->last_action_waiting;
8196 int action = player->action_waiting;
8198 if (player->is_waiting)
8200 if (action != last_action)
8201 PlayLevelSoundElementAction(jx, jy, element, action);
8203 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8207 if (action != last_action)
8208 StopSound(element_info[element].sound[last_action]);
8210 if (last_action == ACTION_SLEEPING)
8211 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8215 static void PlayAllPlayersSound()
8219 for (i = 0; i < MAX_PLAYERS; i++)
8220 if (stored_player[i].active)
8221 PlayPlayerSound(&stored_player[i]);
8224 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8226 boolean last_waiting = player->is_waiting;
8227 int move_dir = player->MovDir;
8229 player->last_action_waiting = player->action_waiting;
8233 if (!last_waiting) /* not waiting -> waiting */
8235 player->is_waiting = TRUE;
8237 player->frame_counter_bored =
8239 game.player_boring_delay_fixed +
8240 SimpleRND(game.player_boring_delay_random);
8241 player->frame_counter_sleeping =
8243 game.player_sleeping_delay_fixed +
8244 SimpleRND(game.player_sleeping_delay_random);
8246 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8249 if (game.player_sleeping_delay_fixed +
8250 game.player_sleeping_delay_random > 0 &&
8251 player->anim_delay_counter == 0 &&
8252 player->post_delay_counter == 0 &&
8253 FrameCounter >= player->frame_counter_sleeping)
8254 player->is_sleeping = TRUE;
8255 else if (game.player_boring_delay_fixed +
8256 game.player_boring_delay_random > 0 &&
8257 FrameCounter >= player->frame_counter_bored)
8258 player->is_bored = TRUE;
8260 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8261 player->is_bored ? ACTION_BORING :
8264 if (player->is_sleeping)
8266 if (player->num_special_action_sleeping > 0)
8268 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8270 int last_special_action = player->special_action_sleeping;
8271 int num_special_action = player->num_special_action_sleeping;
8272 int special_action =
8273 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8274 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8275 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8276 last_special_action + 1 : ACTION_SLEEPING);
8277 int special_graphic =
8278 el_act_dir2img(player->element_nr, special_action, move_dir);
8280 player->anim_delay_counter =
8281 graphic_info[special_graphic].anim_delay_fixed +
8282 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8283 player->post_delay_counter =
8284 graphic_info[special_graphic].post_delay_fixed +
8285 SimpleRND(graphic_info[special_graphic].post_delay_random);
8287 player->special_action_sleeping = special_action;
8290 if (player->anim_delay_counter > 0)
8292 player->action_waiting = player->special_action_sleeping;
8293 player->anim_delay_counter--;
8295 else if (player->post_delay_counter > 0)
8297 player->post_delay_counter--;
8301 else if (player->is_bored)
8303 if (player->num_special_action_bored > 0)
8305 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8307 int special_action =
8308 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8309 int special_graphic =
8310 el_act_dir2img(player->element_nr, special_action, move_dir);
8312 player->anim_delay_counter =
8313 graphic_info[special_graphic].anim_delay_fixed +
8314 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8315 player->post_delay_counter =
8316 graphic_info[special_graphic].post_delay_fixed +
8317 SimpleRND(graphic_info[special_graphic].post_delay_random);
8319 player->special_action_bored = special_action;
8322 if (player->anim_delay_counter > 0)
8324 player->action_waiting = player->special_action_bored;
8325 player->anim_delay_counter--;
8327 else if (player->post_delay_counter > 0)
8329 player->post_delay_counter--;
8334 else if (last_waiting) /* waiting -> not waiting */
8336 player->is_waiting = FALSE;
8337 player->is_bored = FALSE;
8338 player->is_sleeping = FALSE;
8340 player->frame_counter_bored = -1;
8341 player->frame_counter_sleeping = -1;
8343 player->anim_delay_counter = 0;
8344 player->post_delay_counter = 0;
8346 player->action_waiting = ACTION_DEFAULT;
8348 player->special_action_bored = ACTION_DEFAULT;
8349 player->special_action_sleeping = ACTION_DEFAULT;
8354 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8357 static byte stored_player_action[MAX_PLAYERS];
8358 static int num_stored_actions = 0;
8360 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8361 int left = player_action & JOY_LEFT;
8362 int right = player_action & JOY_RIGHT;
8363 int up = player_action & JOY_UP;
8364 int down = player_action & JOY_DOWN;
8365 int button1 = player_action & JOY_BUTTON_1;
8366 int button2 = player_action & JOY_BUTTON_2;
8367 int dx = (left ? -1 : right ? 1 : 0);
8368 int dy = (up ? -1 : down ? 1 : 0);
8371 stored_player_action[player->index_nr] = 0;
8372 num_stored_actions++;
8376 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8379 if (!player->active || tape.pausing)
8383 printf("::: [%d %d %d %d] [%d %d]\n",
8384 left, right, up, down, button1, button2);
8390 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8395 if (player->MovPos == 0)
8396 CheckGravityMovement(player);
8399 snapped = SnapField(player, dx, dy);
8403 dropped = DropElement(player);
8405 moved = MovePlayer(player, dx, dy);
8408 if (tape.single_step && tape.recording && !tape.pausing)
8410 if (button1 || (dropped && !moved))
8412 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8413 SnapField(player, 0, 0); /* stop snapping */
8417 SetPlayerWaiting(player, FALSE);
8420 return player_action;
8422 stored_player_action[player->index_nr] = player_action;
8428 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8431 /* no actions for this player (no input at player's configured device) */
8433 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8434 SnapField(player, 0, 0);
8435 CheckGravityMovementWhenNotMoving(player);
8437 if (player->MovPos == 0)
8438 SetPlayerWaiting(player, TRUE);
8440 if (player->MovPos == 0) /* needed for tape.playing */
8441 player->is_moving = FALSE;
8443 player->is_dropping = FALSE;
8449 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8451 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8453 TapeRecordAction(stored_player_action);
8454 num_stored_actions = 0;
8461 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8463 static byte stored_player_action[MAX_PLAYERS];
8464 static int num_stored_actions = 0;
8465 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8466 int left = player_action & JOY_LEFT;
8467 int right = player_action & JOY_RIGHT;
8468 int up = player_action & JOY_UP;
8469 int down = player_action & JOY_DOWN;
8470 int button1 = player_action & JOY_BUTTON_1;
8471 int button2 = player_action & JOY_BUTTON_2;
8472 int dx = (left ? -1 : right ? 1 : 0);
8473 int dy = (up ? -1 : down ? 1 : 0);
8475 stored_player_action[player->index_nr] = 0;
8476 num_stored_actions++;
8478 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8480 if (!player->active || tape.pausing)
8485 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8488 snapped = SnapField(player, dx, dy);
8492 dropped = DropElement(player);
8494 moved = MovePlayer(player, dx, dy);
8497 if (tape.single_step && tape.recording && !tape.pausing)
8499 if (button1 || (dropped && !moved))
8501 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8502 SnapField(player, 0, 0); /* stop snapping */
8506 stored_player_action[player->index_nr] = player_action;
8510 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8512 /* no actions for this player (no input at player's configured device) */
8514 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8515 SnapField(player, 0, 0);
8516 CheckGravityMovementWhenNotMoving(player);
8518 if (player->MovPos == 0)
8519 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8521 if (player->MovPos == 0) /* needed for tape.playing */
8522 player->is_moving = FALSE;
8525 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8527 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8529 TapeRecordAction(stored_player_action);
8530 num_stored_actions = 0;
8535 void AdvanceFrameAndPlayerCounters(int player_nr)
8539 /* advance frame counters (global frame counter and time frame counter) */
8543 /* advance player counters (counters for move delay, move animation etc.) */
8544 for (i = 0; i < MAX_PLAYERS; i++)
8546 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8548 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8550 if (!advance_player_counters) /* not all players may be affected */
8553 stored_player[i].Frame += move_frames;
8555 if (stored_player[i].MovPos != 0)
8556 stored_player[i].StepFrame += move_frames;
8558 #if USE_NEW_MOVE_DELAY
8559 if (stored_player[i].move_delay > 0)
8560 stored_player[i].move_delay--;
8563 #if USE_NEW_PUSH_DELAY
8564 /* due to bugs in previous versions, counter must count up, not down */
8565 if (stored_player[i].push_delay != -1)
8566 stored_player[i].push_delay++;
8569 if (stored_player[i].drop_delay > 0)
8570 stored_player[i].drop_delay--;
8576 static unsigned long game_frame_delay = 0;
8577 unsigned long game_frame_delay_value;
8578 int magic_wall_x = 0, magic_wall_y = 0;
8579 int i, x, y, element, graphic;
8580 byte *recorded_player_action;
8581 byte summarized_player_action = 0;
8583 byte tape_action[MAX_PLAYERS];
8586 if (game_status != GAME_MODE_PLAYING)
8589 game_frame_delay_value =
8590 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8592 if (tape.playing && tape.warp_forward && !tape.pausing)
8593 game_frame_delay_value = 0;
8595 /* ---------- main game synchronization point ---------- */
8597 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8599 if (network_playing && !network_player_action_received)
8603 printf("DEBUG: try to get network player actions in time\n");
8607 #if defined(NETWORK_AVALIABLE)
8608 /* last chance to get network player actions without main loop delay */
8612 if (game_status != GAME_MODE_PLAYING)
8615 if (!network_player_action_received)
8619 printf("DEBUG: failed to get network player actions in time\n");
8630 printf("::: getting new tape action [%d]\n", FrameCounter);
8633 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8636 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8637 if (recorded_player_action == NULL && tape.pausing)
8642 printf("::: %d\n", stored_player[0].action);
8646 if (recorded_player_action != NULL)
8647 for (i = 0; i < MAX_PLAYERS; i++)
8648 stored_player[i].action = recorded_player_action[i];
8651 for (i = 0; i < MAX_PLAYERS; i++)
8653 summarized_player_action |= stored_player[i].action;
8655 if (!network_playing)
8656 stored_player[i].effective_action = stored_player[i].action;
8659 #if defined(NETWORK_AVALIABLE)
8660 if (network_playing)
8661 SendToServer_MovePlayer(summarized_player_action);
8664 if (!options.network && !setup.team_mode)
8665 local_player->effective_action = summarized_player_action;
8668 if (recorded_player_action != NULL)
8669 for (i = 0; i < MAX_PLAYERS; i++)
8670 stored_player[i].effective_action = recorded_player_action[i];
8674 for (i = 0; i < MAX_PLAYERS; i++)
8676 tape_action[i] = stored_player[i].effective_action;
8678 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8679 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8682 /* only save actions from input devices, but not programmed actions */
8684 TapeRecordAction(tape_action);
8687 for (i = 0; i < MAX_PLAYERS; i++)
8689 int actual_player_action = stored_player[i].effective_action;
8692 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8693 - rnd_equinox_tetrachloride 048
8694 - rnd_equinox_tetrachloride_ii 096
8695 - rnd_emanuel_schmieg 002
8696 - doctor_sloan_ww 001, 020
8698 if (stored_player[i].MovPos == 0)
8699 CheckGravityMovement(&stored_player[i]);
8703 /* overwrite programmed action with tape action */
8704 if (stored_player[i].programmed_action)
8705 actual_player_action = stored_player[i].programmed_action;
8709 if (stored_player[i].programmed_action)
8710 printf("::: %d\n", stored_player[i].programmed_action);
8713 if (recorded_player_action)
8716 if (stored_player[i].programmed_action &&
8717 stored_player[i].programmed_action != recorded_player_action[i])
8718 printf("::: %d: %d <-> %d\n", i,
8719 stored_player[i].programmed_action, recorded_player_action[i]);
8723 actual_player_action = recorded_player_action[i];
8728 /* overwrite tape action with programmed action */
8729 if (stored_player[i].programmed_action)
8730 actual_player_action = stored_player[i].programmed_action;
8735 printf("::: action: %d: %x [%d]\n",
8736 stored_player[i].MovPos, actual_player_action, FrameCounter);
8740 PlayerActions(&stored_player[i], actual_player_action);
8742 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8744 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8745 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8748 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8753 TapeRecordAction(tape_action);
8756 network_player_action_received = FALSE;
8758 ScrollScreen(NULL, SCROLL_GO_ON);
8764 for (i = 0; i < MAX_PLAYERS; i++)
8765 stored_player[i].Frame++;
8769 /* for backwards compatibility, the following code emulates a fixed bug that
8770 occured when pushing elements (causing elements that just made their last
8771 pushing step to already (if possible) make their first falling step in the
8772 same game frame, which is bad); this code is also needed to use the famous
8773 "spring push bug" which is used in older levels and might be wanted to be
8774 used also in newer levels, but in this case the buggy pushing code is only
8775 affecting the "spring" element and no other elements */
8778 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8780 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8783 for (i = 0; i < MAX_PLAYERS; i++)
8785 struct PlayerInfo *player = &stored_player[i];
8790 if (player->active && player->is_pushing && player->is_moving &&
8792 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8793 Feld[x][y] == EL_SPRING))
8795 if (player->active && player->is_pushing && player->is_moving &&
8799 ContinueMoving(x, y);
8801 /* continue moving after pushing (this is actually a bug) */
8802 if (!IS_MOVING(x, y))
8811 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8813 Changed[x][y] = CE_BITMASK_DEFAULT;
8814 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8816 #if USE_NEW_BLOCK_STYLE
8817 /* this must be handled before main playfield loop */
8818 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8821 if (MovDelay[x][y] <= 0)
8827 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8829 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8830 printf("GameActions(): This should never happen!\n");
8832 ChangePage[x][y] = -1;
8837 if (WasJustMoving[x][y] > 0)
8838 WasJustMoving[x][y]--;
8839 if (WasJustFalling[x][y] > 0)
8840 WasJustFalling[x][y]--;
8841 if (CheckCollision[x][y] > 0)
8842 CheckCollision[x][y]--;
8847 /* reset finished pushing action (not done in ContinueMoving() to allow
8848 continous pushing animation for elements with zero push delay) */
8849 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8851 ResetGfxAnimation(x, y);
8852 DrawLevelField(x, y);
8857 if (IS_BLOCKED(x, y))
8861 Blocked2Moving(x, y, &oldx, &oldy);
8862 if (!IS_MOVING(oldx, oldy))
8864 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8865 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8866 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8867 printf("GameActions(): This should never happen!\n");
8873 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8875 element = Feld[x][y];
8877 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8879 graphic = el2img(element);
8885 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8887 element = graphic = 0;
8891 if (graphic_info[graphic].anim_global_sync)
8892 GfxFrame[x][y] = FrameCounter;
8894 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8895 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8896 ResetRandomAnimationValue(x, y);
8898 SetRandomAnimationValue(x, y);
8901 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8904 if (IS_INACTIVE(element))
8906 if (IS_ANIMATED(graphic))
8907 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8913 /* this may take place after moving, so 'element' may have changed */
8915 if (IS_CHANGING(x, y))
8917 if (IS_CHANGING(x, y) &&
8918 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8922 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8923 element_info[element].event_page_nr[CE_DELAY]);
8925 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8928 element = Feld[x][y];
8929 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8933 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8938 element = Feld[x][y];
8939 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8941 if (element == EL_MOLE)
8942 printf("::: %d, %d, %d [%d]\n",
8943 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8947 if (element == EL_YAMYAM)
8948 printf("::: %d, %d, %d\n",
8949 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8953 if (IS_ANIMATED(graphic) &&
8957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8960 if (element == EL_BUG)
8961 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8965 if (element == EL_MOLE)
8966 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8970 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8971 EdelsteinFunkeln(x, y);
8973 else if ((element == EL_ACID ||
8974 element == EL_EXIT_OPEN ||
8975 element == EL_SP_EXIT_OPEN ||
8976 element == EL_SP_TERMINAL ||
8977 element == EL_SP_TERMINAL_ACTIVE ||
8978 element == EL_EXTRA_TIME ||
8979 element == EL_SHIELD_NORMAL ||
8980 element == EL_SHIELD_DEADLY) &&
8981 IS_ANIMATED(graphic))
8982 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8983 else if (IS_MOVING(x, y))
8984 ContinueMoving(x, y);
8985 else if (IS_ACTIVE_BOMB(element))
8986 CheckDynamite(x, y);
8988 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8989 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8991 else if (element == EL_AMOEBA_GROWING)
8992 AmoebeWaechst(x, y);
8993 else if (element == EL_AMOEBA_SHRINKING)
8994 AmoebaDisappearing(x, y);
8996 #if !USE_NEW_AMOEBA_CODE
8997 else if (IS_AMOEBALIVE(element))
8998 AmoebeAbleger(x, y);
9001 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9003 else if (element == EL_EXIT_CLOSED)
9005 else if (element == EL_SP_EXIT_CLOSED)
9007 else if (element == EL_EXPANDABLE_WALL_GROWING)
9009 else if (element == EL_EXPANDABLE_WALL ||
9010 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9011 element == EL_EXPANDABLE_WALL_VERTICAL ||
9012 element == EL_EXPANDABLE_WALL_ANY)
9014 else if (element == EL_FLAMES)
9015 CheckForDragon(x, y);
9017 else if (IS_AUTO_CHANGING(element))
9018 ChangeElement(x, y);
9020 else if (element == EL_EXPLOSION)
9021 ; /* drawing of correct explosion animation is handled separately */
9022 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9023 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9026 /* this may take place after moving, so 'element' may have changed */
9027 if (IS_AUTO_CHANGING(Feld[x][y]))
9028 ChangeElement(x, y);
9031 if (IS_BELT_ACTIVE(element))
9032 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9034 if (game.magic_wall_active)
9036 int jx = local_player->jx, jy = local_player->jy;
9038 /* play the element sound at the position nearest to the player */
9039 if ((element == EL_MAGIC_WALL_FULL ||
9040 element == EL_MAGIC_WALL_ACTIVE ||
9041 element == EL_MAGIC_WALL_EMPTYING ||
9042 element == EL_BD_MAGIC_WALL_FULL ||
9043 element == EL_BD_MAGIC_WALL_ACTIVE ||
9044 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9045 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9053 #if USE_NEW_AMOEBA_CODE
9054 /* new experimental amoeba growth stuff */
9056 if (!(FrameCounter % 8))
9059 static unsigned long random = 1684108901;
9061 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9064 x = (random >> 10) % lev_fieldx;
9065 y = (random >> 20) % lev_fieldy;
9067 x = RND(lev_fieldx);
9068 y = RND(lev_fieldy);
9070 element = Feld[x][y];
9073 if (!IS_PLAYER(x,y) &&
9074 (element == EL_EMPTY ||
9075 CAN_GROW_INTO(element) ||
9076 element == EL_QUICKSAND_EMPTY ||
9077 element == EL_ACID_SPLASH_LEFT ||
9078 element == EL_ACID_SPLASH_RIGHT))
9080 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9081 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9082 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9083 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9084 Feld[x][y] = EL_AMOEBA_DROP;
9087 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9088 if (!IS_PLAYER(x,y) &&
9089 (element == EL_EMPTY ||
9090 element == EL_SAND ||
9091 element == EL_QUICKSAND_EMPTY ||
9092 element == EL_ACID_SPLASH_LEFT ||
9093 element == EL_ACID_SPLASH_RIGHT))
9095 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9096 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9097 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9098 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9099 Feld[x][y] = EL_AMOEBA_DROP;
9103 random = random * 129 + 1;
9109 if (game.explosions_delayed)
9112 game.explosions_delayed = FALSE;
9114 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9116 element = Feld[x][y];
9118 if (ExplodeField[x][y])
9119 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9120 else if (element == EL_EXPLOSION)
9121 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9123 ExplodeField[x][y] = EX_TYPE_NONE;
9126 game.explosions_delayed = TRUE;
9129 if (game.magic_wall_active)
9131 if (!(game.magic_wall_time_left % 4))
9133 int element = Feld[magic_wall_x][magic_wall_y];
9135 if (element == EL_BD_MAGIC_WALL_FULL ||
9136 element == EL_BD_MAGIC_WALL_ACTIVE ||
9137 element == EL_BD_MAGIC_WALL_EMPTYING)
9138 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9140 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9143 if (game.magic_wall_time_left > 0)
9145 game.magic_wall_time_left--;
9146 if (!game.magic_wall_time_left)
9148 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9150 element = Feld[x][y];
9152 if (element == EL_MAGIC_WALL_ACTIVE ||
9153 element == EL_MAGIC_WALL_FULL)
9155 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9156 DrawLevelField(x, y);
9158 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9159 element == EL_BD_MAGIC_WALL_FULL)
9161 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9162 DrawLevelField(x, y);
9166 game.magic_wall_active = FALSE;
9171 if (game.light_time_left > 0)
9173 game.light_time_left--;
9175 if (game.light_time_left == 0)
9176 RedrawAllLightSwitchesAndInvisibleElements();
9179 if (game.timegate_time_left > 0)
9181 game.timegate_time_left--;
9183 if (game.timegate_time_left == 0)
9184 CloseAllOpenTimegates();
9187 for (i = 0; i < MAX_PLAYERS; i++)
9189 struct PlayerInfo *player = &stored_player[i];
9191 if (SHIELD_ON(player))
9193 if (player->shield_deadly_time_left)
9194 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9195 else if (player->shield_normal_time_left)
9196 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9200 if (TimeFrames >= FRAMES_PER_SECOND)
9205 for (i = 0; i < MAX_PLAYERS; i++)
9207 struct PlayerInfo *player = &stored_player[i];
9209 if (SHIELD_ON(player))
9211 player->shield_normal_time_left--;
9213 if (player->shield_deadly_time_left > 0)
9214 player->shield_deadly_time_left--;
9218 if (!level.use_step_counter)
9226 if (TimeLeft <= 10 && setup.time_limit)
9227 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9229 DrawGameValue_Time(TimeLeft);
9231 if (!TimeLeft && setup.time_limit)
9232 for (i = 0; i < MAX_PLAYERS; i++)
9233 KillHero(&stored_player[i]);
9235 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9236 DrawGameValue_Time(TimePlayed);
9239 if (tape.recording || tape.playing)
9240 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9244 PlayAllPlayersSound();
9246 if (options.debug) /* calculate frames per second */
9248 static unsigned long fps_counter = 0;
9249 static int fps_frames = 0;
9250 unsigned long fps_delay_ms = Counter() - fps_counter;
9254 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9256 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9259 fps_counter = Counter();
9262 redraw_mask |= REDRAW_FPS;
9266 if (stored_player[0].jx != stored_player[0].last_jx ||
9267 stored_player[0].jy != stored_player[0].last_jy)
9268 printf("::: %d, %d, %d, %d, %d\n",
9269 stored_player[0].MovDir,
9270 stored_player[0].MovPos,
9271 stored_player[0].GfxPos,
9272 stored_player[0].Frame,
9273 stored_player[0].StepFrame);
9276 #if USE_NEW_MOVE_DELAY
9277 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9282 for (i = 0; i < MAX_PLAYERS; i++)
9285 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9287 stored_player[i].Frame += move_frames;
9289 if (stored_player[i].MovPos != 0)
9290 stored_player[i].StepFrame += move_frames;
9292 #if USE_NEW_MOVE_DELAY
9293 if (stored_player[i].move_delay > 0)
9294 stored_player[i].move_delay--;
9297 if (stored_player[i].drop_delay > 0)
9298 stored_player[i].drop_delay--;
9303 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9305 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9307 local_player->show_envelope = 0;
9311 #if USE_NEW_RANDOMIZE
9312 /* use random number generator in every frame to make it less predictable */
9313 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9318 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9320 int min_x = x, min_y = y, max_x = x, max_y = y;
9323 for (i = 0; i < MAX_PLAYERS; i++)
9325 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9327 if (!stored_player[i].active || &stored_player[i] == player)
9330 min_x = MIN(min_x, jx);
9331 min_y = MIN(min_y, jy);
9332 max_x = MAX(max_x, jx);
9333 max_y = MAX(max_y, jy);
9336 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9339 static boolean AllPlayersInVisibleScreen()
9343 for (i = 0; i < MAX_PLAYERS; i++)
9345 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9347 if (!stored_player[i].active)
9350 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9357 void ScrollLevel(int dx, int dy)
9359 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9362 BlitBitmap(drawto_field, drawto_field,
9363 FX + TILEX * (dx == -1) - softscroll_offset,
9364 FY + TILEY * (dy == -1) - softscroll_offset,
9365 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9366 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9367 FX + TILEX * (dx == 1) - softscroll_offset,
9368 FY + TILEY * (dy == 1) - softscroll_offset);
9372 x = (dx == 1 ? BX1 : BX2);
9373 for (y = BY1; y <= BY2; y++)
9374 DrawScreenField(x, y);
9379 y = (dy == 1 ? BY1 : BY2);
9380 for (x = BX1; x <= BX2; x++)
9381 DrawScreenField(x, y);
9384 redraw_mask |= REDRAW_FIELD;
9388 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9390 int nextx = x + dx, nexty = y + dy;
9391 int element = Feld[x][y];
9394 element != EL_SP_PORT_LEFT &&
9395 element != EL_SP_GRAVITY_PORT_LEFT &&
9396 element != EL_SP_PORT_HORIZONTAL &&
9397 element != EL_SP_PORT_ANY) ||
9399 element != EL_SP_PORT_RIGHT &&
9400 element != EL_SP_GRAVITY_PORT_RIGHT &&
9401 element != EL_SP_PORT_HORIZONTAL &&
9402 element != EL_SP_PORT_ANY) ||
9404 element != EL_SP_PORT_UP &&
9405 element != EL_SP_GRAVITY_PORT_UP &&
9406 element != EL_SP_PORT_VERTICAL &&
9407 element != EL_SP_PORT_ANY) ||
9409 element != EL_SP_PORT_DOWN &&
9410 element != EL_SP_GRAVITY_PORT_DOWN &&
9411 element != EL_SP_PORT_VERTICAL &&
9412 element != EL_SP_PORT_ANY) ||
9413 !IN_LEV_FIELD(nextx, nexty) ||
9414 !IS_FREE(nextx, nexty))
9421 static boolean canFallDown(struct PlayerInfo *player)
9423 int jx = player->jx, jy = player->jy;
9425 return (IN_LEV_FIELD(jx, jy + 1) &&
9426 (IS_FREE(jx, jy + 1) ||
9427 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9428 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9429 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9432 static boolean canPassField(int x, int y, int move_dir)
9434 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9435 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9436 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9439 int element = Feld[x][y];
9441 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9442 !CAN_MOVE(element) &&
9443 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9444 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9445 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9448 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9450 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9451 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9452 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9456 int nextx = newx + dx;
9457 int nexty = newy + dy;
9461 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9462 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9464 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9466 (IS_DIGGABLE(Feld[newx][newy]) ||
9467 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9468 canPassField(newx, newy, move_dir)));
9471 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9472 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9473 (IS_DIGGABLE(Feld[newx][newy]) ||
9474 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9475 canPassField(newx, newy, move_dir)));
9478 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9479 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9480 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9481 canPassField(newx, newy, move_dir)));
9483 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9484 (IS_DIGGABLE(Feld[newx][newy]) ||
9485 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9486 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9487 !CAN_MOVE(Feld[newx][newy]) &&
9488 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9489 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9490 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9496 static void CheckGravityMovement(struct PlayerInfo *player)
9498 if (game.gravity && !player->programmed_action)
9501 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9502 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9504 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9505 int move_dir_vertical = player->action & MV_VERTICAL;
9509 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9511 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9514 int jx = player->jx, jy = player->jy;
9516 boolean player_is_moving_to_valid_field =
9517 (!player_is_snapping &&
9518 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9519 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9523 (player->last_move_dir & MV_HORIZONTAL ?
9524 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9525 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9529 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9530 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9531 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9532 int new_jx = jx + dx, new_jy = jy + dy;
9533 int nextx = new_jx + dx, nexty = new_jy + dy;
9539 boolean player_can_fall_down = canFallDown(player);
9541 boolean player_can_fall_down =
9542 (IN_LEV_FIELD(jx, jy + 1) &&
9543 (IS_FREE(jx, jy + 1) ||
9544 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9548 boolean player_can_fall_down =
9549 (IN_LEV_FIELD(jx, jy + 1) &&
9550 (IS_FREE(jx, jy + 1)));
9554 boolean player_is_moving_to_valid_field =
9557 !player_is_snapping &&
9561 IN_LEV_FIELD(new_jx, new_jy) &&
9562 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9563 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9564 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9565 IN_LEV_FIELD(nextx, nexty) &&
9566 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9568 IN_LEV_FIELD(new_jx, new_jy) &&
9569 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9570 Feld[new_jx][new_jy] == EL_SAND ||
9571 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9572 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9573 /* !!! extend EL_SAND to anything diggable !!! */
9579 boolean player_is_standing_on_valid_field =
9580 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9581 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9585 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9586 player_can_fall_down,
9587 player_is_standing_on_valid_field,
9588 player_is_moving_to_valid_field,
9589 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9590 player->effective_action,
9591 player->can_fall_into_acid);
9594 if (player_can_fall_down &&
9596 !player_is_standing_on_valid_field &&
9598 !player_is_moving_to_valid_field)
9601 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9602 jx, jy, FrameCounter);
9605 player->programmed_action = MV_DOWN;
9610 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9613 return CheckGravityMovement(player);
9616 if (game.gravity && !player->programmed_action)
9618 int jx = player->jx, jy = player->jy;
9619 boolean field_under_player_is_free =
9620 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9621 boolean player_is_standing_on_valid_field =
9622 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9623 (IS_WALKABLE(Feld[jx][jy]) &&
9624 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9626 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9627 player->programmed_action = MV_DOWN;
9633 -----------------------------------------------------------------------------
9634 dx, dy: direction (non-diagonal) to try to move the player to
9635 real_dx, real_dy: direction as read from input device (can be diagonal)
9638 boolean MovePlayerOneStep(struct PlayerInfo *player,
9639 int dx, int dy, int real_dx, int real_dy)
9642 static int trigger_sides[4][2] =
9644 /* enter side leave side */
9645 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9646 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9647 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9648 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9650 int move_direction = (dx == -1 ? MV_LEFT :
9651 dx == +1 ? MV_RIGHT :
9653 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9654 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9655 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9657 int jx = player->jx, jy = player->jy;
9658 int new_jx = jx + dx, new_jy = jy + dy;
9662 if (!player->active || (!dx && !dy))
9663 return MF_NO_ACTION;
9665 player->MovDir = (dx < 0 ? MV_LEFT :
9668 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9670 if (!IN_LEV_FIELD(new_jx, new_jy))
9671 return MF_NO_ACTION;
9673 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9674 return MF_NO_ACTION;
9677 element = MovingOrBlocked2Element(new_jx, new_jy);
9679 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9682 if (DONT_RUN_INTO(element))
9684 if (element == EL_ACID && dx == 0 && dy == 1)
9686 SplashAcid(new_jx, new_jy);
9687 Feld[jx][jy] = EL_PLAYER_1;
9688 InitMovingField(jx, jy, MV_DOWN);
9689 Store[jx][jy] = EL_ACID;
9690 ContinueMoving(jx, jy);
9694 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9699 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9700 if (can_move != MF_MOVING)
9703 /* check if DigField() has caused relocation of the player */
9704 if (player->jx != jx || player->jy != jy)
9705 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9707 StorePlayer[jx][jy] = 0;
9708 player->last_jx = jx;
9709 player->last_jy = jy;
9710 player->jx = new_jx;
9711 player->jy = new_jy;
9712 StorePlayer[new_jx][new_jy] = player->element_nr;
9715 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9717 player->step_counter++;
9720 player->drop_delay = 0;
9723 PlayerVisit[jx][jy] = FrameCounter;
9725 ScrollPlayer(player, SCROLL_INIT);
9728 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9730 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9732 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9735 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9737 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9738 CE_OTHER_GETS_ENTERED, enter_side);
9739 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9740 CE_ENTERED_BY_PLAYER, enter_side);
9747 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9749 int jx = player->jx, jy = player->jy;
9750 int old_jx = jx, old_jy = jy;
9751 int moved = MF_NO_ACTION;
9754 if (!player->active)
9759 if (player->MovPos == 0)
9761 player->is_moving = FALSE;
9762 player->is_digging = FALSE;
9763 player->is_collecting = FALSE;
9764 player->is_snapping = FALSE;
9765 player->is_pushing = FALSE;
9771 if (!player->active || (!dx && !dy))
9776 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9784 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9785 player->move_delay + player->move_delay_value);
9788 #if USE_NEW_MOVE_DELAY
9789 if (player->move_delay > 0)
9791 if (!FrameReached(&player->move_delay, player->move_delay_value))
9795 printf("::: can NOT move\n");
9801 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9802 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9809 printf("::: COULD move now\n");
9812 #if USE_NEW_MOVE_DELAY
9813 player->move_delay = -1; /* set to "uninitialized" value */
9816 /* store if player is automatically moved to next field */
9817 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9819 /* remove the last programmed player action */
9820 player->programmed_action = 0;
9824 /* should only happen if pre-1.2 tape recordings are played */
9825 /* this is only for backward compatibility */
9827 int original_move_delay_value = player->move_delay_value;
9830 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9834 /* scroll remaining steps with finest movement resolution */
9835 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9837 while (player->MovPos)
9839 ScrollPlayer(player, SCROLL_GO_ON);
9840 ScrollScreen(NULL, SCROLL_GO_ON);
9842 #if USE_NEW_MOVE_DELAY
9843 AdvanceFrameAndPlayerCounters(player->index_nr);
9852 player->move_delay_value = original_move_delay_value;
9855 if (player->last_move_dir & MV_HORIZONTAL)
9857 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9858 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9862 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9863 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9869 if (moved & MF_MOVING && !ScreenMovPos &&
9870 (player == local_player || !options.network))
9872 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9873 int offset = (setup.scroll_delay ? 3 : 0);
9875 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9877 /* actual player has left the screen -- scroll in that direction */
9878 if (jx != old_jx) /* player has moved horizontally */
9879 scroll_x += (jx - old_jx);
9880 else /* player has moved vertically */
9881 scroll_y += (jy - old_jy);
9885 if (jx != old_jx) /* player has moved horizontally */
9887 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9888 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9889 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9891 /* don't scroll over playfield boundaries */
9892 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9893 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9895 /* don't scroll more than one field at a time */
9896 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9898 /* don't scroll against the player's moving direction */
9899 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9900 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9901 scroll_x = old_scroll_x;
9903 else /* player has moved vertically */
9905 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9906 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9907 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9909 /* don't scroll over playfield boundaries */
9910 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9911 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9913 /* don't scroll more than one field at a time */
9914 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9916 /* don't scroll against the player's moving direction */
9917 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9918 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9919 scroll_y = old_scroll_y;
9923 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9925 if (!options.network && !AllPlayersInVisibleScreen())
9927 scroll_x = old_scroll_x;
9928 scroll_y = old_scroll_y;
9932 ScrollScreen(player, SCROLL_INIT);
9933 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9940 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9942 if (!(moved & MF_MOVING) && !player->is_pushing)
9947 player->StepFrame = 0;
9949 if (moved & MF_MOVING)
9952 printf("::: REALLY moves now\n");
9955 if (old_jx != jx && old_jy == jy)
9956 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9957 else if (old_jx == jx && old_jy != jy)
9958 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9960 DrawLevelField(jx, jy); /* for "crumbled sand" */
9962 player->last_move_dir = player->MovDir;
9963 player->is_moving = TRUE;
9965 player->is_snapping = FALSE;
9969 player->is_switching = FALSE;
9972 player->is_dropping = FALSE;
9976 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9979 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9982 int move_direction = player->MovDir;
9984 int enter_side = MV_DIR_OPPOSITE(move_direction);
9985 int leave_side = move_direction;
9987 static int trigger_sides[4][2] =
9989 /* enter side leave side */
9990 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9991 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9992 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9993 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9995 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9996 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9998 int old_element = Feld[old_jx][old_jy];
9999 int new_element = Feld[jx][jy];
10002 /* !!! TEST ONLY !!! */
10003 if (IS_CUSTOM_ELEMENT(old_element))
10004 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10006 player->index_bit, leave_side);
10008 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10009 CE_OTHER_GETS_LEFT,
10010 player->index_bit, leave_side);
10012 if (IS_CUSTOM_ELEMENT(new_element))
10013 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10014 player->index_bit, enter_side);
10016 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10017 CE_OTHER_GETS_ENTERED,
10018 player->index_bit, enter_side);
10028 CheckGravityMovementWhenNotMoving(player);
10031 player->last_move_dir = MV_NO_MOVING;
10033 player->is_moving = FALSE;
10035 #if USE_NEW_MOVE_STYLE
10036 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10037 /* ensure that the player is also allowed to move in the next frame */
10038 /* (currently, the player is forced to wait eight frames before he can try
10041 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10042 player->move_delay = 0; /* allow direct movement in the next frame */
10046 #if USE_NEW_MOVE_DELAY
10047 if (player->move_delay == -1) /* not yet initialized by DigField() */
10048 player->move_delay = player->move_delay_value;
10051 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10053 TestIfHeroTouchesBadThing(jx, jy);
10054 TestIfPlayerTouchesCustomElement(jx, jy);
10057 if (!player->active)
10058 RemoveHero(player);
10063 void ScrollPlayer(struct PlayerInfo *player, int mode)
10065 int jx = player->jx, jy = player->jy;
10066 int last_jx = player->last_jx, last_jy = player->last_jy;
10067 int move_stepsize = TILEX / player->move_delay_value;
10069 if (!player->active || !player->MovPos)
10072 if (mode == SCROLL_INIT)
10074 player->actual_frame_counter = FrameCounter;
10075 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10078 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10080 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10081 player->block_delay);
10084 #if USE_NEW_BLOCK_STYLE
10087 if (player->block_delay <= 0)
10088 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10091 if (player->block_delay > 0 &&
10092 Feld[last_jx][last_jy] == EL_EMPTY)
10094 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10095 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10098 #if USE_NEW_MOVE_STYLE
10099 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10100 player->block_last_field) &&
10101 Feld[last_jx][last_jy] == EL_EMPTY)
10102 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10104 if (Feld[last_jx][last_jy] == EL_EMPTY)
10105 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10110 DrawPlayer(player);
10115 else if (!FrameReached(&player->actual_frame_counter, 1))
10118 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10119 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10121 #if USE_NEW_BLOCK_STYLE
10123 if (!player->block_last_field &&
10124 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10126 RemoveField(last_jx, last_jy);
10128 Feld[last_jx][last_jy] = EL_EMPTY;
10132 /* before DrawPlayer() to draw correct player graphic for this case */
10133 if (player->MovPos == 0)
10134 CheckGravityMovement(player);
10137 DrawPlayer(player); /* needed here only to cleanup last field */
10140 if (player->MovPos == 0) /* player reached destination field */
10143 if (player->move_delay_reset_counter > 0)
10145 player->move_delay_reset_counter--;
10147 if (player->move_delay_reset_counter == 0)
10149 /* continue with normal speed after quickly moving through gate */
10150 HALVE_PLAYER_SPEED(player);
10152 /* be able to make the next move without delay */
10153 player->move_delay = 0;
10157 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10159 /* continue with normal speed after quickly moving through gate */
10160 HALVE_PLAYER_SPEED(player);
10162 /* be able to make the next move without delay */
10163 player->move_delay = 0;
10167 #if USE_NEW_BLOCK_STYLE
10169 if (player->block_last_field &&
10170 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10172 RemoveField(last_jx, last_jy);
10174 Feld[last_jx][last_jy] = EL_EMPTY;
10178 player->last_jx = jx;
10179 player->last_jy = jy;
10181 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10182 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10183 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10185 DrawPlayer(player); /* needed here only to cleanup last field */
10186 RemoveHero(player);
10188 if (local_player->friends_still_needed == 0 ||
10189 IS_SP_ELEMENT(Feld[jx][jy]))
10190 player->LevelSolved = player->GameOver = TRUE;
10194 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10195 /* this breaks one level: "machine", level 000 */
10197 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10200 int move_direction = player->MovDir;
10202 int enter_side = MV_DIR_OPPOSITE(move_direction);
10203 int leave_side = move_direction;
10205 static int trigger_sides[4][2] =
10207 /* enter side leave side */
10208 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10209 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10210 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10211 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10213 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10214 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10216 int old_jx = last_jx;
10217 int old_jy = last_jy;
10218 int old_element = Feld[old_jx][old_jy];
10219 int new_element = Feld[jx][jy];
10222 /* !!! TEST ONLY !!! */
10223 if (IS_CUSTOM_ELEMENT(old_element))
10224 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10226 player->index_bit, leave_side);
10228 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10229 CE_OTHER_GETS_LEFT,
10230 player->index_bit, leave_side);
10232 if (IS_CUSTOM_ELEMENT(new_element))
10233 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10234 player->index_bit, enter_side);
10236 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10237 CE_OTHER_GETS_ENTERED,
10238 player->index_bit, enter_side);
10244 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10246 TestIfHeroTouchesBadThing(jx, jy);
10247 TestIfPlayerTouchesCustomElement(jx, jy);
10250 /* needed because pushed element has not yet reached its destination,
10251 so it would trigger a change event at its previous field location */
10252 if (!player->is_pushing)
10254 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10257 if (!player->active)
10258 RemoveHero(player);
10261 if (level.use_step_counter)
10271 if (TimeLeft <= 10 && setup.time_limit)
10272 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10274 DrawGameValue_Time(TimeLeft);
10276 if (!TimeLeft && setup.time_limit)
10277 for (i = 0; i < MAX_PLAYERS; i++)
10278 KillHero(&stored_player[i]);
10280 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10281 DrawGameValue_Time(TimePlayed);
10284 if (tape.single_step && tape.recording && !tape.pausing &&
10285 !player->programmed_action)
10286 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10290 void ScrollScreen(struct PlayerInfo *player, int mode)
10292 static unsigned long screen_frame_counter = 0;
10294 if (mode == SCROLL_INIT)
10296 /* set scrolling step size according to actual player's moving speed */
10297 ScrollStepSize = TILEX / player->move_delay_value;
10299 screen_frame_counter = FrameCounter;
10300 ScreenMovDir = player->MovDir;
10301 ScreenMovPos = player->MovPos;
10302 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10305 else if (!FrameReached(&screen_frame_counter, 1))
10310 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10311 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10312 redraw_mask |= REDRAW_FIELD;
10315 ScreenMovDir = MV_NO_MOVING;
10318 void TestIfPlayerTouchesCustomElement(int x, int y)
10320 static int xy[4][2] =
10327 static int trigger_sides[4][2] =
10329 /* center side border side */
10330 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10331 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10332 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10333 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10335 static int touch_dir[4] =
10337 MV_LEFT | MV_RIGHT,
10342 int center_element = Feld[x][y]; /* should always be non-moving! */
10345 for (i = 0; i < NUM_DIRECTIONS; i++)
10347 int xx = x + xy[i][0];
10348 int yy = y + xy[i][1];
10349 int center_side = trigger_sides[i][0];
10350 int border_side = trigger_sides[i][1];
10351 int border_element;
10353 if (!IN_LEV_FIELD(xx, yy))
10356 if (IS_PLAYER(x, y))
10358 struct PlayerInfo *player = PLAYERINFO(x, y);
10360 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10361 border_element = Feld[xx][yy]; /* may be moving! */
10362 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10363 border_element = Feld[xx][yy];
10364 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10365 border_element = MovingOrBlocked2Element(xx, yy);
10367 continue; /* center and border element do not touch */
10370 /* !!! TEST ONLY !!! */
10371 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10372 player->index_bit, border_side);
10373 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10374 CE_OTHER_GETS_TOUCHED,
10375 player->index_bit, border_side);
10377 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10378 CE_OTHER_GETS_TOUCHED,
10379 player->index_bit, border_side);
10380 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10381 player->index_bit, border_side);
10384 else if (IS_PLAYER(xx, yy))
10386 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10388 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10390 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10391 continue; /* center and border element do not touch */
10395 /* !!! TEST ONLY !!! */
10396 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10397 player->index_bit, center_side);
10398 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10399 CE_OTHER_GETS_TOUCHED,
10400 player->index_bit, center_side);
10402 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10403 CE_OTHER_GETS_TOUCHED,
10404 player->index_bit, center_side);
10405 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10406 player->index_bit, center_side);
10414 void TestIfElementTouchesCustomElement(int x, int y)
10416 static int xy[4][2] =
10423 static int trigger_sides[4][2] =
10425 /* center side border side */
10426 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10427 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10428 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10429 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10431 static int touch_dir[4] =
10433 MV_LEFT | MV_RIGHT,
10438 boolean change_center_element = FALSE;
10439 int center_element_change_page = 0;
10440 int center_element = Feld[x][y]; /* should always be non-moving! */
10441 int border_trigger_element = EL_UNDEFINED;
10444 for (i = 0; i < NUM_DIRECTIONS; i++)
10446 int xx = x + xy[i][0];
10447 int yy = y + xy[i][1];
10448 int center_side = trigger_sides[i][0];
10449 int border_side = trigger_sides[i][1];
10450 int border_element;
10452 if (!IN_LEV_FIELD(xx, yy))
10455 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10456 border_element = Feld[xx][yy]; /* may be moving! */
10457 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10458 border_element = Feld[xx][yy];
10459 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10460 border_element = MovingOrBlocked2Element(xx, yy);
10462 continue; /* center and border element do not touch */
10464 /* check for change of center element (but change it only once) */
10465 if (IS_CUSTOM_ELEMENT(center_element) &&
10466 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10467 !change_center_element)
10469 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10471 struct ElementChangeInfo *change =
10472 &element_info[center_element].change_page[j];
10474 if (change->can_change &&
10475 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10476 change->trigger_side & border_side &&
10478 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10480 change->trigger_element == border_element
10484 change_center_element = TRUE;
10485 center_element_change_page = j;
10486 border_trigger_element = border_element;
10493 /* check for change of border element */
10494 if (IS_CUSTOM_ELEMENT(border_element) &&
10495 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10497 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10499 struct ElementChangeInfo *change =
10500 &element_info[border_element].change_page[j];
10502 if (change->can_change &&
10503 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10504 change->trigger_side & center_side &&
10506 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10508 change->trigger_element == center_element
10513 printf("::: border_element %d, %d\n", x, y);
10516 CheckElementChangeByPage(xx, yy, border_element, center_element,
10517 CE_OTHER_IS_TOUCHING, j);
10524 if (change_center_element)
10527 printf("::: center_element %d, %d\n", x, y);
10530 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10531 CE_OTHER_IS_TOUCHING, center_element_change_page);
10535 void TestIfElementHitsCustomElement(int x, int y, int direction)
10537 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10538 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10539 int hitx = x + dx, hity = y + dy;
10540 int hitting_element = Feld[x][y];
10541 int touched_element;
10543 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10544 !IS_FREE(hitx, hity) &&
10545 (!IS_MOVING(hitx, hity) ||
10546 MovDir[hitx][hity] != direction ||
10547 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10550 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10554 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10558 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10559 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10561 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10562 CE_HITTING_SOMETHING, direction);
10564 if (IN_LEV_FIELD(hitx, hity))
10566 int opposite_direction = MV_DIR_OPPOSITE(direction);
10567 int hitting_side = direction;
10568 int touched_side = opposite_direction;
10570 int touched_element = MovingOrBlocked2Element(hitx, hity);
10573 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10574 MovDir[hitx][hity] != direction ||
10575 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10584 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10585 CE_HIT_BY_SOMETHING, opposite_direction);
10587 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10588 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10590 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10592 struct ElementChangeInfo *change =
10593 &element_info[hitting_element].change_page[i];
10595 if (change->can_change &&
10596 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10597 change->trigger_side & touched_side &&
10600 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10602 change->trigger_element == touched_element
10606 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10607 CE_OTHER_IS_HITTING, i);
10613 if (IS_CUSTOM_ELEMENT(touched_element) &&
10614 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10616 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10618 struct ElementChangeInfo *change =
10619 &element_info[touched_element].change_page[i];
10621 if (change->can_change &&
10622 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10623 change->trigger_side & hitting_side &&
10625 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10627 change->trigger_element == hitting_element
10631 CheckElementChangeByPage(hitx, hity, touched_element,
10632 hitting_element, CE_OTHER_GETS_HIT, i);
10642 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10644 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10645 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10646 int hitx = x + dx, hity = y + dy;
10647 int hitting_element = Feld[x][y];
10648 int touched_element;
10650 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10651 !IS_FREE(hitx, hity) &&
10652 (!IS_MOVING(hitx, hity) ||
10653 MovDir[hitx][hity] != direction ||
10654 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10657 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10661 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10665 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10666 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10668 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10669 EP_CAN_SMASH_EVERYTHING, direction);
10671 if (IN_LEV_FIELD(hitx, hity))
10673 int opposite_direction = MV_DIR_OPPOSITE(direction);
10674 int hitting_side = direction;
10675 int touched_side = opposite_direction;
10677 int touched_element = MovingOrBlocked2Element(hitx, hity);
10680 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10681 MovDir[hitx][hity] != direction ||
10682 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10691 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10692 CE_SMASHED_BY_SOMETHING, opposite_direction);
10694 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10695 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10697 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10699 struct ElementChangeInfo *change =
10700 &element_info[hitting_element].change_page[i];
10702 if (change->can_change &&
10703 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10704 change->trigger_side & touched_side &&
10707 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10709 change->trigger_element == touched_element
10713 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10714 CE_OTHER_IS_SMASHING, i);
10720 if (IS_CUSTOM_ELEMENT(touched_element) &&
10721 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10723 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10725 struct ElementChangeInfo *change =
10726 &element_info[touched_element].change_page[i];
10728 if (change->can_change &&
10729 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10730 change->trigger_side & hitting_side &&
10732 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10734 change->trigger_element == hitting_element
10738 CheckElementChangeByPage(hitx, hity, touched_element,
10739 hitting_element, CE_OTHER_GETS_SMASHED,i);
10749 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10751 int i, kill_x = -1, kill_y = -1;
10752 int bad_element = -1;
10753 static int test_xy[4][2] =
10760 static int test_dir[4] =
10768 for (i = 0; i < NUM_DIRECTIONS; i++)
10770 int test_x, test_y, test_move_dir, test_element;
10772 test_x = good_x + test_xy[i][0];
10773 test_y = good_y + test_xy[i][1];
10775 if (!IN_LEV_FIELD(test_x, test_y))
10779 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10782 test_element = Feld[test_x][test_y];
10784 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10787 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10788 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10790 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10791 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10795 bad_element = test_element;
10801 if (kill_x != -1 || kill_y != -1)
10803 if (IS_PLAYER(good_x, good_y))
10805 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10808 if (player->shield_deadly_time_left > 0 &&
10809 !IS_INDESTRUCTIBLE(bad_element))
10810 Bang(kill_x, kill_y);
10811 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10814 if (player->shield_deadly_time_left > 0)
10815 Bang(kill_x, kill_y);
10816 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10821 Bang(good_x, good_y);
10825 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10827 int i, kill_x = -1, kill_y = -1;
10828 int bad_element = Feld[bad_x][bad_y];
10829 static int test_xy[4][2] =
10836 static int touch_dir[4] =
10838 MV_LEFT | MV_RIGHT,
10843 static int test_dir[4] =
10851 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10854 for (i = 0; i < NUM_DIRECTIONS; i++)
10856 int test_x, test_y, test_move_dir, test_element;
10858 test_x = bad_x + test_xy[i][0];
10859 test_y = bad_y + test_xy[i][1];
10860 if (!IN_LEV_FIELD(test_x, test_y))
10864 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10866 test_element = Feld[test_x][test_y];
10868 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10869 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10871 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10872 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10874 /* good thing is player or penguin that does not move away */
10875 if (IS_PLAYER(test_x, test_y))
10877 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10879 if (bad_element == EL_ROBOT && player->is_moving)
10880 continue; /* robot does not kill player if he is moving */
10882 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10884 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10885 continue; /* center and border element do not touch */
10892 else if (test_element == EL_PENGUIN)
10901 if (kill_x != -1 || kill_y != -1)
10903 if (IS_PLAYER(kill_x, kill_y))
10905 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10908 if (player->shield_deadly_time_left > 0 &&
10909 !IS_INDESTRUCTIBLE(bad_element))
10910 Bang(bad_x, bad_y);
10911 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10914 if (player->shield_deadly_time_left > 0)
10915 Bang(bad_x, bad_y);
10916 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10921 Bang(kill_x, kill_y);
10925 void TestIfHeroTouchesBadThing(int x, int y)
10927 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10930 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10932 TestIfGoodThingHitsBadThing(x, y, move_dir);
10935 void TestIfBadThingTouchesHero(int x, int y)
10937 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10940 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10942 TestIfBadThingHitsGoodThing(x, y, move_dir);
10945 void TestIfFriendTouchesBadThing(int x, int y)
10947 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10950 void TestIfBadThingTouchesFriend(int x, int y)
10952 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10955 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10957 int i, kill_x = bad_x, kill_y = bad_y;
10958 static int xy[4][2] =
10966 for (i = 0; i < NUM_DIRECTIONS; i++)
10970 x = bad_x + xy[i][0];
10971 y = bad_y + xy[i][1];
10972 if (!IN_LEV_FIELD(x, y))
10975 element = Feld[x][y];
10976 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10977 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10985 if (kill_x != bad_x || kill_y != bad_y)
10986 Bang(bad_x, bad_y);
10989 void KillHero(struct PlayerInfo *player)
10991 int jx = player->jx, jy = player->jy;
10993 if (!player->active)
10996 /* remove accessible field at the player's position */
10997 Feld[jx][jy] = EL_EMPTY;
10999 /* deactivate shield (else Bang()/Explode() would not work right) */
11000 player->shield_normal_time_left = 0;
11001 player->shield_deadly_time_left = 0;
11007 static void KillHeroUnlessEnemyProtected(int x, int y)
11009 if (!PLAYER_ENEMY_PROTECTED(x, y))
11010 KillHero(PLAYERINFO(x, y));
11013 static void KillHeroUnlessExplosionProtected(int x, int y)
11015 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11016 KillHero(PLAYERINFO(x, y));
11019 void BuryHero(struct PlayerInfo *player)
11021 int jx = player->jx, jy = player->jy;
11023 if (!player->active)
11027 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11029 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11031 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11033 player->GameOver = TRUE;
11034 RemoveHero(player);
11037 void RemoveHero(struct PlayerInfo *player)
11039 int jx = player->jx, jy = player->jy;
11040 int i, found = FALSE;
11042 player->present = FALSE;
11043 player->active = FALSE;
11045 if (!ExplodeField[jx][jy])
11046 StorePlayer[jx][jy] = 0;
11048 for (i = 0; i < MAX_PLAYERS; i++)
11049 if (stored_player[i].active)
11053 AllPlayersGone = TRUE;
11060 =============================================================================
11061 checkDiagonalPushing()
11062 -----------------------------------------------------------------------------
11063 check if diagonal input device direction results in pushing of object
11064 (by checking if the alternative direction is walkable, diggable, ...)
11065 =============================================================================
11068 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11069 int x, int y, int real_dx, int real_dy)
11071 int jx, jy, dx, dy, xx, yy;
11073 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11076 /* diagonal direction: check alternative direction */
11081 xx = jx + (dx == 0 ? real_dx : 0);
11082 yy = jy + (dy == 0 ? real_dy : 0);
11084 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11088 =============================================================================
11090 -----------------------------------------------------------------------------
11091 x, y: field next to player (non-diagonal) to try to dig to
11092 real_dx, real_dy: direction as read from input device (can be diagonal)
11093 =============================================================================
11096 int DigField(struct PlayerInfo *player,
11097 int oldx, int oldy, int x, int y,
11098 int real_dx, int real_dy, int mode)
11101 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11103 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11104 boolean player_was_pushing = player->is_pushing;
11105 int jx = oldx, jy = oldy;
11106 int dx = x - jx, dy = y - jy;
11107 int nextx = x + dx, nexty = y + dy;
11108 int move_direction = (dx == -1 ? MV_LEFT :
11109 dx == +1 ? MV_RIGHT :
11111 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11112 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11114 int dig_side = MV_DIR_OPPOSITE(move_direction);
11116 static int trigger_sides[4] =
11118 CH_SIDE_RIGHT, /* moving left */
11119 CH_SIDE_LEFT, /* moving right */
11120 CH_SIDE_BOTTOM, /* moving up */
11121 CH_SIDE_TOP, /* moving down */
11123 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11125 int old_element = Feld[jx][jy];
11128 if (is_player) /* function can also be called by EL_PENGUIN */
11130 if (player->MovPos == 0)
11132 player->is_digging = FALSE;
11133 player->is_collecting = FALSE;
11136 if (player->MovPos == 0) /* last pushing move finished */
11137 player->is_pushing = FALSE;
11139 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11141 player->is_switching = FALSE;
11142 #if USE_NEW_PUSH_DELAY
11143 player->push_delay = -1;
11145 player->push_delay = 0;
11148 return MF_NO_ACTION;
11152 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11153 return MF_NO_ACTION;
11158 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11160 if (IS_TUBE(Feld[jx][jy]) ||
11161 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11165 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11166 int tube_leave_directions[][2] =
11168 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11169 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11170 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11171 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11172 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11173 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11174 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11175 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11176 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11177 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11178 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11179 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11182 while (tube_leave_directions[i][0] != tube_element)
11185 if (tube_leave_directions[i][0] == -1) /* should not happen */
11189 if (!(tube_leave_directions[i][1] & move_direction))
11190 return MF_NO_ACTION; /* tube has no opening in this direction */
11195 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11196 old_element = Back[jx][jy];
11200 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11201 return MF_NO_ACTION; /* field has no opening in this direction */
11203 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11204 return MF_NO_ACTION; /* field has no opening in this direction */
11206 element = Feld[x][y];
11208 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11209 return MF_NO_ACTION;
11211 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11212 game.engine_version >= VERSION_IDENT(2,2,0,0))
11213 return MF_NO_ACTION;
11216 if (game.gravity && is_player && !player->is_auto_moving &&
11217 canFallDown(player) && move_direction != MV_DOWN &&
11218 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11219 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11223 if (element == EL_EMPTY_SPACE &&
11224 game.gravity && !player->is_auto_moving &&
11225 canFallDown(player) && move_direction != MV_DOWN)
11226 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11232 case EL_SP_PORT_LEFT:
11233 case EL_SP_PORT_RIGHT:
11234 case EL_SP_PORT_UP:
11235 case EL_SP_PORT_DOWN:
11236 case EL_SP_PORT_HORIZONTAL:
11237 case EL_SP_PORT_VERTICAL:
11238 case EL_SP_PORT_ANY:
11239 case EL_SP_GRAVITY_PORT_LEFT:
11240 case EL_SP_GRAVITY_PORT_RIGHT:
11241 case EL_SP_GRAVITY_PORT_UP:
11242 case EL_SP_GRAVITY_PORT_DOWN:
11244 if (!canEnterSupaplexPort(x, y, dx, dy))
11245 return MF_NO_ACTION;
11248 element != EL_SP_PORT_LEFT &&
11249 element != EL_SP_GRAVITY_PORT_LEFT &&
11250 element != EL_SP_PORT_HORIZONTAL &&
11251 element != EL_SP_PORT_ANY) ||
11253 element != EL_SP_PORT_RIGHT &&
11254 element != EL_SP_GRAVITY_PORT_RIGHT &&
11255 element != EL_SP_PORT_HORIZONTAL &&
11256 element != EL_SP_PORT_ANY) ||
11258 element != EL_SP_PORT_UP &&
11259 element != EL_SP_GRAVITY_PORT_UP &&
11260 element != EL_SP_PORT_VERTICAL &&
11261 element != EL_SP_PORT_ANY) ||
11263 element != EL_SP_PORT_DOWN &&
11264 element != EL_SP_GRAVITY_PORT_DOWN &&
11265 element != EL_SP_PORT_VERTICAL &&
11266 element != EL_SP_PORT_ANY) ||
11267 !IN_LEV_FIELD(nextx, nexty) ||
11268 !IS_FREE(nextx, nexty))
11269 return MF_NO_ACTION;
11272 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11273 element == EL_SP_GRAVITY_PORT_RIGHT ||
11274 element == EL_SP_GRAVITY_PORT_UP ||
11275 element == EL_SP_GRAVITY_PORT_DOWN)
11276 game.gravity = !game.gravity;
11278 /* automatically move to the next field with double speed */
11279 player->programmed_action = move_direction;
11281 if (player->move_delay_reset_counter == 0)
11283 player->move_delay_reset_counter = 2; /* two double speed steps */
11285 DOUBLE_PLAYER_SPEED(player);
11288 player->move_delay_reset_counter = 2;
11290 DOUBLE_PLAYER_SPEED(player);
11294 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11297 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11303 case EL_TUBE_VERTICAL:
11304 case EL_TUBE_HORIZONTAL:
11305 case EL_TUBE_VERTICAL_LEFT:
11306 case EL_TUBE_VERTICAL_RIGHT:
11307 case EL_TUBE_HORIZONTAL_UP:
11308 case EL_TUBE_HORIZONTAL_DOWN:
11309 case EL_TUBE_LEFT_UP:
11310 case EL_TUBE_LEFT_DOWN:
11311 case EL_TUBE_RIGHT_UP:
11312 case EL_TUBE_RIGHT_DOWN:
11315 int tube_enter_directions[][2] =
11317 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11318 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11319 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11320 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11321 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11322 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11323 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11324 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11325 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11326 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11327 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11328 { -1, MV_NO_MOVING }
11331 while (tube_enter_directions[i][0] != element)
11334 if (tube_enter_directions[i][0] == -1) /* should not happen */
11338 if (!(tube_enter_directions[i][1] & move_direction))
11339 return MF_NO_ACTION; /* tube has no opening in this direction */
11341 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11349 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11351 if (IS_WALKABLE(element))
11354 int sound_element = SND_ELEMENT(element);
11355 int sound_action = ACTION_WALKING;
11358 if (!ACCESS_FROM(element, opposite_direction))
11359 return MF_NO_ACTION; /* field not accessible from this direction */
11363 if (element == EL_EMPTY_SPACE &&
11364 game.gravity && !player->is_auto_moving &&
11365 canFallDown(player) && move_direction != MV_DOWN)
11366 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11369 if (IS_GATE(element))
11371 if (!player->key[element - EL_GATE_1])
11372 return MF_NO_ACTION;
11374 else if (IS_GATE_GRAY(element))
11376 if (!player->key[element - EL_GATE_1_GRAY])
11377 return MF_NO_ACTION;
11379 else if (element == EL_EXIT_OPEN ||
11380 element == EL_SP_EXIT_OPEN ||
11381 element == EL_SP_EXIT_OPENING)
11383 sound_action = ACTION_PASSING; /* player is passing exit */
11385 else if (element == EL_EMPTY)
11387 sound_action = ACTION_MOVING; /* nothing to walk on */
11390 /* play sound from background or player, whatever is available */
11391 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11392 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11394 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11399 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11401 else if (IS_PASSABLE(element))
11405 if (!canPassField(x, y, move_direction))
11406 return MF_NO_ACTION;
11411 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11412 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11413 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11414 return MF_NO_ACTION;
11416 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11417 return MF_NO_ACTION;
11422 if (!ACCESS_FROM(element, opposite_direction))
11423 return MF_NO_ACTION; /* field not accessible from this direction */
11425 if (IS_CUSTOM_ELEMENT(element) &&
11426 !ACCESS_FROM(element, opposite_direction))
11427 return MF_NO_ACTION; /* field not accessible from this direction */
11431 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11432 return MF_NO_ACTION;
11437 if (IS_EM_GATE(element))
11439 if (!player->key[element - EL_EM_GATE_1])
11440 return MF_NO_ACTION;
11442 else if (IS_EM_GATE_GRAY(element))
11444 if (!player->key[element - EL_EM_GATE_1_GRAY])
11445 return MF_NO_ACTION;
11447 else if (IS_SP_PORT(element))
11449 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11450 element == EL_SP_GRAVITY_PORT_RIGHT ||
11451 element == EL_SP_GRAVITY_PORT_UP ||
11452 element == EL_SP_GRAVITY_PORT_DOWN)
11453 game.gravity = !game.gravity;
11454 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11455 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11456 element == EL_SP_GRAVITY_ON_PORT_UP ||
11457 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11458 game.gravity = TRUE;
11459 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11460 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11461 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11462 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11463 game.gravity = FALSE;
11466 /* automatically move to the next field with double speed */
11467 player->programmed_action = move_direction;
11469 if (player->move_delay_reset_counter == 0)
11471 player->move_delay_reset_counter = 2; /* two double speed steps */
11473 DOUBLE_PLAYER_SPEED(player);
11476 player->move_delay_reset_counter = 2;
11478 DOUBLE_PLAYER_SPEED(player);
11481 PlayLevelSoundAction(x, y, ACTION_PASSING);
11485 else if (IS_DIGGABLE(element))
11489 if (mode != DF_SNAP)
11492 GfxElement[x][y] = GFX_ELEMENT(element);
11495 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11497 player->is_digging = TRUE;
11500 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11502 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11503 player->index_bit, dig_side);
11506 if (mode == DF_SNAP)
11507 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11512 else if (IS_COLLECTIBLE(element))
11516 if (is_player && mode != DF_SNAP)
11518 GfxElement[x][y] = element;
11519 player->is_collecting = TRUE;
11522 if (element == EL_SPEED_PILL)
11523 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11524 else if (element == EL_EXTRA_TIME && level.time > 0)
11527 DrawGameValue_Time(TimeLeft);
11529 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11531 player->shield_normal_time_left += 10;
11532 if (element == EL_SHIELD_DEADLY)
11533 player->shield_deadly_time_left += 10;
11535 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11537 if (player->inventory_size < MAX_INVENTORY_SIZE)
11538 player->inventory_element[player->inventory_size++] = element;
11540 DrawGameValue_Dynamite(local_player->inventory_size);
11542 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11544 player->dynabomb_count++;
11545 player->dynabombs_left++;
11547 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11549 player->dynabomb_size++;
11551 else if (element == EL_DYNABOMB_INCREASE_POWER)
11553 player->dynabomb_xl = TRUE;
11555 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11556 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11558 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11559 element - EL_KEY_1 : element - EL_EM_KEY_1);
11561 player->key[key_nr] = TRUE;
11563 DrawGameValue_Keys(player->key);
11565 redraw_mask |= REDRAW_DOOR_1;
11567 else if (IS_ENVELOPE(element))
11570 player->show_envelope = element;
11572 ShowEnvelope(element - EL_ENVELOPE_1);
11575 else if (IS_DROPPABLE(element) ||
11576 IS_THROWABLE(element)) /* can be collected and dropped */
11580 if (element_info[element].collect_count == 0)
11581 player->inventory_infinite_element = element;
11583 for (i = 0; i < element_info[element].collect_count; i++)
11584 if (player->inventory_size < MAX_INVENTORY_SIZE)
11585 player->inventory_element[player->inventory_size++] = element;
11587 DrawGameValue_Dynamite(local_player->inventory_size);
11589 else if (element_info[element].collect_count > 0)
11591 local_player->gems_still_needed -=
11592 element_info[element].collect_count;
11593 if (local_player->gems_still_needed < 0)
11594 local_player->gems_still_needed = 0;
11596 DrawGameValue_Emeralds(local_player->gems_still_needed);
11599 RaiseScoreElement(element);
11600 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11603 CheckTriggeredElementChangeByPlayer(x, y, element,
11604 CE_OTHER_GETS_COLLECTED,
11605 player->index_bit, dig_side);
11608 if (mode == DF_SNAP)
11609 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11614 else if (IS_PUSHABLE(element))
11616 if (mode == DF_SNAP && element != EL_BD_ROCK)
11617 return MF_NO_ACTION;
11619 if (CAN_FALL(element) && dy)
11620 return MF_NO_ACTION;
11622 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11623 !(element == EL_SPRING && level.use_spring_bug))
11624 return MF_NO_ACTION;
11627 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11628 ((move_direction & MV_VERTICAL &&
11629 ((element_info[element].move_pattern & MV_LEFT &&
11630 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11631 (element_info[element].move_pattern & MV_RIGHT &&
11632 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11633 (move_direction & MV_HORIZONTAL &&
11634 ((element_info[element].move_pattern & MV_UP &&
11635 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11636 (element_info[element].move_pattern & MV_DOWN &&
11637 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11638 return MF_NO_ACTION;
11642 /* do not push elements already moving away faster than player */
11643 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11644 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11645 return MF_NO_ACTION;
11647 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11648 return MF_NO_ACTION;
11654 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11656 if (player->push_delay_value == -1 || !player_was_pushing)
11657 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11659 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11661 if (player->push_delay_value == -1)
11662 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11665 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11667 if (player->push_delay_value == -1 || !player_was_pushing)
11668 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11671 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11673 if (!player->is_pushing)
11674 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11678 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11679 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11680 !player_is_pushing))
11681 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11684 if (!player->is_pushing &&
11685 game.engine_version >= VERSION_IDENT(2,2,0,7))
11686 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11690 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11691 player->push_delay, player->push_delay_value,
11692 FrameCounter, game.engine_version,
11693 player_was_pushing, player->is_pushing,
11694 element, element_info[element].token_name,
11695 GET_NEW_PUSH_DELAY(element));
11698 player->is_pushing = TRUE;
11700 if (!(IN_LEV_FIELD(nextx, nexty) &&
11701 (IS_FREE(nextx, nexty) ||
11702 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11703 IS_SB_ELEMENT(element)))))
11704 return MF_NO_ACTION;
11706 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11707 return MF_NO_ACTION;
11709 #if USE_NEW_PUSH_DELAY
11712 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11713 printf("::: ALERT: %d, %d [%d / %d]\n",
11714 player->push_delay, player->push_delay2,
11715 FrameCounter, FrameCounter / 50);
11718 if (player->push_delay == -1) /* new pushing; restart delay */
11719 player->push_delay = 0;
11721 if (player->push_delay == 0) /* new pushing; restart delay */
11722 player->push_delay = FrameCounter;
11725 #if USE_NEW_PUSH_DELAY
11727 if ( (player->push_delay > 0) != (!xxx_fr) )
11728 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11729 player->push_delay,
11730 xxx_pdv2, player->push_delay2, player->push_delay_value,
11731 FrameCounter, FrameCounter / 50);
11735 if (player->push_delay > 0 &&
11736 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11737 element != EL_SPRING && element != EL_BALLOON)
11740 if (player->push_delay < player->push_delay_value &&
11741 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11742 element != EL_SPRING && element != EL_BALLOON)
11746 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11747 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11748 element != EL_SPRING && element != EL_BALLOON)
11751 /* make sure that there is no move delay before next try to push */
11752 #if USE_NEW_MOVE_DELAY
11753 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11754 player->move_delay = 0;
11756 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11757 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11760 return MF_NO_ACTION;
11764 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11767 if (IS_SB_ELEMENT(element))
11769 if (element == EL_SOKOBAN_FIELD_FULL)
11771 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11772 local_player->sokobanfields_still_needed++;
11775 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11777 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11778 local_player->sokobanfields_still_needed--;
11781 Feld[x][y] = EL_SOKOBAN_OBJECT;
11783 if (Back[x][y] == Back[nextx][nexty])
11784 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11785 else if (Back[x][y] != 0)
11786 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11789 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11792 if (local_player->sokobanfields_still_needed == 0 &&
11793 game.emulation == EMU_SOKOBAN)
11795 player->LevelSolved = player->GameOver = TRUE;
11796 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11800 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11802 InitMovingField(x, y, move_direction);
11803 GfxAction[x][y] = ACTION_PUSHING;
11805 if (mode == DF_SNAP)
11806 ContinueMoving(x, y);
11808 MovPos[x][y] = (dx != 0 ? dx : dy);
11810 Pushed[x][y] = TRUE;
11811 Pushed[nextx][nexty] = TRUE;
11813 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11814 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11816 player->push_delay_value = -1; /* get new value later */
11818 #if USE_PUSH_BUGFIX
11819 /* now: check for element change _after_ element has been pushed! */
11821 if (game.use_bug_change_when_pushing)
11823 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11826 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11827 player->index_bit, dig_side);
11828 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11829 player->index_bit, dig_side);
11835 /* check for element change _after_ element has been pushed! */
11839 /* !!! TEST ONLY !!! */
11840 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11841 player->index_bit, dig_side);
11842 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11843 player->index_bit, dig_side);
11845 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11846 player->index_bit, dig_side);
11847 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11848 player->index_bit, dig_side);
11856 else if (IS_SWITCHABLE(element))
11858 if (PLAYER_SWITCHING(player, x, y))
11860 CheckTriggeredElementChangeByPlayer(x,y, element,
11861 CE_OTHER_GETS_PRESSED,
11862 player->index_bit, dig_side);
11867 player->is_switching = TRUE;
11868 player->switch_x = x;
11869 player->switch_y = y;
11871 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11873 if (element == EL_ROBOT_WHEEL)
11875 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11879 DrawLevelField(x, y);
11881 else if (element == EL_SP_TERMINAL)
11885 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11887 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11889 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11890 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11893 else if (IS_BELT_SWITCH(element))
11895 ToggleBeltSwitch(x, y);
11897 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11898 element == EL_SWITCHGATE_SWITCH_DOWN)
11900 ToggleSwitchgateSwitch(x, y);
11902 else if (element == EL_LIGHT_SWITCH ||
11903 element == EL_LIGHT_SWITCH_ACTIVE)
11905 ToggleLightSwitch(x, y);
11908 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11909 SND_LIGHT_SWITCH_ACTIVATING :
11910 SND_LIGHT_SWITCH_DEACTIVATING);
11913 else if (element == EL_TIMEGATE_SWITCH)
11915 ActivateTimegateSwitch(x, y);
11917 else if (element == EL_BALLOON_SWITCH_LEFT ||
11918 element == EL_BALLOON_SWITCH_RIGHT ||
11919 element == EL_BALLOON_SWITCH_UP ||
11920 element == EL_BALLOON_SWITCH_DOWN ||
11921 element == EL_BALLOON_SWITCH_ANY)
11923 if (element == EL_BALLOON_SWITCH_ANY)
11924 game.balloon_dir = move_direction;
11926 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11927 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11928 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11929 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11932 else if (element == EL_LAMP)
11934 Feld[x][y] = EL_LAMP_ACTIVE;
11935 local_player->lights_still_needed--;
11937 DrawLevelField(x, y);
11939 else if (element == EL_TIME_ORB_FULL)
11941 Feld[x][y] = EL_TIME_ORB_EMPTY;
11943 DrawGameValue_Time(TimeLeft);
11945 DrawLevelField(x, y);
11948 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11952 CheckTriggeredElementChangeByPlayer(x, y, element,
11953 CE_OTHER_IS_SWITCHING,
11954 player->index_bit, dig_side);
11956 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11957 player->index_bit, dig_side);
11963 if (!PLAYER_SWITCHING(player, x, y))
11965 player->is_switching = TRUE;
11966 player->switch_x = x;
11967 player->switch_y = y;
11970 /* !!! TEST ONLY !!! */
11971 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11972 player->index_bit, dig_side);
11973 CheckTriggeredElementChangeByPlayer(x, y, element,
11974 CE_OTHER_IS_SWITCHING,
11975 player->index_bit, dig_side);
11977 CheckTriggeredElementChangeByPlayer(x, y, element,
11978 CE_OTHER_IS_SWITCHING,
11979 player->index_bit, dig_side);
11980 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11981 player->index_bit, dig_side);
11986 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11987 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11988 player->index_bit, dig_side);
11989 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11990 player->index_bit, dig_side);
11992 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11993 player->index_bit, dig_side);
11994 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11995 player->index_bit, dig_side);
11999 return MF_NO_ACTION;
12002 #if USE_NEW_PUSH_DELAY
12003 player->push_delay = -1;
12005 player->push_delay = 0;
12008 if (Feld[x][y] != element) /* really digged/collected something */
12009 player->is_collecting = !player->is_digging;
12014 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12016 int jx = player->jx, jy = player->jy;
12017 int x = jx + dx, y = jy + dy;
12018 int snap_direction = (dx == -1 ? MV_LEFT :
12019 dx == +1 ? MV_RIGHT :
12021 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12024 if (player->MovPos != 0)
12027 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12031 if (!player->active || !IN_LEV_FIELD(x, y))
12039 if (player->MovPos == 0)
12040 player->is_pushing = FALSE;
12042 player->is_snapping = FALSE;
12044 if (player->MovPos == 0)
12046 player->is_moving = FALSE;
12047 player->is_digging = FALSE;
12048 player->is_collecting = FALSE;
12054 if (player->is_snapping)
12057 player->MovDir = snap_direction;
12060 if (player->MovPos == 0)
12063 player->is_moving = FALSE;
12064 player->is_digging = FALSE;
12065 player->is_collecting = FALSE;
12068 player->is_dropping = FALSE;
12070 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12073 player->is_snapping = TRUE;
12076 if (player->MovPos == 0)
12079 player->is_moving = FALSE;
12080 player->is_digging = FALSE;
12081 player->is_collecting = FALSE;
12085 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12086 DrawLevelField(player->last_jx, player->last_jy);
12089 DrawLevelField(x, y);
12098 boolean DropElement(struct PlayerInfo *player)
12100 int old_element, new_element;
12101 int dropx = player->jx, dropy = player->jy;
12102 int drop_direction = player->MovDir;
12104 int drop_side = drop_direction;
12106 static int trigger_sides[4] =
12108 CH_SIDE_LEFT, /* dropping left */
12109 CH_SIDE_RIGHT, /* dropping right */
12110 CH_SIDE_TOP, /* dropping up */
12111 CH_SIDE_BOTTOM, /* dropping down */
12113 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12115 int drop_element = (player->inventory_size > 0 ?
12116 player->inventory_element[player->inventory_size - 1] :
12117 player->inventory_infinite_element != EL_UNDEFINED ?
12118 player->inventory_infinite_element :
12119 player->dynabombs_left > 0 ?
12120 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12123 if (IS_THROWABLE(drop_element))
12125 dropx += GET_DX_FROM_DIR(drop_direction);
12126 dropy += GET_DY_FROM_DIR(drop_direction);
12128 if (!IN_LEV_FIELD(dropx, dropy))
12132 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12133 new_element = drop_element; /* default: no change when dropping */
12135 /* check if player is active, not moving and ready to drop */
12136 if (!player->active || player->MovPos || player->drop_delay > 0)
12139 /* check if player has anything that can be dropped */
12141 if (new_element == EL_UNDEFINED)
12144 if (player->inventory_size == 0 &&
12145 player->inventory_infinite_element == EL_UNDEFINED &&
12146 player->dynabombs_left == 0)
12150 /* check if anything can be dropped at the current position */
12151 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12154 /* collected custom elements can only be dropped on empty fields */
12156 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12159 if (player->inventory_size > 0 &&
12160 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12161 && old_element != EL_EMPTY)
12165 if (old_element != EL_EMPTY)
12166 Back[dropx][dropy] = old_element; /* store old element on this field */
12168 ResetGfxAnimation(dropx, dropy);
12169 ResetRandomAnimationValue(dropx, dropy);
12171 if (player->inventory_size > 0 ||
12172 player->inventory_infinite_element != EL_UNDEFINED)
12174 if (player->inventory_size > 0)
12176 player->inventory_size--;
12179 new_element = player->inventory_element[player->inventory_size];
12182 DrawGameValue_Dynamite(local_player->inventory_size);
12184 if (new_element == EL_DYNAMITE)
12185 new_element = EL_DYNAMITE_ACTIVE;
12186 else if (new_element == EL_SP_DISK_RED)
12187 new_element = EL_SP_DISK_RED_ACTIVE;
12190 Feld[dropx][dropy] = new_element;
12192 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12193 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12194 el2img(Feld[dropx][dropy]), 0);
12196 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12199 /* needed if previous element just changed to "empty" in the last frame */
12200 Changed[dropx][dropy] = 0; /* allow another change */
12204 /* !!! TEST ONLY !!! */
12205 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12206 player->index_bit, drop_side);
12207 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12208 CE_OTHER_GETS_DROPPED,
12209 player->index_bit, drop_side);
12211 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12212 CE_OTHER_GETS_DROPPED,
12213 player->index_bit, drop_side);
12214 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12215 player->index_bit, drop_side);
12218 TestIfElementTouchesCustomElement(dropx, dropy);
12220 else /* player is dropping a dyna bomb */
12222 player->dynabombs_left--;
12225 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12228 Feld[dropx][dropy] = new_element;
12230 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12231 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12232 el2img(Feld[dropx][dropy]), 0);
12234 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12241 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12244 InitField_WithBug1(dropx, dropy, FALSE);
12246 InitField(dropx, dropy, FALSE);
12247 if (CAN_MOVE(Feld[dropx][dropy]))
12248 InitMovDir(dropx, dropy);
12252 new_element = Feld[dropx][dropy]; /* element might have changed */
12254 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12255 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12258 int move_stepsize = element_info[new_element].move_stepsize;
12260 int move_direction, nextx, nexty;
12262 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12263 MovDir[dropx][dropy] = drop_direction;
12265 move_direction = MovDir[dropx][dropy];
12266 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12267 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12270 Changed[dropx][dropy] = 0; /* allow another change */
12271 CheckCollision[dropx][dropy] = 2;
12274 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12277 WasJustMoving[dropx][dropy] = 3;
12280 InitMovingField(dropx, dropy, move_direction);
12281 ContinueMoving(dropx, dropy);
12286 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12289 Changed[dropx][dropy] = 0; /* allow another change */
12292 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12294 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12295 CE_HITTING_SOMETHING, move_direction);
12303 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12308 player->drop_delay = 8 + 8 + 8;
12312 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12317 player->is_dropping = TRUE;
12323 /* ------------------------------------------------------------------------- */
12324 /* game sound playing functions */
12325 /* ------------------------------------------------------------------------- */
12327 static int *loop_sound_frame = NULL;
12328 static int *loop_sound_volume = NULL;
12330 void InitPlayLevelSound()
12332 int num_sounds = getSoundListSize();
12334 checked_free(loop_sound_frame);
12335 checked_free(loop_sound_volume);
12337 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12338 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12341 static void PlayLevelSound(int x, int y, int nr)
12343 int sx = SCREENX(x), sy = SCREENY(y);
12344 int volume, stereo_position;
12345 int max_distance = 8;
12346 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12348 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12349 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12352 if (!IN_LEV_FIELD(x, y) ||
12353 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12354 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12357 volume = SOUND_MAX_VOLUME;
12359 if (!IN_SCR_FIELD(sx, sy))
12361 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12362 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12364 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12367 stereo_position = (SOUND_MAX_LEFT +
12368 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12369 (SCR_FIELDX + 2 * max_distance));
12371 if (IS_LOOP_SOUND(nr))
12373 /* This assures that quieter loop sounds do not overwrite louder ones,
12374 while restarting sound volume comparison with each new game frame. */
12376 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12379 loop_sound_volume[nr] = volume;
12380 loop_sound_frame[nr] = FrameCounter;
12383 PlaySoundExt(nr, volume, stereo_position, type);
12386 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12388 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12389 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12390 y < LEVELY(BY1) ? LEVELY(BY1) :
12391 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12395 static void PlayLevelSoundAction(int x, int y, int action)
12397 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12400 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12402 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12404 if (sound_effect != SND_UNDEFINED)
12405 PlayLevelSound(x, y, sound_effect);
12408 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12411 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12413 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12414 PlayLevelSound(x, y, sound_effect);
12417 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12419 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12421 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12422 PlayLevelSound(x, y, sound_effect);
12425 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12427 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12429 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12430 StopSound(sound_effect);
12433 static void PlayLevelMusic()
12435 if (levelset.music[level_nr] != MUS_UNDEFINED)
12436 PlayMusic(levelset.music[level_nr]); /* from config file */
12438 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12441 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12443 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12446 if (sample == SAMPLE_bug)
12447 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12453 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12457 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12461 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12465 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12469 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12473 PlayLevelSoundElementAction(x, y, EL_BUG, ACTION_MOVING);
12477 PlayLevelSoundElementAction(x, y, EL_SPACESHIP, ACTION_MOVING);
12480 case SAMPLE_android:
12481 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12484 case SAMPLE_spring:
12485 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12489 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12493 PlayLevelSoundElementAction(x, y, EL_YAMYAM, ACTION_WAITING);
12497 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12500 case SAMPLE_collect:
12501 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12504 case SAMPLE_diamond:
12505 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12508 case SAMPLE_squash:
12509 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12512 case SAMPLE_wonderfall:
12513 PlayLevelSoundElementAction(x, y, EL_MAGIC_WALL, ACTION_FILLING);
12517 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12521 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12525 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12529 PlayLevelSound(x, y, SND_ACID_SPLASHING);
12533 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12537 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12540 case SAMPLE_wonder:
12541 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12545 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12549 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12552 case SAMPLE_dynamite:
12553 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12557 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12561 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12565 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
12569 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12573 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12577 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12581 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12586 void RaiseScore(int value)
12588 local_player->score += value;
12590 DrawGameValue_Score(local_player->score);
12593 void RaiseScoreElement(int element)
12598 case EL_BD_DIAMOND:
12599 case EL_EMERALD_YELLOW:
12600 case EL_EMERALD_RED:
12601 case EL_EMERALD_PURPLE:
12602 case EL_SP_INFOTRON:
12603 RaiseScore(level.score[SC_EMERALD]);
12606 RaiseScore(level.score[SC_DIAMOND]);
12609 RaiseScore(level.score[SC_CRYSTAL]);
12612 RaiseScore(level.score[SC_PEARL]);
12615 case EL_BD_BUTTERFLY:
12616 case EL_SP_ELECTRON:
12617 RaiseScore(level.score[SC_BUG]);
12620 case EL_BD_FIREFLY:
12621 case EL_SP_SNIKSNAK:
12622 RaiseScore(level.score[SC_SPACESHIP]);
12625 case EL_DARK_YAMYAM:
12626 RaiseScore(level.score[SC_YAMYAM]);
12629 RaiseScore(level.score[SC_ROBOT]);
12632 RaiseScore(level.score[SC_PACMAN]);
12635 RaiseScore(level.score[SC_NUT]);
12638 case EL_SP_DISK_RED:
12639 case EL_DYNABOMB_INCREASE_NUMBER:
12640 case EL_DYNABOMB_INCREASE_SIZE:
12641 case EL_DYNABOMB_INCREASE_POWER:
12642 RaiseScore(level.score[SC_DYNAMITE]);
12644 case EL_SHIELD_NORMAL:
12645 case EL_SHIELD_DEADLY:
12646 RaiseScore(level.score[SC_SHIELD]);
12648 case EL_EXTRA_TIME:
12649 RaiseScore(level.score[SC_TIME_BONUS]);
12655 RaiseScore(level.score[SC_KEY]);
12658 RaiseScore(element_info[element].collect_score);
12663 void RequestQuitGame(boolean ask_if_really_quit)
12665 if (AllPlayersGone ||
12666 !ask_if_really_quit ||
12667 level_editor_test_game ||
12668 Request("Do you really want to quit the game ?",
12669 REQ_ASK | REQ_STAY_CLOSED))
12671 #if defined(NETWORK_AVALIABLE)
12672 if (options.network)
12673 SendToServer_StopPlaying();
12677 game_status = GAME_MODE_MAIN;
12685 if (tape.playing && tape.deactivate_display)
12686 TapeDeactivateDisplayOff(TRUE);
12689 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12692 if (tape.playing && tape.deactivate_display)
12693 TapeDeactivateDisplayOn();
12700 /* ---------- new game button stuff ---------------------------------------- */
12702 /* graphic position values for game buttons */
12703 #define GAME_BUTTON_XSIZE 30
12704 #define GAME_BUTTON_YSIZE 30
12705 #define GAME_BUTTON_XPOS 5
12706 #define GAME_BUTTON_YPOS 215
12707 #define SOUND_BUTTON_XPOS 5
12708 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12710 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12711 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12712 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12713 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12714 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12715 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12722 } gamebutton_info[NUM_GAME_BUTTONS] =
12725 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12730 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12731 GAME_CTRL_ID_PAUSE,
12735 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12740 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12741 SOUND_CTRL_ID_MUSIC,
12742 "background music on/off"
12745 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12746 SOUND_CTRL_ID_LOOPS,
12747 "sound loops on/off"
12750 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12751 SOUND_CTRL_ID_SIMPLE,
12752 "normal sounds on/off"
12756 void CreateGameButtons()
12760 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12762 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12763 struct GadgetInfo *gi;
12766 unsigned long event_mask;
12767 int gd_xoffset, gd_yoffset;
12768 int gd_x1, gd_x2, gd_y1, gd_y2;
12771 gd_xoffset = gamebutton_info[i].x;
12772 gd_yoffset = gamebutton_info[i].y;
12773 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12774 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12776 if (id == GAME_CTRL_ID_STOP ||
12777 id == GAME_CTRL_ID_PAUSE ||
12778 id == GAME_CTRL_ID_PLAY)
12780 button_type = GD_TYPE_NORMAL_BUTTON;
12782 event_mask = GD_EVENT_RELEASED;
12783 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12784 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12788 button_type = GD_TYPE_CHECK_BUTTON;
12790 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12791 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12792 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12793 event_mask = GD_EVENT_PRESSED;
12794 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12795 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12798 gi = CreateGadget(GDI_CUSTOM_ID, id,
12799 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12800 GDI_X, DX + gd_xoffset,
12801 GDI_Y, DY + gd_yoffset,
12802 GDI_WIDTH, GAME_BUTTON_XSIZE,
12803 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12804 GDI_TYPE, button_type,
12805 GDI_STATE, GD_BUTTON_UNPRESSED,
12806 GDI_CHECKED, checked,
12807 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12808 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12809 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12810 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12811 GDI_EVENT_MASK, event_mask,
12812 GDI_CALLBACK_ACTION, HandleGameButtons,
12816 Error(ERR_EXIT, "cannot create gadget");
12818 game_gadget[id] = gi;
12822 void FreeGameButtons()
12826 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12827 FreeGadget(game_gadget[i]);
12830 static void MapGameButtons()
12834 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12835 MapGadget(game_gadget[i]);
12838 void UnmapGameButtons()
12842 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12843 UnmapGadget(game_gadget[i]);
12846 static void HandleGameButtons(struct GadgetInfo *gi)
12848 int id = gi->custom_id;
12850 if (game_status != GAME_MODE_PLAYING)
12855 case GAME_CTRL_ID_STOP:
12856 RequestQuitGame(TRUE);
12859 case GAME_CTRL_ID_PAUSE:
12860 if (options.network)
12862 #if defined(NETWORK_AVALIABLE)
12864 SendToServer_ContinuePlaying();
12866 SendToServer_PausePlaying();
12870 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12873 case GAME_CTRL_ID_PLAY:
12876 #if defined(NETWORK_AVALIABLE)
12877 if (options.network)
12878 SendToServer_ContinuePlaying();
12882 tape.pausing = FALSE;
12883 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12888 case SOUND_CTRL_ID_MUSIC:
12889 if (setup.sound_music)
12891 setup.sound_music = FALSE;
12894 else if (audio.music_available)
12896 setup.sound = setup.sound_music = TRUE;
12898 SetAudioMode(setup.sound);
12904 case SOUND_CTRL_ID_LOOPS:
12905 if (setup.sound_loops)
12906 setup.sound_loops = FALSE;
12907 else if (audio.loops_available)
12909 setup.sound = setup.sound_loops = TRUE;
12910 SetAudioMode(setup.sound);
12914 case SOUND_CTRL_ID_SIMPLE:
12915 if (setup.sound_simple)
12916 setup.sound_simple = FALSE;
12917 else if (audio.sound_available)
12919 setup.sound = setup.sound_simple = TRUE;
12920 SetAudioMode(setup.sound);