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_Level(level_nr);
1123 DrawGameValue_Emeralds(emeralds);
1124 DrawGameValue_Dynamite(dynamite);
1125 DrawGameValue_Score(score);
1126 DrawGameValue_Time(time);
1128 DrawGameValue_Keys(key);
1131 void DrawGameDoorValues()
1135 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1137 DrawGameDoorValues_EM();
1142 DrawGameValue_Level(level_nr);
1144 DrawGameValue_Emeralds(local_player->gems_still_needed);
1145 DrawGameValue_Dynamite(local_player->inventory_size);
1146 DrawGameValue_Score(local_player->score);
1147 DrawGameValue_Time(TimeLeft);
1149 for (i = 0; i < MAX_PLAYERS; i++)
1150 DrawGameValue_Keys(stored_player[i].key);
1153 static void resolve_group_element(int group_element, int recursion_depth)
1155 static int group_nr;
1156 static struct ElementGroupInfo *group;
1157 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1160 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1162 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1163 group_element - EL_GROUP_START + 1);
1165 /* replace element which caused too deep recursion by question mark */
1166 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1171 if (recursion_depth == 0) /* initialization */
1173 group = element_info[group_element].group;
1174 group_nr = group_element - EL_GROUP_START;
1176 group->num_elements_resolved = 0;
1177 group->choice_pos = 0;
1180 for (i = 0; i < actual_group->num_elements; i++)
1182 int element = actual_group->element[i];
1184 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1187 if (IS_GROUP_ELEMENT(element))
1188 resolve_group_element(element, recursion_depth + 1);
1191 group->element_resolved[group->num_elements_resolved++] = element;
1192 element_info[element].in_group[group_nr] = TRUE;
1197 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1199 printf("::: group %d: %d resolved elements\n",
1200 group_element - EL_GROUP_START, group->num_elements_resolved);
1201 for (i = 0; i < group->num_elements_resolved; i++)
1202 printf("::: - %d ['%s']\n", group->element_resolved[i],
1203 element_info[group->element_resolved[i]].token_name);
1210 =============================================================================
1212 -----------------------------------------------------------------------------
1213 initialize game engine due to level / tape version number
1214 =============================================================================
1217 static void InitGameEngine()
1221 /* set game engine from tape file when re-playing, else from level file */
1222 game.engine_version = (tape.playing ? tape.engine_version :
1223 level.game_version);
1225 /* ---------------------------------------------------------------------- */
1226 /* set flags for bugs and changes according to active game engine version */
1227 /* ---------------------------------------------------------------------- */
1231 Before 3.1.0, custom elements that "change when pushing" changed directly
1232 after the player started pushing them (until then handled in "DigField()").
1233 Since 3.1.0, these custom elements are not changed until the "pushing"
1234 move of the element is finished (now handled in "ContinueMoving()").
1236 Affected levels/tapes:
1237 The first condition is generally needed for all levels/tapes before version
1238 3.1.0, which might use the old behaviour before it was changed; known tapes
1239 that are affected are some tapes from the level set "Walpurgis Gardens" by
1241 The second condition is an exception from the above case and is needed for
1242 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1243 above (including some development versions of 3.1.0), but before it was
1244 known that this change would break tapes like the above and was fixed in
1245 3.1.1, so that the changed behaviour was active although the engine version
1246 while recording maybe was before 3.1.0. There is at least one tape that is
1247 affected by this exception, which is the tape for the one-level set "Bug
1248 Machine" by Juergen Bonhagen.
1251 game.use_bug_change_when_pushing =
1252 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1254 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1255 tape.game_version < VERSION_IDENT(3,1,1,0)));
1257 /* ---------------------------------------------------------------------- */
1259 /* dynamically adjust element properties according to game engine version */
1260 InitElementPropertiesEngine(game.engine_version);
1263 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1264 printf(" tape version == %06d [%s] [file: %06d]\n",
1265 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1267 printf(" => game.engine_version == %06d\n", game.engine_version);
1270 /* ---------- recursively resolve group elements ------------------------- */
1272 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1273 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1274 element_info[i].in_group[j] = FALSE;
1276 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1277 resolve_group_element(EL_GROUP_START + i, 0);
1279 /* ---------- initialize player's initial move delay --------------------- */
1281 #if USE_NEW_MOVE_DELAY
1282 /* dynamically adjust player properties according to level information */
1283 game.initial_move_delay_value =
1284 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1286 /* dynamically adjust player properties according to game engine version */
1287 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1288 game.initial_move_delay_value : 0);
1290 /* dynamically adjust player properties according to game engine version */
1291 game.initial_move_delay =
1292 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1293 INITIAL_MOVE_DELAY_OFF);
1295 /* dynamically adjust player properties according to level information */
1296 game.initial_move_delay_value =
1297 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1300 /* ---------- initialize player's initial push delay --------------------- */
1302 /* dynamically adjust player properties according to game engine version */
1303 game.initial_push_delay_value =
1304 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1306 /* ---------- initialize changing elements ------------------------------- */
1308 /* initialize changing elements information */
1309 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1311 struct ElementInfo *ei = &element_info[i];
1313 /* this pointer might have been changed in the level editor */
1314 ei->change = &ei->change_page[0];
1316 if (!IS_CUSTOM_ELEMENT(i))
1318 ei->change->target_element = EL_EMPTY_SPACE;
1319 ei->change->delay_fixed = 0;
1320 ei->change->delay_random = 0;
1321 ei->change->delay_frames = 1;
1324 ei->change_events = CE_BITMASK_DEFAULT;
1325 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1327 ei->event_page_nr[j] = 0;
1328 ei->event_page[j] = &ei->change_page[0];
1332 /* add changing elements from pre-defined list */
1333 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1335 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1336 struct ElementInfo *ei = &element_info[ch_delay->element];
1338 ei->change->target_element = ch_delay->target_element;
1339 ei->change->delay_fixed = ch_delay->change_delay;
1341 ei->change->pre_change_function = ch_delay->pre_change_function;
1342 ei->change->change_function = ch_delay->change_function;
1343 ei->change->post_change_function = ch_delay->post_change_function;
1345 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1348 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1353 /* add change events from custom element configuration */
1354 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1356 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1358 for (j = 0; j < ei->num_change_pages; j++)
1360 if (!ei->change_page[j].can_change)
1363 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1365 /* only add event page for the first page found with this event */
1366 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1367 !(ei->change_events & CH_EVENT_BIT(k)))
1369 ei->change_events |= CH_EVENT_BIT(k);
1370 ei->event_page_nr[k] = j;
1371 ei->event_page[k] = &ei->change_page[j];
1379 /* add change events from custom element configuration */
1380 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1382 int element = EL_CUSTOM_START + i;
1384 /* only add custom elements that change after fixed/random frame delay */
1385 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1386 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1390 /* ---------- initialize run-time trigger player and element ------------- */
1392 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1394 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1396 for (j = 0; j < ei->num_change_pages; j++)
1398 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1399 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1403 /* ---------- initialize trigger events ---------------------------------- */
1405 /* initialize trigger events information */
1406 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1407 trigger_events[i] = EP_BITMASK_DEFAULT;
1410 /* add trigger events from element change event properties */
1411 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1413 struct ElementInfo *ei = &element_info[i];
1415 for (j = 0; j < ei->num_change_pages; j++)
1417 if (!ei->change_page[j].can_change)
1420 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1422 int trigger_element = ei->change_page[j].trigger_element;
1424 if (IS_GROUP_ELEMENT(trigger_element))
1426 struct ElementGroupInfo *group = element_info[trigger_element].group;
1428 for (k = 0; k < group->num_elements_resolved; k++)
1429 trigger_events[group->element_resolved[k]]
1430 |= ei->change_page[j].events;
1433 trigger_events[trigger_element] |= ei->change_page[j].events;
1438 /* add trigger events from element change event properties */
1439 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1440 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1441 trigger_events[element_info[i].change->trigger_element] |=
1442 element_info[i].change->events;
1445 /* ---------- initialize push delay -------------------------------------- */
1447 /* initialize push delay values to default */
1448 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1450 if (!IS_CUSTOM_ELEMENT(i))
1452 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1453 element_info[i].push_delay_random = game.default_push_delay_random;
1457 /* set push delay value for certain elements from pre-defined list */
1458 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1460 int e = push_delay_list[i].element;
1462 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1463 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1466 /* set push delay value for Supaplex elements for newer engine versions */
1467 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1469 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1471 if (IS_SP_ELEMENT(i))
1473 #if USE_NEW_MOVE_STYLE
1474 /* set SP push delay to just enough to push under a falling zonk */
1475 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1477 element_info[i].push_delay_fixed = delay;
1478 element_info[i].push_delay_random = 0;
1480 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1481 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1487 /* ---------- initialize move stepsize ----------------------------------- */
1489 /* initialize move stepsize values to default */
1490 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1491 if (!IS_CUSTOM_ELEMENT(i))
1492 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1494 /* set move stepsize value for certain elements from pre-defined list */
1495 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1497 int e = move_stepsize_list[i].element;
1499 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1503 /* ---------- initialize move dig/leave ---------------------------------- */
1505 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1507 element_info[i].can_leave_element = FALSE;
1508 element_info[i].can_leave_element_last = FALSE;
1512 /* ---------- initialize gem count --------------------------------------- */
1514 /* initialize gem count values for each element */
1515 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1516 if (!IS_CUSTOM_ELEMENT(i))
1517 element_info[i].collect_count = 0;
1519 /* add gem count values for all elements from pre-defined list */
1520 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1521 element_info[collect_count_list[i].element].collect_count =
1522 collect_count_list[i].count;
1524 /* ---------- initialize access direction -------------------------------- */
1526 /* initialize access direction values to default (access from every side) */
1527 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1528 if (!IS_CUSTOM_ELEMENT(i))
1529 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1531 /* set access direction value for certain elements from pre-defined list */
1532 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1533 element_info[access_direction_list[i].element].access_direction =
1534 access_direction_list[i].direction;
1539 =============================================================================
1541 -----------------------------------------------------------------------------
1542 initialize and start new game
1543 =============================================================================
1548 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1549 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1550 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1557 #if USE_NEW_AMOEBA_CODE
1558 printf("Using new amoeba code.\n");
1560 printf("Using old amoeba code.\n");
1565 /* don't play tapes over network */
1566 network_playing = (options.network && !tape.playing);
1568 for (i = 0; i < MAX_PLAYERS; i++)
1570 struct PlayerInfo *player = &stored_player[i];
1572 player->index_nr = i;
1573 player->index_bit = (1 << i);
1574 player->element_nr = EL_PLAYER_1 + i;
1576 player->present = FALSE;
1577 player->active = FALSE;
1580 player->effective_action = 0;
1581 player->programmed_action = 0;
1584 player->gems_still_needed = level.gems_needed;
1585 player->sokobanfields_still_needed = 0;
1586 player->lights_still_needed = 0;
1587 player->friends_still_needed = 0;
1589 for (j = 0; j < MAX_KEYS; j++)
1590 player->key[j] = FALSE;
1592 player->dynabomb_count = 0;
1593 player->dynabomb_size = 1;
1594 player->dynabombs_left = 0;
1595 player->dynabomb_xl = FALSE;
1597 player->MovDir = MV_NO_MOVING;
1600 player->GfxDir = MV_NO_MOVING;
1601 player->GfxAction = ACTION_DEFAULT;
1603 player->StepFrame = 0;
1605 player->use_murphy_graphic = FALSE;
1607 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1608 player->block_delay = -1; /* initialized in InitPlayerField() */
1610 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1612 player->actual_frame_counter = 0;
1614 player->step_counter = 0;
1616 player->last_move_dir = MV_NO_MOVING;
1618 player->is_waiting = FALSE;
1619 player->is_moving = FALSE;
1620 player->is_auto_moving = FALSE;
1621 player->is_digging = FALSE;
1622 player->is_snapping = FALSE;
1623 player->is_collecting = FALSE;
1624 player->is_pushing = FALSE;
1625 player->is_switching = FALSE;
1626 player->is_dropping = FALSE;
1628 player->is_bored = FALSE;
1629 player->is_sleeping = FALSE;
1631 player->frame_counter_bored = -1;
1632 player->frame_counter_sleeping = -1;
1634 player->anim_delay_counter = 0;
1635 player->post_delay_counter = 0;
1637 player->action_waiting = ACTION_DEFAULT;
1638 player->last_action_waiting = ACTION_DEFAULT;
1639 player->special_action_bored = ACTION_DEFAULT;
1640 player->special_action_sleeping = ACTION_DEFAULT;
1642 player->num_special_action_bored = 0;
1643 player->num_special_action_sleeping = 0;
1645 /* determine number of special actions for bored and sleeping animation */
1646 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1648 boolean found = FALSE;
1650 for (k = 0; k < NUM_DIRECTIONS; k++)
1651 if (el_act_dir2img(player->element_nr, j, k) !=
1652 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1656 player->num_special_action_bored++;
1660 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1662 boolean found = FALSE;
1664 for (k = 0; k < NUM_DIRECTIONS; k++)
1665 if (el_act_dir2img(player->element_nr, j, k) !=
1666 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1670 player->num_special_action_sleeping++;
1675 player->switch_x = -1;
1676 player->switch_y = -1;
1678 player->show_envelope = 0;
1680 player->move_delay = game.initial_move_delay;
1681 player->move_delay_value = game.initial_move_delay_value;
1683 player->move_delay_reset_counter = 0;
1685 #if USE_NEW_PUSH_DELAY
1686 player->push_delay = -1; /* initialized when pushing starts */
1687 player->push_delay_value = game.initial_push_delay_value;
1689 player->push_delay = 0;
1690 player->push_delay_value = game.initial_push_delay_value;
1693 player->drop_delay = 0;
1695 player->last_jx = player->last_jy = 0;
1696 player->jx = player->jy = 0;
1698 player->shield_normal_time_left = 0;
1699 player->shield_deadly_time_left = 0;
1701 player->inventory_infinite_element = EL_UNDEFINED;
1702 player->inventory_size = 0;
1704 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1705 SnapField(player, 0, 0);
1707 player->LevelSolved = FALSE;
1708 player->GameOver = FALSE;
1711 network_player_action_received = FALSE;
1713 #if defined(NETWORK_AVALIABLE)
1714 /* initial null action */
1715 if (network_playing)
1716 SendToServer_MovePlayer(MV_NO_MOVING);
1725 TimeLeft = level.time;
1728 ScreenMovDir = MV_NO_MOVING;
1732 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1734 AllPlayersGone = FALSE;
1736 game.yamyam_content_nr = 0;
1737 game.magic_wall_active = FALSE;
1738 game.magic_wall_time_left = 0;
1739 game.light_time_left = 0;
1740 game.timegate_time_left = 0;
1741 game.switchgate_pos = 0;
1742 game.balloon_dir = MV_NO_MOVING;
1743 game.gravity = level.initial_gravity;
1744 game.explosions_delayed = TRUE;
1746 game.envelope_active = FALSE;
1748 for (i = 0; i < NUM_BELTS; i++)
1750 game.belt_dir[i] = MV_NO_MOVING;
1751 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1754 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1755 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1757 for (x = 0; x < lev_fieldx; x++)
1759 for (y = 0; y < lev_fieldy; y++)
1761 Feld[x][y] = level.field[x][y];
1762 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1763 ChangeDelay[x][y] = 0;
1764 ChangePage[x][y] = -1;
1765 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1767 WasJustMoving[x][y] = 0;
1768 WasJustFalling[x][y] = 0;
1769 CheckCollision[x][y] = 0;
1771 Pushed[x][y] = FALSE;
1773 Changed[x][y] = CE_BITMASK_DEFAULT;
1774 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1776 ExplodePhase[x][y] = 0;
1777 ExplodeDelay[x][y] = 0;
1778 ExplodeField[x][y] = EX_TYPE_NONE;
1780 RunnerVisit[x][y] = 0;
1781 PlayerVisit[x][y] = 0;
1784 GfxRandom[x][y] = INIT_GFX_RANDOM();
1785 GfxElement[x][y] = EL_UNDEFINED;
1786 GfxAction[x][y] = ACTION_DEFAULT;
1787 GfxDir[x][y] = MV_NO_MOVING;
1791 for (y = 0; y < lev_fieldy; y++)
1793 for (x = 0; x < lev_fieldx; x++)
1795 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1797 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1799 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1802 InitField(x, y, TRUE);
1808 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1809 emulate_sb ? EMU_SOKOBAN :
1810 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1812 /* initialize explosion and ignition delay */
1813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1815 if (!IS_CUSTOM_ELEMENT(i))
1818 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1819 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1820 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1821 int last_phase = (num_phase + 1) * delay;
1822 int half_phase = (num_phase / 2) * delay;
1824 element_info[i].explosion_delay = last_phase - 1;
1825 element_info[i].ignition_delay = half_phase;
1828 if (i == EL_BLACK_ORB)
1829 element_info[i].ignition_delay = 0;
1831 if (i == EL_BLACK_ORB)
1832 element_info[i].ignition_delay = 1;
1837 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1838 element_info[i].explosion_delay = 1;
1840 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1841 element_info[i].ignition_delay = 1;
1845 /* correct non-moving belts to start moving left */
1846 for (i = 0; i < NUM_BELTS; i++)
1847 if (game.belt_dir[i] == MV_NO_MOVING)
1848 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1850 /* check if any connected player was not found in playfield */
1851 for (i = 0; i < MAX_PLAYERS; i++)
1853 struct PlayerInfo *player = &stored_player[i];
1855 if (player->connected && !player->present)
1857 for (j = 0; j < MAX_PLAYERS; j++)
1859 struct PlayerInfo *some_player = &stored_player[j];
1860 int jx = some_player->jx, jy = some_player->jy;
1862 /* assign first free player found that is present in the playfield */
1863 if (some_player->present && !some_player->connected)
1865 player->present = TRUE;
1866 player->active = TRUE;
1868 some_player->present = FALSE;
1869 some_player->active = FALSE;
1872 player->element_nr = some_player->element_nr;
1875 #if USE_NEW_BLOCK_STYLE
1876 player->block_last_field = some_player->block_last_field;
1877 player->block_delay = some_player->block_delay;
1880 StorePlayer[jx][jy] = player->element_nr;
1881 player->jx = player->last_jx = jx;
1882 player->jy = player->last_jy = jy;
1892 /* when playing a tape, eliminate all players which do not participate */
1894 for (i = 0; i < MAX_PLAYERS; i++)
1896 if (stored_player[i].active && !tape.player_participates[i])
1898 struct PlayerInfo *player = &stored_player[i];
1899 int jx = player->jx, jy = player->jy;
1901 player->active = FALSE;
1902 StorePlayer[jx][jy] = 0;
1903 Feld[jx][jy] = EL_EMPTY;
1907 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1909 /* when in single player mode, eliminate all but the first active player */
1911 for (i = 0; i < MAX_PLAYERS; i++)
1913 if (stored_player[i].active)
1915 for (j = i + 1; j < MAX_PLAYERS; j++)
1917 if (stored_player[j].active)
1919 struct PlayerInfo *player = &stored_player[j];
1920 int jx = player->jx, jy = player->jy;
1922 player->active = FALSE;
1923 player->present = FALSE;
1925 StorePlayer[jx][jy] = 0;
1926 Feld[jx][jy] = EL_EMPTY;
1933 /* when recording the game, store which players take part in the game */
1936 for (i = 0; i < MAX_PLAYERS; i++)
1937 if (stored_player[i].active)
1938 tape.player_participates[i] = TRUE;
1943 for (i = 0; i < MAX_PLAYERS; i++)
1945 struct PlayerInfo *player = &stored_player[i];
1947 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1952 if (local_player == player)
1953 printf("Player %d is local player.\n", i+1);
1957 if (BorderElement == EL_EMPTY)
1960 SBX_Right = lev_fieldx - SCR_FIELDX;
1962 SBY_Lower = lev_fieldy - SCR_FIELDY;
1967 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1969 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1972 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1973 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1975 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1976 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1978 /* if local player not found, look for custom element that might create
1979 the player (make some assumptions about the right custom element) */
1980 if (!local_player->present)
1982 int start_x = 0, start_y = 0;
1983 int found_rating = 0;
1984 int found_element = EL_UNDEFINED;
1986 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1988 int element = Feld[x][y];
1993 if (!IS_CUSTOM_ELEMENT(element))
1996 if (CAN_CHANGE(element))
1998 for (i = 0; i < element_info[element].num_change_pages; i++)
2000 content = element_info[element].change_page[i].target_element;
2001 is_player = ELEM_IS_PLAYER(content);
2003 if (is_player && (found_rating < 3 || element < found_element))
2009 found_element = element;
2014 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2016 content = element_info[element].content[xx][yy];
2017 is_player = ELEM_IS_PLAYER(content);
2019 if (is_player && (found_rating < 2 || element < found_element))
2021 start_x = x + xx - 1;
2022 start_y = y + yy - 1;
2025 found_element = element;
2028 if (!CAN_CHANGE(element))
2031 for (i = 0; i < element_info[element].num_change_pages; i++)
2033 content= element_info[element].change_page[i].target_content[xx][yy];
2034 is_player = ELEM_IS_PLAYER(content);
2036 if (is_player && (found_rating < 1 || element < found_element))
2038 start_x = x + xx - 1;
2039 start_y = y + yy - 1;
2042 found_element = element;
2048 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2049 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2052 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2053 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2059 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2060 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2061 local_player->jx - MIDPOSX);
2063 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2064 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2065 local_player->jy - MIDPOSY);
2067 scroll_x = SBX_Left;
2068 scroll_y = SBY_Upper;
2069 if (local_player->jx >= SBX_Left + MIDPOSX)
2070 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2071 local_player->jx - MIDPOSX :
2073 if (local_player->jy >= SBY_Upper + MIDPOSY)
2074 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2075 local_player->jy - MIDPOSY :
2080 CloseDoor(DOOR_CLOSE_1);
2082 /* !!! FIX THIS (START) !!! */
2083 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2085 InitGameEngine_EM();
2092 /* after drawing the level, correct some elements */
2093 if (game.timegate_time_left == 0)
2094 CloseAllOpenTimegates();
2096 if (setup.soft_scrolling)
2097 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2099 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2102 /* !!! FIX THIS (END) !!! */
2104 /* copy default game door content to main double buffer */
2105 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2106 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2108 DrawGameDoorValues();
2112 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2113 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2114 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2118 /* copy actual game door content to door double buffer for OpenDoor() */
2119 BlitBitmap(drawto, bitmap_db_door,
2120 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2122 OpenDoor(DOOR_OPEN_ALL);
2124 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2126 if (setup.sound_music)
2129 KeyboardAutoRepeatOffUnlessAutoplay();
2133 for (i = 0; i < MAX_PLAYERS; i++)
2134 printf("Player %d %sactive.\n",
2135 i + 1, (stored_player[i].active ? "" : "not "));
2139 printf("::: starting game [%d]\n", FrameCounter);
2143 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2145 /* this is used for non-R'n'D game engines to update certain engine values */
2147 /* needed to determine if sounds are played within the visible screen area */
2148 scroll_x = actual_scroll_x;
2149 scroll_y = actual_scroll_y;
2152 void InitMovDir(int x, int y)
2154 int i, element = Feld[x][y];
2155 static int xy[4][2] =
2162 static int direction[3][4] =
2164 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2165 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2166 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2175 Feld[x][y] = EL_BUG;
2176 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2179 case EL_SPACESHIP_RIGHT:
2180 case EL_SPACESHIP_UP:
2181 case EL_SPACESHIP_LEFT:
2182 case EL_SPACESHIP_DOWN:
2183 Feld[x][y] = EL_SPACESHIP;
2184 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2187 case EL_BD_BUTTERFLY_RIGHT:
2188 case EL_BD_BUTTERFLY_UP:
2189 case EL_BD_BUTTERFLY_LEFT:
2190 case EL_BD_BUTTERFLY_DOWN:
2191 Feld[x][y] = EL_BD_BUTTERFLY;
2192 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2195 case EL_BD_FIREFLY_RIGHT:
2196 case EL_BD_FIREFLY_UP:
2197 case EL_BD_FIREFLY_LEFT:
2198 case EL_BD_FIREFLY_DOWN:
2199 Feld[x][y] = EL_BD_FIREFLY;
2200 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2203 case EL_PACMAN_RIGHT:
2205 case EL_PACMAN_LEFT:
2206 case EL_PACMAN_DOWN:
2207 Feld[x][y] = EL_PACMAN;
2208 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2211 case EL_SP_SNIKSNAK:
2212 MovDir[x][y] = MV_UP;
2215 case EL_SP_ELECTRON:
2216 MovDir[x][y] = MV_LEFT;
2223 Feld[x][y] = EL_MOLE;
2224 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2228 if (IS_CUSTOM_ELEMENT(element))
2230 struct ElementInfo *ei = &element_info[element];
2231 int move_direction_initial = ei->move_direction_initial;
2232 int move_pattern = ei->move_pattern;
2234 if (move_direction_initial == MV_START_PREVIOUS)
2236 if (MovDir[x][y] != MV_NO_MOVING)
2239 move_direction_initial = MV_START_AUTOMATIC;
2242 if (move_direction_initial == MV_START_RANDOM)
2243 MovDir[x][y] = 1 << RND(4);
2244 else if (move_direction_initial & MV_ANY_DIRECTION)
2245 MovDir[x][y] = move_direction_initial;
2246 else if (move_pattern == MV_ALL_DIRECTIONS ||
2247 move_pattern == MV_TURNING_LEFT ||
2248 move_pattern == MV_TURNING_RIGHT ||
2249 move_pattern == MV_TURNING_LEFT_RIGHT ||
2250 move_pattern == MV_TURNING_RIGHT_LEFT ||
2251 move_pattern == MV_TURNING_RANDOM)
2252 MovDir[x][y] = 1 << RND(4);
2253 else if (move_pattern == MV_HORIZONTAL)
2254 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2255 else if (move_pattern == MV_VERTICAL)
2256 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2257 else if (move_pattern & MV_ANY_DIRECTION)
2258 MovDir[x][y] = element_info[element].move_pattern;
2259 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2260 move_pattern == MV_ALONG_RIGHT_SIDE)
2263 /* use random direction as default start direction */
2264 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2265 MovDir[x][y] = 1 << RND(4);
2268 for (i = 0; i < NUM_DIRECTIONS; i++)
2270 int x1 = x + xy[i][0];
2271 int y1 = y + xy[i][1];
2273 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2275 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2276 MovDir[x][y] = direction[0][i];
2278 MovDir[x][y] = direction[1][i];
2287 MovDir[x][y] = 1 << RND(4);
2289 if (element != EL_BUG &&
2290 element != EL_SPACESHIP &&
2291 element != EL_BD_BUTTERFLY &&
2292 element != EL_BD_FIREFLY)
2295 for (i = 0; i < NUM_DIRECTIONS; i++)
2297 int x1 = x + xy[i][0];
2298 int y1 = y + xy[i][1];
2300 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2302 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2304 MovDir[x][y] = direction[0][i];
2307 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2308 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2310 MovDir[x][y] = direction[1][i];
2319 GfxDir[x][y] = MovDir[x][y];
2322 void InitAmoebaNr(int x, int y)
2325 int group_nr = AmoebeNachbarNr(x, y);
2329 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2331 if (AmoebaCnt[i] == 0)
2339 AmoebaNr[x][y] = group_nr;
2340 AmoebaCnt[group_nr]++;
2341 AmoebaCnt2[group_nr]++;
2347 boolean raise_level = FALSE;
2349 if (local_player->MovPos)
2353 if (tape.auto_play) /* tape might already be stopped here */
2354 tape.auto_play_level_solved = TRUE;
2356 if (tape.playing && tape.auto_play)
2357 tape.auto_play_level_solved = TRUE;
2360 local_player->LevelSolved = FALSE;
2362 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2366 if (!tape.playing && setup.sound_loops)
2367 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2368 SND_CTRL_PLAY_LOOP);
2370 while (TimeLeft > 0)
2372 if (!tape.playing && !setup.sound_loops)
2373 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2374 if (TimeLeft > 0 && !(TimeLeft % 10))
2375 RaiseScore(level.score[SC_TIME_BONUS]);
2376 if (TimeLeft > 100 && !(TimeLeft % 10))
2381 DrawGameValue_Time(TimeLeft);
2389 if (!tape.playing && setup.sound_loops)
2390 StopSound(SND_GAME_LEVELTIME_BONUS);
2392 else if (level.time == 0) /* level without time limit */
2394 if (!tape.playing && setup.sound_loops)
2395 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2396 SND_CTRL_PLAY_LOOP);
2398 while (TimePlayed < 999)
2400 if (!tape.playing && !setup.sound_loops)
2401 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2402 if (TimePlayed < 999 && !(TimePlayed % 10))
2403 RaiseScore(level.score[SC_TIME_BONUS]);
2404 if (TimePlayed < 900 && !(TimePlayed % 10))
2409 DrawGameValue_Time(TimePlayed);
2417 if (!tape.playing && setup.sound_loops)
2418 StopSound(SND_GAME_LEVELTIME_BONUS);
2421 /* close exit door after last player */
2422 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2423 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2424 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2426 int element = Feld[ExitX][ExitY];
2428 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2429 EL_SP_EXIT_CLOSING);
2431 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2434 /* Hero disappears */
2435 if (ExitX >= 0 && ExitY >= 0)
2436 DrawLevelField(ExitX, ExitY);
2443 CloseDoor(DOOR_CLOSE_1);
2448 SaveTape(tape.level_nr); /* Ask to save tape */
2451 if (level_nr == leveldir_current->handicap_level)
2453 leveldir_current->handicap_level++;
2454 SaveLevelSetup_SeriesInfo();
2457 if (level_editor_test_game)
2458 local_player->score = -1; /* no highscore when playing from editor */
2459 else if (level_nr < leveldir_current->last_level)
2460 raise_level = TRUE; /* advance to next level */
2462 if ((hi_pos = NewHiScore()) >= 0)
2464 game_status = GAME_MODE_SCORES;
2465 DrawHallOfFame(hi_pos);
2474 game_status = GAME_MODE_MAIN;
2491 LoadScore(level_nr);
2493 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2494 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2497 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2499 if (local_player->score > highscore[k].Score)
2501 /* player has made it to the hall of fame */
2503 if (k < MAX_SCORE_ENTRIES - 1)
2505 int m = MAX_SCORE_ENTRIES - 1;
2508 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2509 if (!strcmp(setup.player_name, highscore[l].Name))
2511 if (m == k) /* player's new highscore overwrites his old one */
2515 for (l = m; l > k; l--)
2517 strcpy(highscore[l].Name, highscore[l - 1].Name);
2518 highscore[l].Score = highscore[l - 1].Score;
2525 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2526 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2527 highscore[k].Score = local_player->score;
2533 else if (!strncmp(setup.player_name, highscore[k].Name,
2534 MAX_PLAYER_NAME_LEN))
2535 break; /* player already there with a higher score */
2541 SaveScore(level_nr);
2546 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2548 if (player->GfxAction != action || player->GfxDir != dir)
2551 printf("Player frame reset! (%d => %d, %d => %d)\n",
2552 player->GfxAction, action, player->GfxDir, dir);
2555 player->GfxAction = action;
2556 player->GfxDir = dir;
2558 player->StepFrame = 0;
2562 static void ResetRandomAnimationValue(int x, int y)
2564 GfxRandom[x][y] = INIT_GFX_RANDOM();
2567 static void ResetGfxAnimation(int x, int y)
2570 GfxAction[x][y] = ACTION_DEFAULT;
2571 GfxDir[x][y] = MovDir[x][y];
2574 void InitMovingField(int x, int y, int direction)
2576 int element = Feld[x][y];
2577 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2578 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2582 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2583 ResetGfxAnimation(x, y);
2585 MovDir[newx][newy] = MovDir[x][y] = direction;
2586 GfxDir[x][y] = direction;
2588 if (Feld[newx][newy] == EL_EMPTY)
2589 Feld[newx][newy] = EL_BLOCKED;
2591 if (direction == MV_DOWN && CAN_FALL(element))
2592 GfxAction[x][y] = ACTION_FALLING;
2594 GfxAction[x][y] = ACTION_MOVING;
2596 GfxFrame[newx][newy] = GfxFrame[x][y];
2597 GfxRandom[newx][newy] = GfxRandom[x][y];
2598 GfxAction[newx][newy] = GfxAction[x][y];
2599 GfxDir[newx][newy] = GfxDir[x][y];
2602 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2604 int direction = MovDir[x][y];
2605 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2606 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2612 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2614 int oldx = x, oldy = y;
2615 int direction = MovDir[x][y];
2617 if (direction == MV_LEFT)
2619 else if (direction == MV_RIGHT)
2621 else if (direction == MV_UP)
2623 else if (direction == MV_DOWN)
2626 *comes_from_x = oldx;
2627 *comes_from_y = oldy;
2630 int MovingOrBlocked2Element(int x, int y)
2632 int element = Feld[x][y];
2634 if (element == EL_BLOCKED)
2638 Blocked2Moving(x, y, &oldx, &oldy);
2639 return Feld[oldx][oldy];
2645 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2647 /* like MovingOrBlocked2Element(), but if element is moving
2648 and (x,y) is the field the moving element is just leaving,
2649 return EL_BLOCKED instead of the element value */
2650 int element = Feld[x][y];
2652 if (IS_MOVING(x, y))
2654 if (element == EL_BLOCKED)
2658 Blocked2Moving(x, y, &oldx, &oldy);
2659 return Feld[oldx][oldy];
2668 static void RemoveField(int x, int y)
2670 Feld[x][y] = EL_EMPTY;
2677 ChangeDelay[x][y] = 0;
2678 ChangePage[x][y] = -1;
2679 Pushed[x][y] = FALSE;
2682 ExplodeField[x][y] = EX_TYPE_NONE;
2685 GfxElement[x][y] = EL_UNDEFINED;
2686 GfxAction[x][y] = ACTION_DEFAULT;
2687 GfxDir[x][y] = MV_NO_MOVING;
2690 void RemoveMovingField(int x, int y)
2692 int oldx = x, oldy = y, newx = x, newy = y;
2693 int element = Feld[x][y];
2694 int next_element = EL_UNDEFINED;
2696 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2699 if (IS_MOVING(x, y))
2701 Moving2Blocked(x, y, &newx, &newy);
2703 if (Feld[newx][newy] != EL_BLOCKED)
2706 if (Feld[newx][newy] != EL_BLOCKED)
2708 /* element is moving, but target field is not free (blocked), but
2709 already occupied by something different (example: acid pool);
2710 in this case, only remove the moving field, but not the target */
2712 RemoveField(oldx, oldy);
2714 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2716 DrawLevelField(oldx, oldy);
2722 else if (element == EL_BLOCKED)
2724 Blocked2Moving(x, y, &oldx, &oldy);
2725 if (!IS_MOVING(oldx, oldy))
2729 if (element == EL_BLOCKED &&
2730 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2731 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2732 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2733 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2734 next_element = get_next_element(Feld[oldx][oldy]);
2736 RemoveField(oldx, oldy);
2737 RemoveField(newx, newy);
2739 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2741 if (next_element != EL_UNDEFINED)
2742 Feld[oldx][oldy] = next_element;
2744 DrawLevelField(oldx, oldy);
2745 DrawLevelField(newx, newy);
2748 void DrawDynamite(int x, int y)
2750 int sx = SCREENX(x), sy = SCREENY(y);
2751 int graphic = el2img(Feld[x][y]);
2754 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2757 if (IS_WALKABLE_INSIDE(Back[x][y]))
2761 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2762 else if (Store[x][y])
2763 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2765 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2768 if (Back[x][y] || Store[x][y])
2769 DrawGraphicThruMask(sx, sy, graphic, frame);
2771 DrawGraphic(sx, sy, graphic, frame);
2773 if (game.emulation == EMU_SUPAPLEX)
2774 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2775 else if (Store[x][y])
2776 DrawGraphicThruMask(sx, sy, graphic, frame);
2778 DrawGraphic(sx, sy, graphic, frame);
2782 void CheckDynamite(int x, int y)
2784 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2788 if (MovDelay[x][y] != 0)
2791 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2798 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2800 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2801 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2802 StopSound(SND_DYNAMITE_ACTIVE);
2804 StopSound(SND_DYNABOMB_ACTIVE);
2810 void DrawRelocatePlayer(struct PlayerInfo *player)
2812 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2813 boolean no_delay = (tape.warp_forward);
2814 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2815 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2816 int jx = player->jx;
2817 int jy = player->jy;
2819 if (level.instant_relocation)
2822 int offset = (setup.scroll_delay ? 3 : 0);
2824 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2826 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2827 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2828 local_player->jx - MIDPOSX);
2830 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2831 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2832 local_player->jy - MIDPOSY);
2836 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2837 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2838 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2840 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2841 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2842 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2844 /* don't scroll over playfield boundaries */
2845 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2846 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2848 /* don't scroll over playfield boundaries */
2849 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2850 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2853 scroll_x += (local_player->jx - old_jx);
2854 scroll_y += (local_player->jy - old_jy);
2856 /* don't scroll over playfield boundaries */
2857 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2858 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2860 /* don't scroll over playfield boundaries */
2861 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2862 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2865 RedrawPlayfield(TRUE, 0,0,0,0);
2871 int offset = (setup.scroll_delay ? 3 : 0);
2873 int scroll_xx = -999, scroll_yy = -999;
2875 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2877 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2880 int fx = FX, fy = FY;
2882 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2883 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2884 local_player->jx - MIDPOSX);
2886 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2887 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2888 local_player->jy - MIDPOSY);
2890 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2891 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2894 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2897 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2904 fx += dx * TILEX / 2;
2905 fy += dy * TILEY / 2;
2907 ScrollLevel(dx, dy);
2910 /* scroll in two steps of half tile size to make things smoother */
2911 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2913 Delay(wait_delay_value);
2915 /* scroll second step to align at full tile size */
2917 Delay(wait_delay_value);
2920 int scroll_xx = -999, scroll_yy = -999;
2922 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2924 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2927 int fx = FX, fy = FY;
2929 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2930 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2931 local_player->jx - MIDPOSX);
2933 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2934 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2935 local_player->jy - MIDPOSY);
2937 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2938 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2941 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2944 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2951 fx += dx * TILEX / 2;
2952 fy += dy * TILEY / 2;
2954 ScrollLevel(dx, dy);
2957 /* scroll in two steps of half tile size to make things smoother */
2958 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2960 Delay(wait_delay_value);
2962 /* scroll second step to align at full tile size */
2964 Delay(wait_delay_value);
2970 Delay(wait_delay_value);
2974 void RelocatePlayer(int jx, int jy, int el_player_raw)
2977 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2979 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2981 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2982 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2983 boolean no_delay = (tape.warp_forward);
2984 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2985 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2986 int old_jx = player->jx;
2987 int old_jy = player->jy;
2988 int old_element = Feld[old_jx][old_jy];
2989 int element = Feld[jx][jy];
2990 boolean player_relocated = (old_jx != jx || old_jy != jy);
2992 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2993 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2995 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2996 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2997 int leave_side_horiz = move_dir_horiz;
2998 int leave_side_vert = move_dir_vert;
3000 static int trigger_sides[4][2] =
3002 /* enter side leave side */
3003 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3004 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3005 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3006 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3008 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3009 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3010 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3011 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3013 int enter_side = enter_side_horiz | enter_side_vert;
3014 int leave_side = leave_side_horiz | leave_side_vert;
3016 if (player->GameOver) /* do not reanimate dead player */
3019 if (!player_relocated) /* no need to relocate the player */
3022 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3024 RemoveField(jx, jy); /* temporarily remove newly placed player */
3025 DrawLevelField(jx, jy);
3028 if (player->present)
3030 while (player->MovPos)
3032 ScrollPlayer(player, SCROLL_GO_ON);
3033 ScrollScreen(NULL, SCROLL_GO_ON);
3035 #if USE_NEW_MOVE_DELAY
3036 AdvanceFrameAndPlayerCounters(player->index_nr);
3044 Delay(wait_delay_value);
3047 DrawPlayer(player); /* needed here only to cleanup last field */
3048 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3050 player->is_moving = FALSE;
3054 if (IS_CUSTOM_ELEMENT(old_element))
3055 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3057 player->index_bit, leave_side);
3059 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3061 player->index_bit, leave_side);
3064 Feld[jx][jy] = el_player;
3065 InitPlayerField(jx, jy, el_player, TRUE);
3067 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3069 Feld[jx][jy] = element;
3070 InitField(jx, jy, FALSE);
3074 if (player == local_player) /* only visually relocate local player */
3075 DrawRelocatePlayer(player);
3079 TestIfHeroTouchesBadThing(jx, jy);
3080 TestIfPlayerTouchesCustomElement(jx, jy);
3084 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3089 /* needed to allow change of walkable custom element by entering player */
3090 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3091 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3093 /* needed to allow change of walkable custom element by entering player */
3094 Changed[jx][jy] = 0; /* allow another change */
3099 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3100 enter_side == MV_LEFT ? "left" :
3101 enter_side == MV_RIGHT ? "right" :
3102 enter_side == MV_UP ? "top" :
3103 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3107 if (IS_CUSTOM_ELEMENT(element))
3108 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3109 player->index_bit, enter_side);
3111 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3112 CE_OTHER_GETS_ENTERED,
3113 player->index_bit, enter_side);
3117 void Explode(int ex, int ey, int phase, int mode)
3124 /* !!! eliminate this variable !!! */
3125 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3130 int last_phase = num_phase * delay;
3131 int half_phase = (num_phase / 2) * delay;
3132 int first_phase_after_start = EX_PHASE_START + 1;
3136 if (game.explosions_delayed)
3138 ExplodeField[ex][ey] = mode;
3142 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3144 int center_element = Feld[ex][ey];
3147 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3151 /* --- This is only really needed (and now handled) in "Impact()". --- */
3152 /* do not explode moving elements that left the explode field in time */
3153 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3154 center_element == EL_EMPTY &&
3155 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3160 if (mode == EX_TYPE_NORMAL ||
3161 mode == EX_TYPE_CENTER ||
3162 mode == EX_TYPE_CROSS)
3163 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3165 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3166 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3169 /* remove things displayed in background while burning dynamite */
3170 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3173 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3175 /* put moving element to center field (and let it explode there) */
3176 center_element = MovingOrBlocked2Element(ex, ey);
3177 RemoveMovingField(ex, ey);
3178 Feld[ex][ey] = center_element;
3184 last_phase = element_info[center_element].explosion_delay + 1;
3186 last_phase = element_info[center_element].explosion_delay;
3190 printf("::: %d -> %d\n", center_element, last_phase);
3194 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3196 int xx = x - ex + 1;
3197 int yy = y - ey + 1;
3202 if (!IN_LEV_FIELD(x, y) ||
3203 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3204 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3207 if (!IN_LEV_FIELD(x, y) ||
3208 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3212 if (!IN_LEV_FIELD(x, y) ||
3213 ((mode != EX_TYPE_NORMAL ||
3214 center_element == EL_AMOEBA_TO_DIAMOND) &&
3215 (x != ex || y != ey)))
3219 element = Feld[x][y];
3221 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3223 element = MovingOrBlocked2Element(x, y);
3225 if (!IS_EXPLOSION_PROOF(element))
3226 RemoveMovingField(x, y);
3232 if (IS_EXPLOSION_PROOF(element))
3235 /* indestructible elements can only explode in center (but not flames) */
3237 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3238 mode == EX_TYPE_BORDER)) ||
3239 element == EL_FLAMES)
3242 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3243 element == EL_FLAMES)
3249 if ((IS_INDESTRUCTIBLE(element) &&
3250 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3251 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3252 element == EL_FLAMES)
3257 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3258 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3259 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3261 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3264 if (IS_ACTIVE_BOMB(element))
3266 /* re-activate things under the bomb like gate or penguin */
3268 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3271 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3276 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3277 element_info[Feld[x][y]].token_name,
3278 Store[x][y], Store2[x][y]);
3285 /* save walkable background elements while explosion on same tile */
3287 if (IS_INDESTRUCTIBLE(element))
3288 Back[x][y] = element;
3292 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3293 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3294 Back[x][y] = element;
3296 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3297 (x != ex || y != ey))
3298 Back[x][y] = element;
3301 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3302 Back[x][y] = element;
3306 /* ignite explodable elements reached by other explosion */
3307 if (element == EL_EXPLOSION)
3308 element = Store2[x][y];
3311 if (AmoebaNr[x][y] &&
3312 (element == EL_AMOEBA_FULL ||
3313 element == EL_BD_AMOEBA ||
3314 element == EL_AMOEBA_GROWING))
3316 AmoebaCnt[AmoebaNr[x][y]]--;
3317 AmoebaCnt2[AmoebaNr[x][y]]--;
3323 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3325 switch(StorePlayer[ex][ey])
3328 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3331 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3334 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3338 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3343 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3344 Store[x][y] = EL_EMPTY;
3346 if (game.emulation == EMU_SUPAPLEX)
3347 Store[x][y] = EL_EMPTY;
3350 else if (center_element == EL_MOLE)
3351 Store[x][y] = EL_EMERALD_RED;
3352 else if (center_element == EL_PENGUIN)
3353 Store[x][y] = EL_EMERALD_PURPLE;
3354 else if (center_element == EL_BUG)
3355 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3356 else if (center_element == EL_BD_BUTTERFLY)
3357 Store[x][y] = EL_BD_DIAMOND;
3358 else if (center_element == EL_SP_ELECTRON)
3359 Store[x][y] = EL_SP_INFOTRON;
3360 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3361 Store[x][y] = level.amoeba_content;
3362 else if (center_element == EL_YAMYAM)
3363 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3364 else if (IS_CUSTOM_ELEMENT(center_element) &&
3365 element_info[center_element].content[xx][yy] != EL_EMPTY)
3366 Store[x][y] = element_info[center_element].content[xx][yy];
3367 else if (element == EL_WALL_EMERALD)
3368 Store[x][y] = EL_EMERALD;
3369 else if (element == EL_WALL_DIAMOND)
3370 Store[x][y] = EL_DIAMOND;
3371 else if (element == EL_WALL_BD_DIAMOND)
3372 Store[x][y] = EL_BD_DIAMOND;
3373 else if (element == EL_WALL_EMERALD_YELLOW)
3374 Store[x][y] = EL_EMERALD_YELLOW;
3375 else if (element == EL_WALL_EMERALD_RED)
3376 Store[x][y] = EL_EMERALD_RED;
3377 else if (element == EL_WALL_EMERALD_PURPLE)
3378 Store[x][y] = EL_EMERALD_PURPLE;
3379 else if (element == EL_WALL_PEARL)
3380 Store[x][y] = EL_PEARL;
3381 else if (element == EL_WALL_CRYSTAL)
3382 Store[x][y] = EL_CRYSTAL;
3383 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3384 Store[x][y] = element_info[element].content[1][1];
3386 Store[x][y] = EL_EMPTY;
3388 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3389 center_element == EL_AMOEBA_TO_DIAMOND)
3390 Store2[x][y] = element;
3393 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3394 element_info[Store2[x][y]].token_name);
3398 if (AmoebaNr[x][y] &&
3399 (element == EL_AMOEBA_FULL ||
3400 element == EL_BD_AMOEBA ||
3401 element == EL_AMOEBA_GROWING))
3403 AmoebaCnt[AmoebaNr[x][y]]--;
3404 AmoebaCnt2[AmoebaNr[x][y]]--;
3410 MovDir[x][y] = MovPos[x][y] = 0;
3411 GfxDir[x][y] = MovDir[x][y];
3416 Feld[x][y] = EL_EXPLOSION;
3418 GfxElement[x][y] = center_element;
3420 GfxElement[x][y] = EL_UNDEFINED;
3423 ExplodePhase[x][y] = 1;
3425 ExplodeDelay[x][y] = last_phase;
3430 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3432 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3439 if (center_element == EL_YAMYAM)
3440 game.yamyam_content_nr =
3441 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3444 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3445 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3459 GfxFrame[x][y] = 0; /* restart explosion animation */
3463 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3467 last_phase = ExplodeDelay[x][y];
3470 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3474 /* activate this even in non-DEBUG version until cause for crash in
3475 getGraphicAnimationFrame() (see below) is found and eliminated */
3479 if (GfxElement[x][y] == EL_UNDEFINED)
3482 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3483 printf("Explode(): This should never happen!\n");
3486 GfxElement[x][y] = EL_EMPTY;
3492 border_element = Store2[x][y];
3494 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3495 border_element = StorePlayer[x][y];
3497 if (IS_PLAYER(x, y))
3498 border_element = StorePlayer[x][y];
3502 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3503 element_info[border_element].token_name, Store2[x][y]);
3507 printf("::: phase == %d\n", phase);
3510 if (phase == element_info[border_element].ignition_delay ||
3511 phase == last_phase)
3513 boolean border_explosion = FALSE;
3517 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3518 !PLAYER_EXPLOSION_PROTECTED(x, y))
3520 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3523 if (IS_PLAYER(x, y))
3526 KillHeroUnlessExplosionProtected(x, y);
3527 border_explosion = TRUE;
3530 if (phase == last_phase)
3531 printf("::: IS_PLAYER\n");
3534 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3537 printf("::: %d,%d: %d %s\n", x, y, border_element,
3538 element_info[border_element].token_name);
3541 Feld[x][y] = Store2[x][y];
3544 border_explosion = TRUE;
3547 if (phase == last_phase)
3548 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3551 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3553 AmoebeUmwandeln(x, y);
3555 border_explosion = TRUE;
3558 if (phase == last_phase)
3559 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3560 element_info[border_element].explosion_delay,
3561 element_info[border_element].ignition_delay,
3567 /* if an element just explodes due to another explosion (chain-reaction),
3568 do not immediately end the new explosion when it was the last frame of
3569 the explosion (as it would be done in the following "if"-statement!) */
3570 if (border_explosion && phase == last_phase)
3577 if (phase == first_phase_after_start)
3579 int element = Store2[x][y];
3581 if (element == EL_BLACK_ORB)
3583 Feld[x][y] = Store2[x][y];
3588 else if (phase == half_phase)
3590 int element = Store2[x][y];
3592 if (IS_PLAYER(x, y))
3593 KillHeroUnlessExplosionProtected(x, y);
3594 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3596 Feld[x][y] = Store2[x][y];
3600 else if (element == EL_AMOEBA_TO_DIAMOND)
3601 AmoebeUmwandeln(x, y);
3605 if (phase == last_phase)
3610 printf("::: done: phase == %d\n", phase);
3614 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3617 element = Feld[x][y] = Store[x][y];
3618 Store[x][y] = Store2[x][y] = 0;
3619 GfxElement[x][y] = EL_UNDEFINED;
3621 /* player can escape from explosions and might therefore be still alive */
3622 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3623 element <= EL_PLAYER_IS_EXPLODING_4)
3624 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3626 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3627 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3628 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3631 /* restore probably existing indestructible background element */
3632 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3633 element = Feld[x][y] = Back[x][y];
3636 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3637 GfxDir[x][y] = MV_NO_MOVING;
3638 ChangeDelay[x][y] = 0;
3639 ChangePage[x][y] = -1;
3642 InitField_WithBug2(x, y, FALSE);
3644 InitField(x, y, FALSE);
3646 /* !!! not needed !!! */
3648 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3649 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3652 if (CAN_MOVE(element))
3657 DrawLevelField(x, y);
3659 TestIfElementTouchesCustomElement(x, y);
3661 if (GFX_CRUMBLED(element))
3662 DrawLevelFieldCrumbledSandNeighbours(x, y);
3664 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3665 StorePlayer[x][y] = 0;
3667 if (ELEM_IS_PLAYER(element))
3668 RelocatePlayer(x, y, element);
3671 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3673 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3677 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3679 int stored = Store[x][y];
3680 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3681 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3685 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3687 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3691 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3695 printf("::: %d / %d [%d - %d]\n",
3696 GfxFrame[x][y], phase - delay, phase, delay);
3700 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3701 element_info[GfxElement[x][y]].token_name,
3706 DrawLevelFieldCrumbledSand(x, y);
3708 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3710 DrawLevelElement(x, y, Back[x][y]);
3711 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3713 else if (IS_WALKABLE_UNDER(Back[x][y]))
3715 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3716 DrawLevelElementThruMask(x, y, Back[x][y]);
3718 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3719 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3723 void DynaExplode(int ex, int ey)
3726 int dynabomb_element = Feld[ex][ey];
3727 int dynabomb_size = 1;
3728 boolean dynabomb_xl = FALSE;
3729 struct PlayerInfo *player;
3730 static int xy[4][2] =
3738 if (IS_ACTIVE_BOMB(dynabomb_element))
3740 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3741 dynabomb_size = player->dynabomb_size;
3742 dynabomb_xl = player->dynabomb_xl;
3743 player->dynabombs_left++;
3746 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3748 for (i = 0; i < NUM_DIRECTIONS; i++)
3750 for (j = 1; j <= dynabomb_size; j++)
3752 int x = ex + j * xy[i][0];
3753 int y = ey + j * xy[i][1];
3756 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3759 element = Feld[x][y];
3761 /* do not restart explosions of fields with active bombs */
3762 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3765 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3769 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3770 !IS_DIGGABLE(element) && !dynabomb_xl)
3773 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3774 !CAN_GROW_INTO(element) && !dynabomb_xl)
3778 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3779 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3780 element != EL_SAND && !dynabomb_xl)
3787 void Bang(int x, int y)
3790 int element = MovingOrBlocked2Element(x, y);
3792 int element = Feld[x][y];
3796 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3798 if (IS_PLAYER(x, y))
3801 struct PlayerInfo *player = PLAYERINFO(x, y);
3803 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3804 player->element_nr);
3809 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3811 if (game.emulation == EMU_SUPAPLEX)
3812 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3814 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3819 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3827 case EL_BD_BUTTERFLY:
3830 case EL_DARK_YAMYAM:
3834 RaiseScoreElement(element);
3835 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3837 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3838 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3839 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3840 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3841 case EL_DYNABOMB_INCREASE_NUMBER:
3842 case EL_DYNABOMB_INCREASE_SIZE:
3843 case EL_DYNABOMB_INCREASE_POWER:
3848 case EL_LAMP_ACTIVE:
3850 case EL_AMOEBA_TO_DIAMOND:
3852 if (IS_PLAYER(x, y))
3853 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3855 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3859 if (element_info[element].explosion_type == EXPLODES_CROSS)
3861 if (CAN_EXPLODE_CROSS(element))
3864 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3869 else if (element_info[element].explosion_type == EXPLODES_1X1)
3871 else if (CAN_EXPLODE_1X1(element))
3873 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3875 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3879 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3882 void SplashAcid(int x, int y)
3885 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3886 (!IN_LEV_FIELD(x - 1, y - 2) ||
3887 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3888 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3890 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3891 (!IN_LEV_FIELD(x + 1, y - 2) ||
3892 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3893 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3895 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3897 /* input: position of element entering acid (obsolete) */
3899 int element = Feld[x][y];
3901 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3904 if (element != EL_ACID_SPLASH_LEFT &&
3905 element != EL_ACID_SPLASH_RIGHT)
3907 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3909 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3910 (!IN_LEV_FIELD(x - 1, y - 1) ||
3911 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3912 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3914 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3915 (!IN_LEV_FIELD(x + 1, y - 1) ||
3916 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3917 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3922 static void InitBeltMovement()
3924 static int belt_base_element[4] =
3926 EL_CONVEYOR_BELT_1_LEFT,
3927 EL_CONVEYOR_BELT_2_LEFT,
3928 EL_CONVEYOR_BELT_3_LEFT,
3929 EL_CONVEYOR_BELT_4_LEFT
3931 static int belt_base_active_element[4] =
3933 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3934 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3935 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3936 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3941 /* set frame order for belt animation graphic according to belt direction */
3942 for (i = 0; i < NUM_BELTS; i++)
3946 for (j = 0; j < NUM_BELT_PARTS; j++)
3948 int element = belt_base_active_element[belt_nr] + j;
3949 int graphic = el2img(element);
3951 if (game.belt_dir[i] == MV_LEFT)
3952 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3954 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3958 for (y = 0; y < lev_fieldy; y++)
3960 for (x = 0; x < lev_fieldx; x++)
3962 int element = Feld[x][y];
3964 for (i = 0; i < NUM_BELTS; i++)
3966 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3968 int e_belt_nr = getBeltNrFromBeltElement(element);
3971 if (e_belt_nr == belt_nr)
3973 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3975 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3983 static void ToggleBeltSwitch(int x, int y)
3985 static int belt_base_element[4] =
3987 EL_CONVEYOR_BELT_1_LEFT,
3988 EL_CONVEYOR_BELT_2_LEFT,
3989 EL_CONVEYOR_BELT_3_LEFT,
3990 EL_CONVEYOR_BELT_4_LEFT
3992 static int belt_base_active_element[4] =
3994 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3995 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3996 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3997 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3999 static int belt_base_switch_element[4] =
4001 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4002 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4003 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4004 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4006 static int belt_move_dir[4] =
4014 int element = Feld[x][y];
4015 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4016 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4017 int belt_dir = belt_move_dir[belt_dir_nr];
4020 if (!IS_BELT_SWITCH(element))
4023 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4024 game.belt_dir[belt_nr] = belt_dir;
4026 if (belt_dir_nr == 3)
4029 /* set frame order for belt animation graphic according to belt direction */
4030 for (i = 0; i < NUM_BELT_PARTS; i++)
4032 int element = belt_base_active_element[belt_nr] + i;
4033 int graphic = el2img(element);
4035 if (belt_dir == MV_LEFT)
4036 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4038 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4041 for (yy = 0; yy < lev_fieldy; yy++)
4043 for (xx = 0; xx < lev_fieldx; xx++)
4045 int element = Feld[xx][yy];
4047 if (IS_BELT_SWITCH(element))
4049 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4051 if (e_belt_nr == belt_nr)
4053 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4054 DrawLevelField(xx, yy);
4057 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4059 int e_belt_nr = getBeltNrFromBeltElement(element);
4061 if (e_belt_nr == belt_nr)
4063 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4065 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4066 DrawLevelField(xx, yy);
4069 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4071 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4073 if (e_belt_nr == belt_nr)
4075 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4077 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4078 DrawLevelField(xx, yy);
4085 static void ToggleSwitchgateSwitch(int x, int y)
4089 game.switchgate_pos = !game.switchgate_pos;
4091 for (yy = 0; yy < lev_fieldy; yy++)
4093 for (xx = 0; xx < lev_fieldx; xx++)
4095 int element = Feld[xx][yy];
4097 if (element == EL_SWITCHGATE_SWITCH_UP ||
4098 element == EL_SWITCHGATE_SWITCH_DOWN)
4100 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4101 DrawLevelField(xx, yy);
4103 else if (element == EL_SWITCHGATE_OPEN ||
4104 element == EL_SWITCHGATE_OPENING)
4106 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4108 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4110 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4113 else if (element == EL_SWITCHGATE_CLOSED ||
4114 element == EL_SWITCHGATE_CLOSING)
4116 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4118 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4120 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4127 static int getInvisibleActiveFromInvisibleElement(int element)
4129 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4130 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4131 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4135 static int getInvisibleFromInvisibleActiveElement(int element)
4137 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4138 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4139 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4143 static void RedrawAllLightSwitchesAndInvisibleElements()
4147 for (y = 0; y < lev_fieldy; y++)
4149 for (x = 0; x < lev_fieldx; x++)
4151 int element = Feld[x][y];
4153 if (element == EL_LIGHT_SWITCH &&
4154 game.light_time_left > 0)
4156 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4157 DrawLevelField(x, y);
4159 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4160 game.light_time_left == 0)
4162 Feld[x][y] = EL_LIGHT_SWITCH;
4163 DrawLevelField(x, y);
4165 else if (element == EL_INVISIBLE_STEELWALL ||
4166 element == EL_INVISIBLE_WALL ||
4167 element == EL_INVISIBLE_SAND)
4169 if (game.light_time_left > 0)
4170 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4172 DrawLevelField(x, y);
4174 /* uncrumble neighbour fields, if needed */
4175 if (element == EL_INVISIBLE_SAND)
4176 DrawLevelFieldCrumbledSandNeighbours(x, y);
4178 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4179 element == EL_INVISIBLE_WALL_ACTIVE ||
4180 element == EL_INVISIBLE_SAND_ACTIVE)
4182 if (game.light_time_left == 0)
4183 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4185 DrawLevelField(x, y);
4187 /* re-crumble neighbour fields, if needed */
4188 if (element == EL_INVISIBLE_SAND)
4189 DrawLevelFieldCrumbledSandNeighbours(x, y);
4195 static void ToggleLightSwitch(int x, int y)
4197 int element = Feld[x][y];
4199 game.light_time_left =
4200 (element == EL_LIGHT_SWITCH ?
4201 level.time_light * FRAMES_PER_SECOND : 0);
4203 RedrawAllLightSwitchesAndInvisibleElements();
4206 static void ActivateTimegateSwitch(int x, int y)
4210 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4212 for (yy = 0; yy < lev_fieldy; yy++)
4214 for (xx = 0; xx < lev_fieldx; xx++)
4216 int element = Feld[xx][yy];
4218 if (element == EL_TIMEGATE_CLOSED ||
4219 element == EL_TIMEGATE_CLOSING)
4221 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4222 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4226 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4228 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4229 DrawLevelField(xx, yy);
4236 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4239 inline static int getElementMoveStepsize(int x, int y)
4241 int element = Feld[x][y];
4242 int direction = MovDir[x][y];
4243 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4244 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4245 int horiz_move = (dx != 0);
4246 int sign = (horiz_move ? dx : dy);
4247 int step = sign * element_info[element].move_stepsize;
4249 /* special values for move stepsize for spring and things on conveyor belt */
4253 if (element == EL_SPRING)
4254 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4255 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4256 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4257 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4259 if (CAN_FALL(element) &&
4260 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4261 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4262 else if (element == EL_SPRING)
4263 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4270 void Impact(int x, int y)
4272 boolean lastline = (y == lev_fieldy-1);
4273 boolean object_hit = FALSE;
4274 boolean impact = (lastline || object_hit);
4275 int element = Feld[x][y];
4276 int smashed = EL_STEELWALL;
4278 if (!lastline) /* check if element below was hit */
4280 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4283 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4284 MovDir[x][y + 1] != MV_DOWN ||
4285 MovPos[x][y + 1] <= TILEY / 2));
4288 object_hit = !IS_FREE(x, y + 1);
4291 /* do not smash moving elements that left the smashed field in time */
4292 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4293 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4297 smashed = MovingOrBlocked2Element(x, y + 1);
4299 impact = (lastline || object_hit);
4302 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4304 SplashAcid(x, y + 1);
4308 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4309 /* only reset graphic animation if graphic really changes after impact */
4311 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4313 ResetGfxAnimation(x, y);
4314 DrawLevelField(x, y);
4317 if (impact && CAN_EXPLODE_IMPACT(element))
4322 else if (impact && element == EL_PEARL)
4324 ResetGfxAnimation(x, y);
4326 Feld[x][y] = EL_PEARL_BREAKING;
4327 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4330 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4332 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4337 if (impact && element == EL_AMOEBA_DROP)
4339 if (object_hit && IS_PLAYER(x, y + 1))
4340 KillHeroUnlessEnemyProtected(x, y + 1);
4341 else if (object_hit && smashed == EL_PENGUIN)
4345 Feld[x][y] = EL_AMOEBA_GROWING;
4346 Store[x][y] = EL_AMOEBA_WET;
4348 ResetRandomAnimationValue(x, y);
4353 if (object_hit) /* check which object was hit */
4355 if (CAN_PASS_MAGIC_WALL(element) &&
4356 (smashed == EL_MAGIC_WALL ||
4357 smashed == EL_BD_MAGIC_WALL))
4360 int activated_magic_wall =
4361 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4362 EL_BD_MAGIC_WALL_ACTIVE);
4364 /* activate magic wall / mill */
4365 for (yy = 0; yy < lev_fieldy; yy++)
4366 for (xx = 0; xx < lev_fieldx; xx++)
4367 if (Feld[xx][yy] == smashed)
4368 Feld[xx][yy] = activated_magic_wall;
4370 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4371 game.magic_wall_active = TRUE;
4373 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4374 SND_MAGIC_WALL_ACTIVATING :
4375 SND_BD_MAGIC_WALL_ACTIVATING));
4378 if (IS_PLAYER(x, y + 1))
4380 if (CAN_SMASH_PLAYER(element))
4382 KillHeroUnlessEnemyProtected(x, y + 1);
4386 else if (smashed == EL_PENGUIN)
4388 if (CAN_SMASH_PLAYER(element))
4394 else if (element == EL_BD_DIAMOND)
4396 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4402 else if (((element == EL_SP_INFOTRON ||
4403 element == EL_SP_ZONK) &&
4404 (smashed == EL_SP_SNIKSNAK ||
4405 smashed == EL_SP_ELECTRON ||
4406 smashed == EL_SP_DISK_ORANGE)) ||
4407 (element == EL_SP_INFOTRON &&
4408 smashed == EL_SP_DISK_YELLOW))
4414 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4420 else if (CAN_SMASH_EVERYTHING(element))
4422 if (IS_CLASSIC_ENEMY(smashed) ||
4423 CAN_EXPLODE_SMASHED(smashed))
4428 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4430 if (smashed == EL_LAMP ||
4431 smashed == EL_LAMP_ACTIVE)
4436 else if (smashed == EL_NUT)
4438 Feld[x][y + 1] = EL_NUT_BREAKING;
4439 PlayLevelSound(x, y, SND_NUT_BREAKING);
4440 RaiseScoreElement(EL_NUT);
4443 else if (smashed == EL_PEARL)
4445 ResetGfxAnimation(x, y);
4447 Feld[x][y + 1] = EL_PEARL_BREAKING;
4448 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4451 else if (smashed == EL_DIAMOND)
4453 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4454 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4457 else if (IS_BELT_SWITCH(smashed))
4459 ToggleBeltSwitch(x, y + 1);
4461 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4462 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4464 ToggleSwitchgateSwitch(x, y + 1);
4466 else if (smashed == EL_LIGHT_SWITCH ||
4467 smashed == EL_LIGHT_SWITCH_ACTIVE)
4469 ToggleLightSwitch(x, y + 1);
4474 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4477 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4480 /* !!! TEST ONLY !!! */
4481 CheckElementChangeBySide(x, y + 1, smashed, element,
4482 CE_SWITCHED, CH_SIDE_TOP);
4483 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4484 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4486 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4487 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4488 CheckElementChangeBySide(x, y + 1, smashed, element,
4489 CE_SWITCHED, CH_SIDE_TOP);
4495 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4500 /* play sound of magic wall / mill */
4502 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4503 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4505 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4506 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4507 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4508 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4513 /* play sound of object that hits the ground */
4514 if (lastline || object_hit)
4515 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4518 inline static void TurnRoundExt(int x, int y)
4530 { 0, 0 }, { 0, 0 }, { 0, 0 },
4535 int left, right, back;
4539 { MV_DOWN, MV_UP, MV_RIGHT },
4540 { MV_UP, MV_DOWN, MV_LEFT },
4542 { MV_LEFT, MV_RIGHT, MV_DOWN },
4546 { MV_RIGHT, MV_LEFT, MV_UP }
4549 int element = Feld[x][y];
4550 int move_pattern = element_info[element].move_pattern;
4552 int old_move_dir = MovDir[x][y];
4553 int left_dir = turn[old_move_dir].left;
4554 int right_dir = turn[old_move_dir].right;
4555 int back_dir = turn[old_move_dir].back;
4557 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4558 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4559 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4560 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4562 int left_x = x + left_dx, left_y = y + left_dy;
4563 int right_x = x + right_dx, right_y = y + right_dy;
4564 int move_x = x + move_dx, move_y = y + move_dy;
4568 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4570 TestIfBadThingTouchesOtherBadThing(x, y);
4572 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4573 MovDir[x][y] = right_dir;
4574 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4575 MovDir[x][y] = left_dir;
4577 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4579 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4583 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4584 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4586 TestIfBadThingTouchesOtherBadThing(x, y);
4588 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4589 MovDir[x][y] = left_dir;
4590 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4591 MovDir[x][y] = right_dir;
4593 if ((element == EL_SPACESHIP ||
4594 element == EL_SP_SNIKSNAK ||
4595 element == EL_SP_ELECTRON)
4596 && MovDir[x][y] != old_move_dir)
4598 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4602 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4604 TestIfBadThingTouchesOtherBadThing(x, y);
4606 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4607 MovDir[x][y] = left_dir;
4608 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4609 MovDir[x][y] = right_dir;
4611 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4613 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4616 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4618 TestIfBadThingTouchesOtherBadThing(x, y);
4620 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4621 MovDir[x][y] = left_dir;
4622 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4623 MovDir[x][y] = right_dir;
4625 if (MovDir[x][y] != old_move_dir)
4629 else if (element == EL_YAMYAM)
4631 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4632 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4634 if (can_turn_left && can_turn_right)
4635 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4636 else if (can_turn_left)
4637 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4638 else if (can_turn_right)
4639 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4641 MovDir[x][y] = back_dir;
4643 MovDelay[x][y] = 16 + 16 * RND(3);
4645 else if (element == EL_DARK_YAMYAM)
4647 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4649 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4652 if (can_turn_left && can_turn_right)
4653 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4654 else if (can_turn_left)
4655 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4656 else if (can_turn_right)
4657 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4659 MovDir[x][y] = back_dir;
4661 MovDelay[x][y] = 16 + 16 * RND(3);
4663 else if (element == EL_PACMAN)
4665 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4666 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4668 if (can_turn_left && can_turn_right)
4669 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4670 else if (can_turn_left)
4671 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4672 else if (can_turn_right)
4673 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4675 MovDir[x][y] = back_dir;
4677 MovDelay[x][y] = 6 + RND(40);
4679 else if (element == EL_PIG)
4681 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4682 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4683 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4684 boolean should_turn_left, should_turn_right, should_move_on;
4686 int rnd = RND(rnd_value);
4688 should_turn_left = (can_turn_left &&
4690 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4691 y + back_dy + left_dy)));
4692 should_turn_right = (can_turn_right &&
4694 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4695 y + back_dy + right_dy)));
4696 should_move_on = (can_move_on &&
4699 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4700 y + move_dy + left_dy) ||
4701 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4702 y + move_dy + right_dy)));
4704 if (should_turn_left || should_turn_right || should_move_on)
4706 if (should_turn_left && should_turn_right && should_move_on)
4707 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4708 rnd < 2 * rnd_value / 3 ? right_dir :
4710 else if (should_turn_left && should_turn_right)
4711 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4712 else if (should_turn_left && should_move_on)
4713 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4714 else if (should_turn_right && should_move_on)
4715 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4716 else if (should_turn_left)
4717 MovDir[x][y] = left_dir;
4718 else if (should_turn_right)
4719 MovDir[x][y] = right_dir;
4720 else if (should_move_on)
4721 MovDir[x][y] = old_move_dir;
4723 else if (can_move_on && rnd > rnd_value / 8)
4724 MovDir[x][y] = old_move_dir;
4725 else if (can_turn_left && can_turn_right)
4726 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4727 else if (can_turn_left && rnd > rnd_value / 8)
4728 MovDir[x][y] = left_dir;
4729 else if (can_turn_right && rnd > rnd_value/8)
4730 MovDir[x][y] = right_dir;
4732 MovDir[x][y] = back_dir;
4734 xx = x + move_xy[MovDir[x][y]].x;
4735 yy = y + move_xy[MovDir[x][y]].y;
4738 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4739 if (!IN_LEV_FIELD(xx, yy) ||
4740 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4741 MovDir[x][y] = old_move_dir;
4743 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4744 MovDir[x][y] = old_move_dir;
4749 else if (element == EL_DRAGON)
4751 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4752 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4753 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4755 int rnd = RND(rnd_value);
4758 if (FrameCounter < 1 && x == 0 && y == 29)
4759 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4762 if (can_move_on && rnd > rnd_value / 8)
4763 MovDir[x][y] = old_move_dir;
4764 else if (can_turn_left && can_turn_right)
4765 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4766 else if (can_turn_left && rnd > rnd_value / 8)
4767 MovDir[x][y] = left_dir;
4768 else if (can_turn_right && rnd > rnd_value / 8)
4769 MovDir[x][y] = right_dir;
4771 MovDir[x][y] = back_dir;
4773 xx = x + move_xy[MovDir[x][y]].x;
4774 yy = y + move_xy[MovDir[x][y]].y;
4777 if (FrameCounter < 1 && x == 0 && y == 29)
4778 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4779 xx, yy, Feld[xx][yy],
4784 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4785 MovDir[x][y] = old_move_dir;
4787 if (!IS_FREE(xx, yy))
4788 MovDir[x][y] = old_move_dir;
4792 if (FrameCounter < 1 && x == 0 && y == 29)
4793 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4798 else if (element == EL_MOLE)
4800 boolean can_move_on =
4801 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4802 IS_AMOEBOID(Feld[move_x][move_y]) ||
4803 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4806 boolean can_turn_left =
4807 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4808 IS_AMOEBOID(Feld[left_x][left_y])));
4810 boolean can_turn_right =
4811 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4812 IS_AMOEBOID(Feld[right_x][right_y])));
4814 if (can_turn_left && can_turn_right)
4815 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4816 else if (can_turn_left)
4817 MovDir[x][y] = left_dir;
4819 MovDir[x][y] = right_dir;
4822 if (MovDir[x][y] != old_move_dir)
4825 else if (element == EL_BALLOON)
4827 MovDir[x][y] = game.balloon_dir;
4830 else if (element == EL_SPRING)
4833 if (MovDir[x][y] & MV_HORIZONTAL &&
4834 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4835 MovDir[x][y] = MV_NO_MOVING;
4837 if (MovDir[x][y] & MV_HORIZONTAL &&
4838 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4839 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4840 MovDir[x][y] = MV_NO_MOVING;
4845 else if (element == EL_ROBOT ||
4846 element == EL_SATELLITE ||
4847 element == EL_PENGUIN)
4849 int attr_x = -1, attr_y = -1;
4860 for (i = 0; i < MAX_PLAYERS; i++)
4862 struct PlayerInfo *player = &stored_player[i];
4863 int jx = player->jx, jy = player->jy;
4865 if (!player->active)
4869 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4878 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4879 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4880 game.engine_version < VERSION_IDENT(3,1,0,0)))
4882 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4889 if (element == EL_PENGUIN)
4892 static int xy[4][2] =
4900 for (i = 0; i < NUM_DIRECTIONS; i++)
4902 int ex = x + xy[i][0];
4903 int ey = y + xy[i][1];
4905 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4914 MovDir[x][y] = MV_NO_MOVING;
4916 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4917 else if (attr_x > x)
4918 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4920 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4921 else if (attr_y > y)
4922 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4924 if (element == EL_ROBOT)
4928 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4929 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4930 Moving2Blocked(x, y, &newx, &newy);
4932 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4933 MovDelay[x][y] = 8 + 8 * !RND(3);
4935 MovDelay[x][y] = 16;
4937 else if (element == EL_PENGUIN)
4943 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4945 boolean first_horiz = RND(2);
4946 int new_move_dir = MovDir[x][y];
4949 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4950 Moving2Blocked(x, y, &newx, &newy);
4952 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4956 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4957 Moving2Blocked(x, y, &newx, &newy);
4959 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4962 MovDir[x][y] = old_move_dir;
4966 else /* (element == EL_SATELLITE) */
4972 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4974 boolean first_horiz = RND(2);
4975 int new_move_dir = MovDir[x][y];
4978 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4979 Moving2Blocked(x, y, &newx, &newy);
4981 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4985 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4986 Moving2Blocked(x, y, &newx, &newy);
4988 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4991 MovDir[x][y] = old_move_dir;
4996 else if (move_pattern == MV_TURNING_LEFT ||
4997 move_pattern == MV_TURNING_RIGHT ||
4998 move_pattern == MV_TURNING_LEFT_RIGHT ||
4999 move_pattern == MV_TURNING_RIGHT_LEFT ||
5000 move_pattern == MV_TURNING_RANDOM ||
5001 move_pattern == MV_ALL_DIRECTIONS)
5003 boolean can_turn_left =
5004 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5005 boolean can_turn_right =
5006 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5008 if (move_pattern == MV_TURNING_LEFT)
5009 MovDir[x][y] = left_dir;
5010 else if (move_pattern == MV_TURNING_RIGHT)
5011 MovDir[x][y] = right_dir;
5012 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5013 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5014 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5015 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5016 else if (move_pattern == MV_TURNING_RANDOM)
5017 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5018 can_turn_right && !can_turn_left ? right_dir :
5019 RND(2) ? left_dir : right_dir);
5020 else if (can_turn_left && can_turn_right)
5021 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5022 else if (can_turn_left)
5023 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5024 else if (can_turn_right)
5025 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5027 MovDir[x][y] = back_dir;
5029 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5031 else if (move_pattern == MV_HORIZONTAL ||
5032 move_pattern == MV_VERTICAL)
5034 if (move_pattern & old_move_dir)
5035 MovDir[x][y] = back_dir;
5036 else if (move_pattern == MV_HORIZONTAL)
5037 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5038 else if (move_pattern == MV_VERTICAL)
5039 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5041 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5043 else if (move_pattern & MV_ANY_DIRECTION)
5045 MovDir[x][y] = move_pattern;
5046 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5048 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5050 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5051 MovDir[x][y] = left_dir;
5052 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5053 MovDir[x][y] = right_dir;
5055 if (MovDir[x][y] != old_move_dir)
5056 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5058 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5060 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5061 MovDir[x][y] = right_dir;
5062 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5063 MovDir[x][y] = left_dir;
5065 if (MovDir[x][y] != old_move_dir)
5066 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5068 else if (move_pattern == MV_TOWARDS_PLAYER ||
5069 move_pattern == MV_AWAY_FROM_PLAYER)
5071 int attr_x = -1, attr_y = -1;
5073 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5084 for (i = 0; i < MAX_PLAYERS; i++)
5086 struct PlayerInfo *player = &stored_player[i];
5087 int jx = player->jx, jy = player->jy;
5089 if (!player->active)
5093 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5101 MovDir[x][y] = MV_NO_MOVING;
5103 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5104 else if (attr_x > x)
5105 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5107 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5108 else if (attr_y > y)
5109 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5111 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5113 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5115 boolean first_horiz = RND(2);
5116 int new_move_dir = MovDir[x][y];
5119 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5120 Moving2Blocked(x, y, &newx, &newy);
5122 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5126 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5127 Moving2Blocked(x, y, &newx, &newy);
5129 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5132 MovDir[x][y] = old_move_dir;
5135 else if (move_pattern == MV_WHEN_PUSHED ||
5136 move_pattern == MV_WHEN_DROPPED)
5138 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5139 MovDir[x][y] = MV_NO_MOVING;
5143 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5145 static int test_xy[7][2] =
5155 static int test_dir[7] =
5165 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5166 int move_preference = -1000000; /* start with very low preference */
5167 int new_move_dir = MV_NO_MOVING;
5168 int start_test = RND(4);
5171 for (i = 0; i < NUM_DIRECTIONS; i++)
5173 int move_dir = test_dir[start_test + i];
5174 int move_dir_preference;
5176 xx = x + test_xy[start_test + i][0];
5177 yy = y + test_xy[start_test + i][1];
5179 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5180 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5182 new_move_dir = move_dir;
5187 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5190 move_dir_preference = -1 * RunnerVisit[xx][yy];
5191 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5192 move_dir_preference = PlayerVisit[xx][yy];
5194 if (move_dir_preference > move_preference)
5196 /* prefer field that has not been visited for the longest time */
5197 move_preference = move_dir_preference;
5198 new_move_dir = move_dir;
5200 else if (move_dir_preference == move_preference &&
5201 move_dir == old_move_dir)
5203 /* prefer last direction when all directions are preferred equally */
5204 move_preference = move_dir_preference;
5205 new_move_dir = move_dir;
5209 MovDir[x][y] = new_move_dir;
5210 if (old_move_dir != new_move_dir)
5213 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5221 static void TurnRound(int x, int y)
5223 int direction = MovDir[x][y];
5226 GfxDir[x][y] = MovDir[x][y];
5232 GfxDir[x][y] = MovDir[x][y];
5235 if (direction != MovDir[x][y])
5240 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5243 GfxAction[x][y] = ACTION_WAITING;
5247 static boolean JustBeingPushed(int x, int y)
5251 for (i = 0; i < MAX_PLAYERS; i++)
5253 struct PlayerInfo *player = &stored_player[i];
5255 if (player->active && player->is_pushing && player->MovPos)
5257 int next_jx = player->jx + (player->jx - player->last_jx);
5258 int next_jy = player->jy + (player->jy - player->last_jy);
5260 if (x == next_jx && y == next_jy)
5268 void StartMoving(int x, int y)
5271 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5273 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5274 int element = Feld[x][y];
5280 if (MovDelay[x][y] == 0)
5281 GfxAction[x][y] = ACTION_DEFAULT;
5283 /* !!! this should be handled more generic (not only for mole) !!! */
5284 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5285 GfxAction[x][y] = ACTION_DEFAULT;
5288 if (CAN_FALL(element) && y < lev_fieldy - 1)
5290 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5291 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5292 if (JustBeingPushed(x, y))
5295 if (element == EL_QUICKSAND_FULL)
5297 if (IS_FREE(x, y + 1))
5299 InitMovingField(x, y, MV_DOWN);
5300 started_moving = TRUE;
5302 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5303 Store[x][y] = EL_ROCK;
5305 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5307 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5310 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5312 if (!MovDelay[x][y])
5313 MovDelay[x][y] = TILEY + 1;
5322 Feld[x][y] = EL_QUICKSAND_EMPTY;
5323 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5324 Store[x][y + 1] = Store[x][y];
5327 PlayLevelSoundAction(x, y, ACTION_FILLING);
5329 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5333 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5334 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5336 InitMovingField(x, y, MV_DOWN);
5337 started_moving = TRUE;
5339 Feld[x][y] = EL_QUICKSAND_FILLING;
5340 Store[x][y] = element;
5342 PlayLevelSoundAction(x, y, ACTION_FILLING);
5344 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5347 else if (element == EL_MAGIC_WALL_FULL)
5349 if (IS_FREE(x, y + 1))
5351 InitMovingField(x, y, MV_DOWN);
5352 started_moving = TRUE;
5354 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5355 Store[x][y] = EL_CHANGED(Store[x][y]);
5357 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5359 if (!MovDelay[x][y])
5360 MovDelay[x][y] = TILEY/4 + 1;
5369 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5370 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5371 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5375 else if (element == EL_BD_MAGIC_WALL_FULL)
5377 if (IS_FREE(x, y + 1))
5379 InitMovingField(x, y, MV_DOWN);
5380 started_moving = TRUE;
5382 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5383 Store[x][y] = EL_CHANGED2(Store[x][y]);
5385 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5387 if (!MovDelay[x][y])
5388 MovDelay[x][y] = TILEY/4 + 1;
5397 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5398 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5399 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5403 else if (CAN_PASS_MAGIC_WALL(element) &&
5404 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5405 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5407 InitMovingField(x, y, MV_DOWN);
5408 started_moving = TRUE;
5411 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5412 EL_BD_MAGIC_WALL_FILLING);
5413 Store[x][y] = element;
5416 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5418 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5421 SplashAcid(x, y + 1);
5423 InitMovingField(x, y, MV_DOWN);
5424 started_moving = TRUE;
5426 Store[x][y] = EL_ACID;
5428 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5429 GfxAction[x][y + 1] = ACTION_ACTIVE;
5433 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5434 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5436 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5437 CAN_SMASH(element) && WasJustFalling[x][y] &&
5438 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5440 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5441 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5442 (Feld[x][y + 1] == EL_BLOCKED)))
5446 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5447 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5448 WasJustMoving[x][y] && !Pushed[x][y + 1])
5450 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5451 WasJustMoving[x][y])
5456 /* this is needed for a special case not covered by calling "Impact()"
5457 from "ContinueMoving()": if an element moves to a tile directly below
5458 another element which was just falling on that tile (which was empty
5459 in the previous frame), the falling element above would just stop
5460 instead of smashing the element below (in previous version, the above
5461 element was just checked for "moving" instead of "falling", resulting
5462 in incorrect smashes caused by horizontal movement of the above
5463 element; also, the case of the player being the element to smash was
5464 simply not covered here... :-/ ) */
5467 WasJustMoving[x][y] = 0;
5468 WasJustFalling[x][y] = 0;
5471 CheckCollision[x][y] = 0;
5474 if (IS_PLAYER(x, y + 1))
5475 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5480 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5482 if (MovDir[x][y] == MV_NO_MOVING)
5484 InitMovingField(x, y, MV_DOWN);
5485 started_moving = TRUE;
5488 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5490 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5491 MovDir[x][y] = MV_DOWN;
5493 InitMovingField(x, y, MV_DOWN);
5494 started_moving = TRUE;
5496 else if (element == EL_AMOEBA_DROP)
5498 Feld[x][y] = EL_AMOEBA_GROWING;
5499 Store[x][y] = EL_AMOEBA_WET;
5501 /* Store[x][y + 1] must be zero, because:
5502 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5505 #if OLD_GAME_BEHAVIOUR
5506 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5508 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5509 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5510 element != EL_DX_SUPABOMB)
5513 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5514 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5515 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5516 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5519 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5520 (IS_FREE(x - 1, y + 1) ||
5521 Feld[x - 1][y + 1] == EL_ACID));
5522 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5523 (IS_FREE(x + 1, y + 1) ||
5524 Feld[x + 1][y + 1] == EL_ACID));
5525 boolean can_fall_any = (can_fall_left || can_fall_right);
5526 boolean can_fall_both = (can_fall_left && can_fall_right);
5528 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5530 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5532 if (slippery_type == SLIPPERY_ONLY_LEFT)
5533 can_fall_right = FALSE;
5534 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5535 can_fall_left = FALSE;
5536 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5537 can_fall_right = FALSE;
5538 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5539 can_fall_left = FALSE;
5541 can_fall_any = (can_fall_left || can_fall_right);
5542 can_fall_both = (can_fall_left && can_fall_right);
5545 #if USE_NEW_SP_SLIPPERY
5546 /* !!! better use the same properties as for custom elements here !!! */
5547 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5548 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5550 can_fall_right = FALSE; /* slip down on left side */
5551 can_fall_both = FALSE;
5558 if (game.emulation == EMU_BOULDERDASH ||
5559 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5560 can_fall_right = FALSE; /* slip down on left side */
5562 can_fall_left = !(can_fall_right = RND(2));
5564 can_fall_both = FALSE;
5571 if (can_fall_both &&
5572 (game.emulation != EMU_BOULDERDASH &&
5573 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5574 can_fall_left = !(can_fall_right = RND(2));
5577 /* if not determined otherwise, prefer left side for slipping down */
5578 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5579 started_moving = TRUE;
5583 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5585 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5588 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5589 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5590 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5591 int belt_dir = game.belt_dir[belt_nr];
5593 if ((belt_dir == MV_LEFT && left_is_free) ||
5594 (belt_dir == MV_RIGHT && right_is_free))
5597 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5600 InitMovingField(x, y, belt_dir);
5601 started_moving = TRUE;
5604 Pushed[x][y] = TRUE;
5605 Pushed[nextx][y] = TRUE;
5608 GfxAction[x][y] = ACTION_DEFAULT;
5612 MovDir[x][y] = 0; /* if element was moving, stop it */
5617 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5619 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5621 if (CAN_MOVE(element) && !started_moving)
5624 int move_pattern = element_info[element].move_pattern;
5629 if (MovDir[x][y] == MV_NO_MOVING)
5631 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5632 x, y, element, element_info[element].token_name);
5633 printf("StartMoving(): This should never happen!\n");
5638 Moving2Blocked(x, y, &newx, &newy);
5641 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5644 if ((element == EL_SATELLITE ||
5645 element == EL_BALLOON ||
5646 element == EL_SPRING)
5647 && JustBeingPushed(x, y))
5654 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5655 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5657 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5658 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5659 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5663 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5664 element, element_info[element].token_name,
5665 WasJustMoving[x][y],
5666 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5667 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5668 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5669 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5673 WasJustMoving[x][y] = 0;
5676 CheckCollision[x][y] = 0;
5678 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5681 if (Feld[x][y] != element) /* element has changed */
5683 element = Feld[x][y];
5684 move_pattern = element_info[element].move_pattern;
5686 if (!CAN_MOVE(element))
5690 if (Feld[x][y] != element) /* element has changed */
5698 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5699 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5701 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5703 Moving2Blocked(x, y, &newx, &newy);
5704 if (Feld[newx][newy] == EL_BLOCKED)
5705 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5711 if (FrameCounter < 1 && x == 0 && y == 29)
5712 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5715 if (!MovDelay[x][y]) /* start new movement phase */
5717 /* all objects that can change their move direction after each step
5718 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5720 if (element != EL_YAMYAM &&
5721 element != EL_DARK_YAMYAM &&
5722 element != EL_PACMAN &&
5723 !(move_pattern & MV_ANY_DIRECTION) &&
5724 move_pattern != MV_TURNING_LEFT &&
5725 move_pattern != MV_TURNING_RIGHT &&
5726 move_pattern != MV_TURNING_LEFT_RIGHT &&
5727 move_pattern != MV_TURNING_RIGHT_LEFT &&
5728 move_pattern != MV_TURNING_RANDOM)
5733 if (FrameCounter < 1 && x == 0 && y == 29)
5734 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5737 if (MovDelay[x][y] && (element == EL_BUG ||
5738 element == EL_SPACESHIP ||
5739 element == EL_SP_SNIKSNAK ||
5740 element == EL_SP_ELECTRON ||
5741 element == EL_MOLE))
5742 DrawLevelField(x, y);
5746 if (MovDelay[x][y]) /* wait some time before next movement */
5751 if (element == EL_YAMYAM)
5754 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5755 DrawLevelElementAnimation(x, y, element);
5759 if (MovDelay[x][y]) /* element still has to wait some time */
5762 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5763 ResetGfxAnimation(x, y);
5767 if (GfxAction[x][y] != ACTION_WAITING)
5768 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5770 GfxAction[x][y] = ACTION_WAITING;
5774 if (element == EL_ROBOT ||
5776 element == EL_PACMAN ||
5778 element == EL_YAMYAM ||
5779 element == EL_DARK_YAMYAM)
5782 DrawLevelElementAnimation(x, y, element);
5784 DrawLevelElementAnimationIfNeeded(x, y, element);
5786 PlayLevelSoundAction(x, y, ACTION_WAITING);
5788 else if (element == EL_SP_ELECTRON)
5789 DrawLevelElementAnimationIfNeeded(x, y, element);
5790 else if (element == EL_DRAGON)
5793 int dir = MovDir[x][y];
5794 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5795 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5796 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5797 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5798 dir == MV_UP ? IMG_FLAMES_1_UP :
5799 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5800 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5803 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5806 GfxAction[x][y] = ACTION_ATTACKING;
5808 if (IS_PLAYER(x, y))
5809 DrawPlayerField(x, y);
5811 DrawLevelField(x, y);
5813 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5815 for (i = 1; i <= 3; i++)
5817 int xx = x + i * dx;
5818 int yy = y + i * dy;
5819 int sx = SCREENX(xx);
5820 int sy = SCREENY(yy);
5821 int flame_graphic = graphic + (i - 1);
5823 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5828 int flamed = MovingOrBlocked2Element(xx, yy);
5832 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5834 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5835 RemoveMovingField(xx, yy);
5837 RemoveField(xx, yy);
5839 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5842 RemoveMovingField(xx, yy);
5846 if (ChangeDelay[xx][yy])
5847 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5848 Feld[xx][yy] == EL_BLOCKED));
5852 ChangeDelay[xx][yy] = 0;
5854 Feld[xx][yy] = EL_FLAMES;
5855 if (IN_SCR_FIELD(sx, sy))
5857 DrawLevelFieldCrumbledSand(xx, yy);
5858 DrawGraphic(sx, sy, flame_graphic, frame);
5863 if (Feld[xx][yy] == EL_FLAMES)
5864 Feld[xx][yy] = EL_EMPTY;
5865 DrawLevelField(xx, yy);
5870 if (MovDelay[x][y]) /* element still has to wait some time */
5872 PlayLevelSoundAction(x, y, ACTION_WAITING);
5878 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5879 for all other elements GfxAction will be set by InitMovingField() */
5880 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5881 GfxAction[x][y] = ACTION_MOVING;
5885 /* now make next step */
5887 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5889 if (DONT_COLLIDE_WITH(element) &&
5890 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5891 !PLAYER_ENEMY_PROTECTED(newx, newy))
5894 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5898 /* player killed by element which is deadly when colliding with */
5900 KillHero(PLAYERINFO(newx, newy));
5907 else if (CAN_MOVE_INTO_ACID(element) &&
5908 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5909 (MovDir[x][y] == MV_DOWN ||
5910 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5912 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5913 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5917 else if ((element == EL_PENGUIN ||
5918 element == EL_ROBOT ||
5919 element == EL_SATELLITE ||
5920 element == EL_BALLOON ||
5921 IS_CUSTOM_ELEMENT(element)) &&
5922 IN_LEV_FIELD(newx, newy) &&
5923 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5926 SplashAcid(newx, newy);
5927 Store[x][y] = EL_ACID;
5929 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5931 if (Feld[newx][newy] == EL_EXIT_OPEN)
5935 DrawLevelField(x, y);
5937 Feld[x][y] = EL_EMPTY;
5938 DrawLevelField(x, y);
5941 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5942 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5943 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5945 local_player->friends_still_needed--;
5946 if (!local_player->friends_still_needed &&
5947 !local_player->GameOver && AllPlayersGone)
5948 local_player->LevelSolved = local_player->GameOver = TRUE;
5952 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5954 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5955 DrawLevelField(newx, newy);
5957 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5959 else if (!IS_FREE(newx, newy))
5961 GfxAction[x][y] = ACTION_WAITING;
5963 if (IS_PLAYER(x, y))
5964 DrawPlayerField(x, y);
5966 DrawLevelField(x, y);
5971 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5973 if (IS_FOOD_PIG(Feld[newx][newy]))
5975 if (IS_MOVING(newx, newy))
5976 RemoveMovingField(newx, newy);
5979 Feld[newx][newy] = EL_EMPTY;
5980 DrawLevelField(newx, newy);
5983 PlayLevelSound(x, y, SND_PIG_DIGGING);
5985 else if (!IS_FREE(newx, newy))
5987 if (IS_PLAYER(x, y))
5988 DrawPlayerField(x, y);
5990 DrawLevelField(x, y);
5999 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6002 else if (IS_CUSTOM_ELEMENT(element) &&
6003 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6007 !IS_FREE(newx, newy)
6012 int new_element = Feld[newx][newy];
6015 printf("::: '%s' digs '%s' [%d]\n",
6016 element_info[element].token_name,
6017 element_info[Feld[newx][newy]].token_name,
6018 StorePlayer[newx][newy]);
6021 if (!IS_FREE(newx, newy))
6023 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6024 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6027 /* no element can dig solid indestructible elements */
6028 if (IS_INDESTRUCTIBLE(new_element) &&
6029 !IS_DIGGABLE(new_element) &&
6030 !IS_COLLECTIBLE(new_element))
6033 if (AmoebaNr[newx][newy] &&
6034 (new_element == EL_AMOEBA_FULL ||
6035 new_element == EL_BD_AMOEBA ||
6036 new_element == EL_AMOEBA_GROWING))
6038 AmoebaCnt[AmoebaNr[newx][newy]]--;
6039 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6042 if (IS_MOVING(newx, newy))
6043 RemoveMovingField(newx, newy);
6046 RemoveField(newx, newy);
6047 DrawLevelField(newx, newy);
6050 /* if digged element was about to explode, prevent the explosion */
6051 ExplodeField[newx][newy] = EX_TYPE_NONE;
6053 PlayLevelSoundAction(x, y, action);
6058 Store[newx][newy] = EL_EMPTY;
6059 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6060 Store[newx][newy] = element_info[element].move_leave_element;
6062 Store[newx][newy] = EL_EMPTY;
6063 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6064 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6065 Store[newx][newy] = element_info[element].move_leave_element;
6068 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6069 element_info[element].can_leave_element = TRUE;
6072 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6074 RunnerVisit[x][y] = FrameCounter;
6075 PlayerVisit[x][y] /= 8; /* expire player visit path */
6081 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6083 if (!IS_FREE(newx, newy))
6085 if (IS_PLAYER(x, y))
6086 DrawPlayerField(x, y);
6088 DrawLevelField(x, y);
6094 boolean wanna_flame = !RND(10);
6095 int dx = newx - x, dy = newy - y;
6096 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6097 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6098 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6099 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6100 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6101 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6104 IS_CLASSIC_ENEMY(element1) ||
6105 IS_CLASSIC_ENEMY(element2)) &&
6106 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6107 element1 != EL_FLAMES && element2 != EL_FLAMES)
6110 ResetGfxAnimation(x, y);
6111 GfxAction[x][y] = ACTION_ATTACKING;
6114 if (IS_PLAYER(x, y))
6115 DrawPlayerField(x, y);
6117 DrawLevelField(x, y);
6119 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6121 MovDelay[x][y] = 50;
6125 RemoveField(newx, newy);
6127 Feld[newx][newy] = EL_FLAMES;
6128 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6131 RemoveField(newx1, newy1);
6133 Feld[newx1][newy1] = EL_FLAMES;
6135 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6138 RemoveField(newx2, newy2);
6140 Feld[newx2][newy2] = EL_FLAMES;
6147 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6148 Feld[newx][newy] == EL_DIAMOND)
6150 if (IS_MOVING(newx, newy))
6151 RemoveMovingField(newx, newy);
6154 Feld[newx][newy] = EL_EMPTY;
6155 DrawLevelField(newx, newy);
6158 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6160 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6161 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6163 if (AmoebaNr[newx][newy])
6165 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6166 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6167 Feld[newx][newy] == EL_BD_AMOEBA)
6168 AmoebaCnt[AmoebaNr[newx][newy]]--;
6173 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6175 if (IS_MOVING(newx, newy))
6178 RemoveMovingField(newx, newy);
6182 Feld[newx][newy] = EL_EMPTY;
6183 DrawLevelField(newx, newy);
6186 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6188 else if ((element == EL_PACMAN || element == EL_MOLE)
6189 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6191 if (AmoebaNr[newx][newy])
6193 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6194 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6195 Feld[newx][newy] == EL_BD_AMOEBA)
6196 AmoebaCnt[AmoebaNr[newx][newy]]--;
6199 if (element == EL_MOLE)
6201 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6202 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6204 ResetGfxAnimation(x, y);
6205 GfxAction[x][y] = ACTION_DIGGING;
6206 DrawLevelField(x, y);
6208 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6210 return; /* wait for shrinking amoeba */
6212 else /* element == EL_PACMAN */
6214 Feld[newx][newy] = EL_EMPTY;
6215 DrawLevelField(newx, newy);
6216 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6219 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6220 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6221 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6223 /* wait for shrinking amoeba to completely disappear */
6226 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6228 /* object was running against a wall */
6233 if (move_pattern & MV_ANY_DIRECTION &&
6234 move_pattern == MovDir[x][y])
6236 int blocking_element =
6237 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6240 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6241 element_info[element].token_name,
6242 element_info[blocking_element].token_name,
6246 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6249 element = Feld[x][y]; /* element might have changed */
6254 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6255 DrawLevelElementAnimation(x, y, element);
6257 if (element == EL_BUG ||
6258 element == EL_SPACESHIP ||
6259 element == EL_SP_SNIKSNAK)
6260 DrawLevelField(x, y);
6261 else if (element == EL_MOLE)
6262 DrawLevelField(x, y);
6263 else if (element == EL_BD_BUTTERFLY ||
6264 element == EL_BD_FIREFLY)
6265 DrawLevelElementAnimationIfNeeded(x, y, element);
6266 else if (element == EL_SATELLITE)
6267 DrawLevelElementAnimationIfNeeded(x, y, element);
6268 else if (element == EL_SP_ELECTRON)
6269 DrawLevelElementAnimationIfNeeded(x, y, element);
6272 if (DONT_TOUCH(element))
6273 TestIfBadThingTouchesHero(x, y);
6276 PlayLevelSoundAction(x, y, ACTION_WAITING);
6282 InitMovingField(x, y, MovDir[x][y]);
6284 PlayLevelSoundAction(x, y, ACTION_MOVING);
6288 ContinueMoving(x, y);
6291 void ContinueMoving(int x, int y)
6293 int element = Feld[x][y];
6294 int stored = Store[x][y];
6295 struct ElementInfo *ei = &element_info[element];
6296 int direction = MovDir[x][y];
6297 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6298 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6299 int newx = x + dx, newy = y + dy;
6301 int nextx = newx + dx, nexty = newy + dy;
6304 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6305 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6307 boolean pushed_by_player = Pushed[x][y];
6310 MovPos[x][y] += getElementMoveStepsize(x, y);
6313 if (pushed_by_player && IS_PLAYER(x, y))
6315 /* special case: moving object pushed by player */
6316 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6319 if (pushed_by_player) /* special case: moving object pushed by player */
6320 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6323 if (ABS(MovPos[x][y]) < TILEX)
6325 DrawLevelField(x, y);
6327 return; /* element is still moving */
6330 /* element reached destination field */
6332 Feld[x][y] = EL_EMPTY;
6333 Feld[newx][newy] = element;
6334 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6337 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6339 element = Feld[newx][newy] = EL_ACID;
6342 else if (element == EL_MOLE)
6344 Feld[x][y] = EL_SAND;
6346 DrawLevelFieldCrumbledSandNeighbours(x, y);
6348 else if (element == EL_QUICKSAND_FILLING)
6350 element = Feld[newx][newy] = get_next_element(element);
6351 Store[newx][newy] = Store[x][y];
6353 else if (element == EL_QUICKSAND_EMPTYING)
6355 Feld[x][y] = get_next_element(element);
6356 element = Feld[newx][newy] = Store[x][y];
6358 else if (element == EL_MAGIC_WALL_FILLING)
6360 element = Feld[newx][newy] = get_next_element(element);
6361 if (!game.magic_wall_active)
6362 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6363 Store[newx][newy] = Store[x][y];
6365 else if (element == EL_MAGIC_WALL_EMPTYING)
6367 Feld[x][y] = get_next_element(element);
6368 if (!game.magic_wall_active)
6369 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6370 element = Feld[newx][newy] = Store[x][y];
6372 else if (element == EL_BD_MAGIC_WALL_FILLING)
6374 element = Feld[newx][newy] = get_next_element(element);
6375 if (!game.magic_wall_active)
6376 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6377 Store[newx][newy] = Store[x][y];
6379 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6381 Feld[x][y] = get_next_element(element);
6382 if (!game.magic_wall_active)
6383 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6384 element = Feld[newx][newy] = Store[x][y];
6386 else if (element == EL_AMOEBA_DROPPING)
6388 Feld[x][y] = get_next_element(element);
6389 element = Feld[newx][newy] = Store[x][y];
6391 else if (element == EL_SOKOBAN_OBJECT)
6394 Feld[x][y] = Back[x][y];
6396 if (Back[newx][newy])
6397 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6399 Back[x][y] = Back[newx][newy] = 0;
6402 else if (Store[x][y] == EL_ACID)
6404 element = Feld[newx][newy] = EL_ACID;
6408 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6409 ei->move_leave_element != EL_EMPTY &&
6410 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6411 Store[x][y] != EL_EMPTY))
6413 /* some elements can leave other elements behind after moving */
6415 Feld[x][y] = ei->move_leave_element;
6416 InitField(x, y, FALSE);
6418 if (GFX_CRUMBLED(Feld[x][y]))
6419 DrawLevelFieldCrumbledSandNeighbours(x, y);
6423 Store[x][y] = EL_EMPTY;
6424 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6425 MovDelay[newx][newy] = 0;
6427 if (CAN_CHANGE(element))
6429 /* copy element change control values to new field */
6430 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6431 ChangePage[newx][newy] = ChangePage[x][y];
6432 Changed[newx][newy] = Changed[x][y];
6433 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6436 ChangeDelay[x][y] = 0;
6437 ChangePage[x][y] = -1;
6438 Changed[x][y] = CE_BITMASK_DEFAULT;
6439 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6441 /* copy animation control values to new field */
6442 GfxFrame[newx][newy] = GfxFrame[x][y];
6443 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6444 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6445 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6447 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6449 ResetGfxAnimation(x, y); /* reset animation values for old field */
6452 /* some elements can leave other elements behind after moving */
6454 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6455 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6456 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6458 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6459 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6463 int move_leave_element = ei->move_leave_element;
6465 Feld[x][y] = move_leave_element;
6466 InitField(x, y, FALSE);
6468 if (GFX_CRUMBLED(Feld[x][y]))
6469 DrawLevelFieldCrumbledSandNeighbours(x, y);
6471 if (ELEM_IS_PLAYER(move_leave_element))
6472 RelocatePlayer(x, y, move_leave_element);
6477 /* some elements can leave other elements behind after moving */
6478 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6479 ei->move_leave_element != EL_EMPTY &&
6480 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6481 ei->can_leave_element_last))
6483 Feld[x][y] = ei->move_leave_element;
6484 InitField(x, y, FALSE);
6486 if (GFX_CRUMBLED(Feld[x][y]))
6487 DrawLevelFieldCrumbledSandNeighbours(x, y);
6490 ei->can_leave_element_last = ei->can_leave_element;
6491 ei->can_leave_element = FALSE;
6495 /* 2.1.1 (does not work correctly for spring) */
6496 if (!CAN_MOVE(element))
6497 MovDir[newx][newy] = 0;
6501 /* (does not work for falling objects that slide horizontally) */
6502 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6503 MovDir[newx][newy] = 0;
6506 if (!CAN_MOVE(element) ||
6507 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6508 MovDir[newx][newy] = 0;
6512 if (!CAN_MOVE(element) ||
6513 (CAN_FALL(element) && direction == MV_DOWN))
6514 GfxDir[x][y] = MovDir[newx][newy] = 0;
6516 if (!CAN_MOVE(element) ||
6517 (CAN_FALL(element) && direction == MV_DOWN &&
6518 (element == EL_SPRING ||
6519 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6520 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6521 GfxDir[x][y] = MovDir[newx][newy] = 0;
6527 DrawLevelField(x, y);
6528 DrawLevelField(newx, newy);
6530 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6532 /* prevent pushed element from moving on in pushed direction */
6533 if (pushed_by_player && CAN_MOVE(element) &&
6534 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6535 !(element_info[element].move_pattern & direction))
6536 TurnRound(newx, newy);
6539 /* prevent elements on conveyor belt from moving on in last direction */
6540 if (pushed_by_conveyor && CAN_FALL(element) &&
6541 direction & MV_HORIZONTAL)
6544 if (CAN_MOVE(element))
6545 InitMovDir(newx, newy);
6547 MovDir[newx][newy] = 0;
6549 MovDir[newx][newy] = 0;
6554 if (!pushed_by_player)
6556 int nextx = newx + dx, nexty = newy + dy;
6557 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6559 WasJustMoving[newx][newy] = 3;
6561 if (CAN_FALL(element) && direction == MV_DOWN)
6562 WasJustFalling[newx][newy] = 3;
6564 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6565 CheckCollision[newx][newy] = 2;
6568 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6570 TestIfBadThingTouchesHero(newx, newy);
6571 TestIfBadThingTouchesFriend(newx, newy);
6573 if (!IS_CUSTOM_ELEMENT(element))
6574 TestIfBadThingTouchesOtherBadThing(newx, newy);
6576 else if (element == EL_PENGUIN)
6577 TestIfFriendTouchesBadThing(newx, newy);
6579 #if USE_NEW_MOVE_STYLE
6581 if (CAN_FALL(element) && direction == MV_DOWN &&
6582 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6583 IS_PLAYER(x, newy + 1))
6584 printf("::: we would now kill the player [%d]\n", FrameCounter);
6587 /* give the player one last chance (one more frame) to move away */
6588 if (CAN_FALL(element) && direction == MV_DOWN &&
6589 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6590 (!IS_PLAYER(x, newy + 1) ||
6591 game.engine_version < VERSION_IDENT(3,1,1,0)))
6594 if (CAN_FALL(element) && direction == MV_DOWN &&
6595 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6603 if (pushed_by_player && !game.use_bug_change_when_pushing)
6605 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6608 if (pushed_by_player)
6613 int dig_side = MV_DIR_OPPOSITE(direction);
6615 static int trigger_sides[4] =
6617 CH_SIDE_RIGHT, /* moving left */
6618 CH_SIDE_LEFT, /* moving right */
6619 CH_SIDE_BOTTOM, /* moving up */
6620 CH_SIDE_TOP, /* moving down */
6622 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6624 struct PlayerInfo *player = PLAYERINFO(x, y);
6626 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6627 player->index_bit, dig_side);
6628 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6629 player->index_bit, dig_side);
6634 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6638 if (ChangePage[newx][newy] != -1) /* delayed change */
6639 ChangeElement(newx, newy, ChangePage[newx][newy]);
6644 TestIfElementHitsCustomElement(newx, newy, direction);
6648 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6650 int hitting_element = Feld[newx][newy];
6652 /* !!! fix side (direction) orientation here and elsewhere !!! */
6653 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6657 if (IN_LEV_FIELD(nextx, nexty))
6659 int opposite_direction = MV_DIR_OPPOSITE(direction);
6660 int hitting_side = direction;
6661 int touched_side = opposite_direction;
6662 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6663 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6664 MovDir[nextx][nexty] != direction ||
6665 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6671 CheckElementChangeBySide(nextx, nexty, touched_element,
6672 CE_HIT_BY_SOMETHING, opposite_direction);
6674 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6675 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6677 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6679 struct ElementChangeInfo *change =
6680 &element_info[hitting_element].change_page[i];
6682 if (change->can_change &&
6683 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6684 change->trigger_side & touched_side &&
6685 change->trigger_element == touched_element)
6687 CheckElementChangeByPage(newx, newy, hitting_element,
6688 touched_element, CE_OTHER_IS_HITTING,i);
6694 if (IS_CUSTOM_ELEMENT(touched_element) &&
6695 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6697 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6699 struct ElementChangeInfo *change =
6700 &element_info[touched_element].change_page[i];
6702 if (change->can_change &&
6703 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6704 change->trigger_side & hitting_side &&
6705 change->trigger_element == hitting_element)
6707 CheckElementChangeByPage(nextx, nexty, touched_element,
6708 hitting_element, CE_OTHER_GETS_HIT, i);
6719 TestIfPlayerTouchesCustomElement(newx, newy);
6720 TestIfElementTouchesCustomElement(newx, newy);
6723 int AmoebeNachbarNr(int ax, int ay)
6726 int element = Feld[ax][ay];
6728 static int xy[4][2] =
6736 for (i = 0; i < NUM_DIRECTIONS; i++)
6738 int x = ax + xy[i][0];
6739 int y = ay + xy[i][1];
6741 if (!IN_LEV_FIELD(x, y))
6744 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6745 group_nr = AmoebaNr[x][y];
6751 void AmoebenVereinigen(int ax, int ay)
6753 int i, x, y, xx, yy;
6754 int new_group_nr = AmoebaNr[ax][ay];
6755 static int xy[4][2] =
6763 if (new_group_nr == 0)
6766 for (i = 0; i < NUM_DIRECTIONS; i++)
6771 if (!IN_LEV_FIELD(x, y))
6774 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6775 Feld[x][y] == EL_BD_AMOEBA ||
6776 Feld[x][y] == EL_AMOEBA_DEAD) &&
6777 AmoebaNr[x][y] != new_group_nr)
6779 int old_group_nr = AmoebaNr[x][y];
6781 if (old_group_nr == 0)
6784 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6785 AmoebaCnt[old_group_nr] = 0;
6786 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6787 AmoebaCnt2[old_group_nr] = 0;
6789 for (yy = 0; yy < lev_fieldy; yy++)
6791 for (xx = 0; xx < lev_fieldx; xx++)
6793 if (AmoebaNr[xx][yy] == old_group_nr)
6794 AmoebaNr[xx][yy] = new_group_nr;
6801 void AmoebeUmwandeln(int ax, int ay)
6805 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6807 int group_nr = AmoebaNr[ax][ay];
6812 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6813 printf("AmoebeUmwandeln(): This should never happen!\n");
6818 for (y = 0; y < lev_fieldy; y++)
6820 for (x = 0; x < lev_fieldx; x++)
6822 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6825 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6829 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6830 SND_AMOEBA_TURNING_TO_GEM :
6831 SND_AMOEBA_TURNING_TO_ROCK));
6836 static int xy[4][2] =
6844 for (i = 0; i < NUM_DIRECTIONS; i++)
6849 if (!IN_LEV_FIELD(x, y))
6852 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6854 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6855 SND_AMOEBA_TURNING_TO_GEM :
6856 SND_AMOEBA_TURNING_TO_ROCK));
6863 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6866 int group_nr = AmoebaNr[ax][ay];
6867 boolean done = FALSE;
6872 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6873 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6878 for (y = 0; y < lev_fieldy; y++)
6880 for (x = 0; x < lev_fieldx; x++)
6882 if (AmoebaNr[x][y] == group_nr &&
6883 (Feld[x][y] == EL_AMOEBA_DEAD ||
6884 Feld[x][y] == EL_BD_AMOEBA ||
6885 Feld[x][y] == EL_AMOEBA_GROWING))
6888 Feld[x][y] = new_element;
6889 InitField(x, y, FALSE);
6890 DrawLevelField(x, y);
6897 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6898 SND_BD_AMOEBA_TURNING_TO_ROCK :
6899 SND_BD_AMOEBA_TURNING_TO_GEM));
6902 void AmoebeWaechst(int x, int y)
6904 static unsigned long sound_delay = 0;
6905 static unsigned long sound_delay_value = 0;
6907 if (!MovDelay[x][y]) /* start new growing cycle */
6911 if (DelayReached(&sound_delay, sound_delay_value))
6914 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6916 if (Store[x][y] == EL_BD_AMOEBA)
6917 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6919 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6921 sound_delay_value = 30;
6925 if (MovDelay[x][y]) /* wait some time before growing bigger */
6928 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6930 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6931 6 - MovDelay[x][y]);
6933 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6936 if (!MovDelay[x][y])
6938 Feld[x][y] = Store[x][y];
6940 DrawLevelField(x, y);
6945 void AmoebaDisappearing(int x, int y)
6947 static unsigned long sound_delay = 0;
6948 static unsigned long sound_delay_value = 0;
6950 if (!MovDelay[x][y]) /* start new shrinking cycle */
6954 if (DelayReached(&sound_delay, sound_delay_value))
6955 sound_delay_value = 30;
6958 if (MovDelay[x][y]) /* wait some time before shrinking */
6961 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6963 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6964 6 - MovDelay[x][y]);
6966 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6969 if (!MovDelay[x][y])
6971 Feld[x][y] = EL_EMPTY;
6972 DrawLevelField(x, y);
6974 /* don't let mole enter this field in this cycle;
6975 (give priority to objects falling to this field from above) */
6981 void AmoebeAbleger(int ax, int ay)
6984 int element = Feld[ax][ay];
6985 int graphic = el2img(element);
6986 int newax = ax, neway = ay;
6987 static int xy[4][2] =
6995 if (!level.amoeba_speed)
6997 Feld[ax][ay] = EL_AMOEBA_DEAD;
6998 DrawLevelField(ax, ay);
7002 if (IS_ANIMATED(graphic))
7003 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7005 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7006 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7008 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7011 if (MovDelay[ax][ay])
7015 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7018 int x = ax + xy[start][0];
7019 int y = ay + xy[start][1];
7021 if (!IN_LEV_FIELD(x, y))
7025 if (IS_FREE(x, y) ||
7026 CAN_GROW_INTO(Feld[x][y]) ||
7027 Feld[x][y] == EL_QUICKSAND_EMPTY)
7033 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7034 if (IS_FREE(x, y) ||
7035 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7042 if (newax == ax && neway == ay)
7045 else /* normal or "filled" (BD style) amoeba */
7048 boolean waiting_for_player = FALSE;
7050 for (i = 0; i < NUM_DIRECTIONS; i++)
7052 int j = (start + i) % 4;
7053 int x = ax + xy[j][0];
7054 int y = ay + xy[j][1];
7056 if (!IN_LEV_FIELD(x, y))
7060 if (IS_FREE(x, y) ||
7061 CAN_GROW_INTO(Feld[x][y]) ||
7062 Feld[x][y] == EL_QUICKSAND_EMPTY)
7069 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7070 if (IS_FREE(x, y) ||
7071 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7078 else if (IS_PLAYER(x, y))
7079 waiting_for_player = TRUE;
7082 if (newax == ax && neway == ay) /* amoeba cannot grow */
7085 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7087 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7090 Feld[ax][ay] = EL_AMOEBA_DEAD;
7091 DrawLevelField(ax, ay);
7092 AmoebaCnt[AmoebaNr[ax][ay]]--;
7094 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7096 if (element == EL_AMOEBA_FULL)
7097 AmoebeUmwandeln(ax, ay);
7098 else if (element == EL_BD_AMOEBA)
7099 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7104 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7106 /* amoeba gets larger by growing in some direction */
7108 int new_group_nr = AmoebaNr[ax][ay];
7111 if (new_group_nr == 0)
7113 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7114 printf("AmoebeAbleger(): This should never happen!\n");
7119 AmoebaNr[newax][neway] = new_group_nr;
7120 AmoebaCnt[new_group_nr]++;
7121 AmoebaCnt2[new_group_nr]++;
7123 /* if amoeba touches other amoeba(s) after growing, unify them */
7124 AmoebenVereinigen(newax, neway);
7126 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7128 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7134 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7135 (neway == lev_fieldy - 1 && newax != ax))
7137 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7138 Store[newax][neway] = element;
7140 else if (neway == ay)
7142 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7144 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7146 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7151 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7152 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7153 Store[ax][ay] = EL_AMOEBA_DROP;
7154 ContinueMoving(ax, ay);
7158 DrawLevelField(newax, neway);
7161 void Life(int ax, int ay)
7164 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7166 int element = Feld[ax][ay];
7167 int graphic = el2img(element);
7168 boolean changed = FALSE;
7170 if (IS_ANIMATED(graphic))
7171 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7176 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7177 MovDelay[ax][ay] = life_time;
7179 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7182 if (MovDelay[ax][ay])
7186 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7188 int xx = ax+x1, yy = ay+y1;
7191 if (!IN_LEV_FIELD(xx, yy))
7194 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7196 int x = xx+x2, y = yy+y2;
7198 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7201 if (((Feld[x][y] == element ||
7202 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7204 (IS_FREE(x, y) && Stop[x][y]))
7208 if (xx == ax && yy == ay) /* field in the middle */
7210 if (nachbarn < life[0] || nachbarn > life[1])
7212 Feld[xx][yy] = EL_EMPTY;
7214 DrawLevelField(xx, yy);
7215 Stop[xx][yy] = TRUE;
7220 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7221 { /* free border field */
7222 if (nachbarn >= life[2] && nachbarn <= life[3])
7224 Feld[xx][yy] = element;
7225 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7227 DrawLevelField(xx, yy);
7228 Stop[xx][yy] = TRUE;
7233 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7234 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7235 { /* free border field */
7236 if (nachbarn >= life[2] && nachbarn <= life[3])
7238 Feld[xx][yy] = element;
7239 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7241 DrawLevelField(xx, yy);
7242 Stop[xx][yy] = TRUE;
7250 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7251 SND_GAME_OF_LIFE_GROWING);
7254 static void InitRobotWheel(int x, int y)
7256 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7259 static void RunRobotWheel(int x, int y)
7261 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7264 static void StopRobotWheel(int x, int y)
7266 if (ZX == x && ZY == y)
7270 static void InitTimegateWheel(int x, int y)
7273 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7275 /* another brainless, "type style" bug ... :-( */
7276 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7280 static void RunTimegateWheel(int x, int y)
7282 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7285 void CheckExit(int x, int y)
7287 if (local_player->gems_still_needed > 0 ||
7288 local_player->sokobanfields_still_needed > 0 ||
7289 local_player->lights_still_needed > 0)
7291 int element = Feld[x][y];
7292 int graphic = el2img(element);
7294 if (IS_ANIMATED(graphic))
7295 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7300 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7303 Feld[x][y] = EL_EXIT_OPENING;
7305 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7308 void CheckExitSP(int x, int y)
7310 if (local_player->gems_still_needed > 0)
7312 int element = Feld[x][y];
7313 int graphic = el2img(element);
7315 if (IS_ANIMATED(graphic))
7316 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7321 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7324 Feld[x][y] = EL_SP_EXIT_OPENING;
7326 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7329 static void CloseAllOpenTimegates()
7333 for (y = 0; y < lev_fieldy; y++)
7335 for (x = 0; x < lev_fieldx; x++)
7337 int element = Feld[x][y];
7339 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7341 Feld[x][y] = EL_TIMEGATE_CLOSING;
7343 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7345 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7352 void EdelsteinFunkeln(int x, int y)
7354 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7357 if (Feld[x][y] == EL_BD_DIAMOND)
7360 if (MovDelay[x][y] == 0) /* next animation frame */
7361 MovDelay[x][y] = 11 * !SimpleRND(500);
7363 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7367 if (setup.direct_draw && MovDelay[x][y])
7368 SetDrawtoField(DRAW_BUFFERED);
7370 DrawLevelElementAnimation(x, y, Feld[x][y]);
7372 if (MovDelay[x][y] != 0)
7374 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7375 10 - MovDelay[x][y]);
7377 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7379 if (setup.direct_draw)
7383 dest_x = FX + SCREENX(x) * TILEX;
7384 dest_y = FY + SCREENY(y) * TILEY;
7386 BlitBitmap(drawto_field, window,
7387 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7388 SetDrawtoField(DRAW_DIRECT);
7394 void MauerWaechst(int x, int y)
7398 if (!MovDelay[x][y]) /* next animation frame */
7399 MovDelay[x][y] = 3 * delay;
7401 if (MovDelay[x][y]) /* wait some time before next frame */
7405 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7407 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7408 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7410 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7413 if (!MovDelay[x][y])
7415 if (MovDir[x][y] == MV_LEFT)
7417 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7418 DrawLevelField(x - 1, y);
7420 else if (MovDir[x][y] == MV_RIGHT)
7422 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7423 DrawLevelField(x + 1, y);
7425 else if (MovDir[x][y] == MV_UP)
7427 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7428 DrawLevelField(x, y - 1);
7432 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7433 DrawLevelField(x, y + 1);
7436 Feld[x][y] = Store[x][y];
7438 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7439 DrawLevelField(x, y);
7444 void MauerAbleger(int ax, int ay)
7446 int element = Feld[ax][ay];
7447 int graphic = el2img(element);
7448 boolean oben_frei = FALSE, unten_frei = FALSE;
7449 boolean links_frei = FALSE, rechts_frei = FALSE;
7450 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7451 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7452 boolean new_wall = FALSE;
7454 if (IS_ANIMATED(graphic))
7455 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7457 if (!MovDelay[ax][ay]) /* start building new wall */
7458 MovDelay[ax][ay] = 6;
7460 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7463 if (MovDelay[ax][ay])
7467 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7469 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7471 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7473 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7476 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7477 element == EL_EXPANDABLE_WALL_ANY)
7481 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7482 Store[ax][ay-1] = element;
7483 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7484 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7485 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7486 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7491 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7492 Store[ax][ay+1] = element;
7493 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7494 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7495 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7496 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7501 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7502 element == EL_EXPANDABLE_WALL_ANY ||
7503 element == EL_EXPANDABLE_WALL)
7507 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7508 Store[ax-1][ay] = element;
7509 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7510 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7511 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7512 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7518 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7519 Store[ax+1][ay] = element;
7520 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7521 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7522 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7523 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7528 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7529 DrawLevelField(ax, ay);
7531 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7533 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7534 unten_massiv = TRUE;
7535 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7536 links_massiv = TRUE;
7537 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7538 rechts_massiv = TRUE;
7540 if (((oben_massiv && unten_massiv) ||
7541 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7542 element == EL_EXPANDABLE_WALL) &&
7543 ((links_massiv && rechts_massiv) ||
7544 element == EL_EXPANDABLE_WALL_VERTICAL))
7545 Feld[ax][ay] = EL_WALL;
7549 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7551 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7555 void CheckForDragon(int x, int y)
7558 boolean dragon_found = FALSE;
7559 static int xy[4][2] =
7567 for (i = 0; i < NUM_DIRECTIONS; i++)
7569 for (j = 0; j < 4; j++)
7571 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7573 if (IN_LEV_FIELD(xx, yy) &&
7574 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7576 if (Feld[xx][yy] == EL_DRAGON)
7577 dragon_found = TRUE;
7586 for (i = 0; i < NUM_DIRECTIONS; i++)
7588 for (j = 0; j < 3; j++)
7590 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7592 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7594 Feld[xx][yy] = EL_EMPTY;
7595 DrawLevelField(xx, yy);
7604 static void InitBuggyBase(int x, int y)
7606 int element = Feld[x][y];
7607 int activating_delay = FRAMES_PER_SECOND / 4;
7610 (element == EL_SP_BUGGY_BASE ?
7611 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7612 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7614 element == EL_SP_BUGGY_BASE_ACTIVE ?
7615 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7618 static void WarnBuggyBase(int x, int y)
7621 static int xy[4][2] =
7629 for (i = 0; i < NUM_DIRECTIONS; i++)
7631 int xx = x + xy[i][0], yy = y + xy[i][1];
7633 if (IS_PLAYER(xx, yy))
7635 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7642 static void InitTrap(int x, int y)
7644 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7647 static void ActivateTrap(int x, int y)
7649 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7652 static void ChangeActiveTrap(int x, int y)
7654 int graphic = IMG_TRAP_ACTIVE;
7656 /* if new animation frame was drawn, correct crumbled sand border */
7657 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7658 DrawLevelFieldCrumbledSand(x, y);
7661 static void ChangeElementNowExt(int x, int y, int target_element)
7663 int previous_move_direction = MovDir[x][y];
7665 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7666 IS_WALKABLE(Feld[x][y]));
7668 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7669 IS_WALKABLE(Feld[x][y]) &&
7673 /* check if element under player changes from accessible to unaccessible
7674 (needed for special case of dropping element which then changes) */
7675 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7676 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7679 printf("::: BOOOM! [%d, '%s']\n", target_element,
7680 element_info[target_element].token_name);
7692 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7693 RemoveMovingField(x, y);
7697 Feld[x][y] = target_element;
7700 Feld[x][y] = target_element;
7703 ResetGfxAnimation(x, y);
7704 ResetRandomAnimationValue(x, y);
7706 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7707 MovDir[x][y] = previous_move_direction;
7710 InitField_WithBug1(x, y, FALSE);
7712 InitField(x, y, FALSE);
7713 if (CAN_MOVE(Feld[x][y]))
7717 DrawLevelField(x, y);
7719 if (GFX_CRUMBLED(Feld[x][y]))
7720 DrawLevelFieldCrumbledSandNeighbours(x, y);
7724 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7728 TestIfBadThingTouchesHero(x, y);
7729 TestIfPlayerTouchesCustomElement(x, y);
7730 TestIfElementTouchesCustomElement(x, y);
7733 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7734 if (ELEM_IS_PLAYER(target_element))
7735 RelocatePlayer(x, y, target_element);
7738 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7742 TestIfBadThingTouchesHero(x, y);
7743 TestIfPlayerTouchesCustomElement(x, y);
7744 TestIfElementTouchesCustomElement(x, y);
7748 static boolean ChangeElementNow(int x, int y, int element, int page)
7750 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7752 int old_element = Feld[x][y];
7754 /* always use default change event to prevent running into a loop */
7755 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7756 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7758 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7760 /* reset actual trigger element and player */
7761 change->actual_trigger_element = EL_EMPTY;
7762 change->actual_trigger_player = EL_PLAYER_1;
7765 /* do not change already changed elements with same change event */
7767 if (Changed[x][y] & ChangeEvent[x][y])
7774 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7777 /* !!! indirect change before direct change !!! */
7778 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7781 if (change->explode)
7788 if (change->use_target_content)
7790 boolean complete_replace = TRUE;
7791 boolean can_replace[3][3];
7794 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7797 boolean is_walkable;
7798 boolean is_diggable;
7799 boolean is_collectible;
7800 boolean is_removable;
7801 boolean is_destructible;
7802 int ex = x + xx - 1;
7803 int ey = y + yy - 1;
7804 int content_element = change->target_content[xx][yy];
7807 can_replace[xx][yy] = TRUE;
7809 if (ex == x && ey == y) /* do not check changing element itself */
7812 if (content_element == EL_EMPTY_SPACE)
7814 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7819 if (!IN_LEV_FIELD(ex, ey))
7821 can_replace[xx][yy] = FALSE;
7822 complete_replace = FALSE;
7828 if (Changed[ex][ey]) /* do not change already changed elements */
7830 can_replace[xx][yy] = FALSE;
7831 complete_replace = FALSE;
7839 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7840 e = MovingOrBlocked2Element(ex, ey);
7845 is_empty = (IS_FREE(ex, ey) ||
7846 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7847 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7848 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7852 is_empty = (IS_FREE(ex, ey) ||
7853 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7855 is_empty = (IS_FREE(ex, ey) ||
7856 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7861 is_walkable = (is_empty || IS_WALKABLE(e));
7862 is_diggable = (is_empty || IS_DIGGABLE(e));
7863 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7864 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7865 is_removable = (is_diggable || is_collectible);
7867 can_replace[xx][yy] =
7868 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7869 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7870 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7871 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7872 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7873 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7874 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7876 if (!can_replace[xx][yy])
7877 complete_replace = FALSE;
7879 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7880 IS_WALKABLE(content_element)));
7882 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7884 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7887 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7888 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7889 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7891 can_replace[xx][yy] = FALSE;
7892 complete_replace = FALSE;
7897 if (!change->only_if_complete || complete_replace)
7899 boolean something_has_changed = FALSE;
7901 if (change->only_if_complete && change->use_random_replace &&
7902 RND(100) < change->random_percentage)
7905 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7907 int ex = x + xx - 1;
7908 int ey = y + yy - 1;
7909 int content_element;
7911 if (can_replace[xx][yy] && (!change->use_random_replace ||
7912 RND(100) < change->random_percentage))
7914 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7915 RemoveMovingField(ex, ey);
7917 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7919 content_element = change->target_content[xx][yy];
7920 target_element = GET_TARGET_ELEMENT(content_element, change);
7922 ChangeElementNowExt(ex, ey, target_element);
7924 something_has_changed = TRUE;
7926 /* for symmetry reasons, freeze newly created border elements */
7927 if (ex != x || ey != y)
7928 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7932 if (something_has_changed)
7933 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7938 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7940 ChangeElementNowExt(x, y, target_element);
7942 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7946 /* this uses direct change before indirect change */
7947 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7953 static void ChangeElement(int x, int y, int page)
7955 int element = MovingOrBlocked2Element(x, y);
7956 struct ElementInfo *ei = &element_info[element];
7957 struct ElementChangeInfo *change = &ei->change_page[page];
7960 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7963 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7964 x, y, element, element_info[element].token_name);
7965 printf("ChangeElement(): This should never happen!\n");
7970 /* this can happen with classic bombs on walkable, changing elements */
7971 if (!CAN_CHANGE(element))
7974 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7975 ChangeDelay[x][y] = 0;
7981 if (ChangeDelay[x][y] == 0) /* initialize element change */
7983 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7984 RND(change->delay_random * change->delay_frames)) + 1;
7986 ResetGfxAnimation(x, y);
7987 ResetRandomAnimationValue(x, y);
7989 if (change->pre_change_function)
7990 change->pre_change_function(x, y);
7993 ChangeDelay[x][y]--;
7995 if (ChangeDelay[x][y] != 0) /* continue element change */
7997 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7999 if (IS_ANIMATED(graphic))
8000 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8002 if (change->change_function)
8003 change->change_function(x, y);
8005 else /* finish element change */
8007 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8009 page = ChangePage[x][y];
8010 ChangePage[x][y] = -1;
8012 change = &ei->change_page[page];
8016 if (IS_MOVING(x, y) && !change->explode)
8018 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8021 ChangeDelay[x][y] = 1; /* try change after next move step */
8022 ChangePage[x][y] = page; /* remember page to use for change */
8027 if (ChangeElementNow(x, y, element, page))
8029 if (change->post_change_function)
8030 change->post_change_function(x, y);
8035 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8036 int trigger_element,
8043 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8045 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8048 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8050 int element = EL_CUSTOM_START + i;
8052 boolean change_element = FALSE;
8055 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8058 for (j = 0; j < element_info[element].num_change_pages; j++)
8060 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8062 if (change->can_change &&
8063 change->events & CH_EVENT_BIT(trigger_event) &&
8064 change->trigger_side & trigger_side &&
8065 change->trigger_player & trigger_player &&
8066 change->trigger_page & trigger_page_bits &&
8067 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8070 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8071 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8072 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8075 change_element = TRUE;
8078 change->actual_trigger_element = trigger_element;
8079 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8085 if (!change_element)
8088 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8091 if (x == lx && y == ly) /* do not change trigger element itself */
8095 if (Feld[x][y] == element)
8097 ChangeDelay[x][y] = 1;
8098 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8099 ChangeElement(x, y, page);
8107 static boolean CheckElementChangeExt(int x, int y,
8109 int trigger_element,
8115 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8118 if (Feld[x][y] == EL_BLOCKED)
8120 Blocked2Moving(x, y, &x, &y);
8121 element = Feld[x][y];
8125 if (Feld[x][y] != element) /* check if element has already changed */
8128 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8129 Feld[x][y], element_info[Feld[x][y]].token_name,
8130 element, element_info[element].token_name,
8139 if (trigger_page < 0)
8141 boolean change_element = FALSE;
8144 for (i = 0; i < element_info[element].num_change_pages; i++)
8146 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8148 if (change->can_change &&
8149 change->events & CH_EVENT_BIT(trigger_event) &&
8150 change->trigger_side & trigger_side &&
8151 change->trigger_player & trigger_player)
8153 change_element = TRUE;
8156 change->actual_trigger_element = trigger_element;
8157 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8163 if (!change_element)
8168 struct ElementInfo *ei = &element_info[element];
8169 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8171 change->actual_trigger_element = trigger_element;
8172 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8177 /* !!! this check misses pages with same event, but different side !!! */
8179 if (trigger_page < 0)
8180 trigger_page = element_info[element].event_page_nr[trigger_event];
8182 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8186 ChangeDelay[x][y] = 1;
8187 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8188 ChangeElement(x, y, trigger_page);
8193 static void PlayPlayerSound(struct PlayerInfo *player)
8195 int jx = player->jx, jy = player->jy;
8196 int element = player->element_nr;
8197 int last_action = player->last_action_waiting;
8198 int action = player->action_waiting;
8200 if (player->is_waiting)
8202 if (action != last_action)
8203 PlayLevelSoundElementAction(jx, jy, element, action);
8205 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8209 if (action != last_action)
8210 StopSound(element_info[element].sound[last_action]);
8212 if (last_action == ACTION_SLEEPING)
8213 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8217 static void PlayAllPlayersSound()
8221 for (i = 0; i < MAX_PLAYERS; i++)
8222 if (stored_player[i].active)
8223 PlayPlayerSound(&stored_player[i]);
8226 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8228 boolean last_waiting = player->is_waiting;
8229 int move_dir = player->MovDir;
8231 player->last_action_waiting = player->action_waiting;
8235 if (!last_waiting) /* not waiting -> waiting */
8237 player->is_waiting = TRUE;
8239 player->frame_counter_bored =
8241 game.player_boring_delay_fixed +
8242 SimpleRND(game.player_boring_delay_random);
8243 player->frame_counter_sleeping =
8245 game.player_sleeping_delay_fixed +
8246 SimpleRND(game.player_sleeping_delay_random);
8248 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8251 if (game.player_sleeping_delay_fixed +
8252 game.player_sleeping_delay_random > 0 &&
8253 player->anim_delay_counter == 0 &&
8254 player->post_delay_counter == 0 &&
8255 FrameCounter >= player->frame_counter_sleeping)
8256 player->is_sleeping = TRUE;
8257 else if (game.player_boring_delay_fixed +
8258 game.player_boring_delay_random > 0 &&
8259 FrameCounter >= player->frame_counter_bored)
8260 player->is_bored = TRUE;
8262 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8263 player->is_bored ? ACTION_BORING :
8266 if (player->is_sleeping)
8268 if (player->num_special_action_sleeping > 0)
8270 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8272 int last_special_action = player->special_action_sleeping;
8273 int num_special_action = player->num_special_action_sleeping;
8274 int special_action =
8275 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8276 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8277 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8278 last_special_action + 1 : ACTION_SLEEPING);
8279 int special_graphic =
8280 el_act_dir2img(player->element_nr, special_action, move_dir);
8282 player->anim_delay_counter =
8283 graphic_info[special_graphic].anim_delay_fixed +
8284 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8285 player->post_delay_counter =
8286 graphic_info[special_graphic].post_delay_fixed +
8287 SimpleRND(graphic_info[special_graphic].post_delay_random);
8289 player->special_action_sleeping = special_action;
8292 if (player->anim_delay_counter > 0)
8294 player->action_waiting = player->special_action_sleeping;
8295 player->anim_delay_counter--;
8297 else if (player->post_delay_counter > 0)
8299 player->post_delay_counter--;
8303 else if (player->is_bored)
8305 if (player->num_special_action_bored > 0)
8307 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8309 int special_action =
8310 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8311 int special_graphic =
8312 el_act_dir2img(player->element_nr, special_action, move_dir);
8314 player->anim_delay_counter =
8315 graphic_info[special_graphic].anim_delay_fixed +
8316 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8317 player->post_delay_counter =
8318 graphic_info[special_graphic].post_delay_fixed +
8319 SimpleRND(graphic_info[special_graphic].post_delay_random);
8321 player->special_action_bored = special_action;
8324 if (player->anim_delay_counter > 0)
8326 player->action_waiting = player->special_action_bored;
8327 player->anim_delay_counter--;
8329 else if (player->post_delay_counter > 0)
8331 player->post_delay_counter--;
8336 else if (last_waiting) /* waiting -> not waiting */
8338 player->is_waiting = FALSE;
8339 player->is_bored = FALSE;
8340 player->is_sleeping = FALSE;
8342 player->frame_counter_bored = -1;
8343 player->frame_counter_sleeping = -1;
8345 player->anim_delay_counter = 0;
8346 player->post_delay_counter = 0;
8348 player->action_waiting = ACTION_DEFAULT;
8350 player->special_action_bored = ACTION_DEFAULT;
8351 player->special_action_sleeping = ACTION_DEFAULT;
8356 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8359 static byte stored_player_action[MAX_PLAYERS];
8360 static int num_stored_actions = 0;
8362 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8363 int left = player_action & JOY_LEFT;
8364 int right = player_action & JOY_RIGHT;
8365 int up = player_action & JOY_UP;
8366 int down = player_action & JOY_DOWN;
8367 int button1 = player_action & JOY_BUTTON_1;
8368 int button2 = player_action & JOY_BUTTON_2;
8369 int dx = (left ? -1 : right ? 1 : 0);
8370 int dy = (up ? -1 : down ? 1 : 0);
8373 stored_player_action[player->index_nr] = 0;
8374 num_stored_actions++;
8378 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8381 if (!player->active || tape.pausing)
8385 printf("::: [%d %d %d %d] [%d %d]\n",
8386 left, right, up, down, button1, button2);
8392 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8397 if (player->MovPos == 0)
8398 CheckGravityMovement(player);
8401 snapped = SnapField(player, dx, dy);
8405 dropped = DropElement(player);
8407 moved = MovePlayer(player, dx, dy);
8410 if (tape.single_step && tape.recording && !tape.pausing)
8412 if (button1 || (dropped && !moved))
8414 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8415 SnapField(player, 0, 0); /* stop snapping */
8419 SetPlayerWaiting(player, FALSE);
8422 return player_action;
8424 stored_player_action[player->index_nr] = player_action;
8430 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8433 /* no actions for this player (no input at player's configured device) */
8435 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8436 SnapField(player, 0, 0);
8437 CheckGravityMovementWhenNotMoving(player);
8439 if (player->MovPos == 0)
8440 SetPlayerWaiting(player, TRUE);
8442 if (player->MovPos == 0) /* needed for tape.playing */
8443 player->is_moving = FALSE;
8445 player->is_dropping = FALSE;
8451 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8453 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8455 TapeRecordAction(stored_player_action);
8456 num_stored_actions = 0;
8463 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8465 static byte stored_player_action[MAX_PLAYERS];
8466 static int num_stored_actions = 0;
8467 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8468 int left = player_action & JOY_LEFT;
8469 int right = player_action & JOY_RIGHT;
8470 int up = player_action & JOY_UP;
8471 int down = player_action & JOY_DOWN;
8472 int button1 = player_action & JOY_BUTTON_1;
8473 int button2 = player_action & JOY_BUTTON_2;
8474 int dx = (left ? -1 : right ? 1 : 0);
8475 int dy = (up ? -1 : down ? 1 : 0);
8477 stored_player_action[player->index_nr] = 0;
8478 num_stored_actions++;
8480 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8482 if (!player->active || tape.pausing)
8487 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8490 snapped = SnapField(player, dx, dy);
8494 dropped = DropElement(player);
8496 moved = MovePlayer(player, dx, dy);
8499 if (tape.single_step && tape.recording && !tape.pausing)
8501 if (button1 || (dropped && !moved))
8503 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8504 SnapField(player, 0, 0); /* stop snapping */
8508 stored_player_action[player->index_nr] = player_action;
8512 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8514 /* no actions for this player (no input at player's configured device) */
8516 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8517 SnapField(player, 0, 0);
8518 CheckGravityMovementWhenNotMoving(player);
8520 if (player->MovPos == 0)
8521 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8523 if (player->MovPos == 0) /* needed for tape.playing */
8524 player->is_moving = FALSE;
8527 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8529 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8531 TapeRecordAction(stored_player_action);
8532 num_stored_actions = 0;
8537 void AdvanceFrameAndPlayerCounters(int player_nr)
8541 /* advance frame counters (global frame counter and time frame counter) */
8545 /* advance player counters (counters for move delay, move animation etc.) */
8546 for (i = 0; i < MAX_PLAYERS; i++)
8548 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8550 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8552 if (!advance_player_counters) /* not all players may be affected */
8555 stored_player[i].Frame += move_frames;
8557 if (stored_player[i].MovPos != 0)
8558 stored_player[i].StepFrame += move_frames;
8560 #if USE_NEW_MOVE_DELAY
8561 if (stored_player[i].move_delay > 0)
8562 stored_player[i].move_delay--;
8565 #if USE_NEW_PUSH_DELAY
8566 /* due to bugs in previous versions, counter must count up, not down */
8567 if (stored_player[i].push_delay != -1)
8568 stored_player[i].push_delay++;
8571 if (stored_player[i].drop_delay > 0)
8572 stored_player[i].drop_delay--;
8578 static unsigned long game_frame_delay = 0;
8579 unsigned long game_frame_delay_value;
8580 int magic_wall_x = 0, magic_wall_y = 0;
8581 int i, x, y, element, graphic;
8582 byte *recorded_player_action;
8583 byte summarized_player_action = 0;
8585 byte tape_action[MAX_PLAYERS];
8588 if (game_status != GAME_MODE_PLAYING)
8591 game_frame_delay_value =
8592 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8594 if (tape.playing && tape.warp_forward && !tape.pausing)
8595 game_frame_delay_value = 0;
8597 /* ---------- main game synchronization point ---------- */
8599 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8601 if (network_playing && !network_player_action_received)
8605 printf("DEBUG: try to get network player actions in time\n");
8609 #if defined(NETWORK_AVALIABLE)
8610 /* last chance to get network player actions without main loop delay */
8614 if (game_status != GAME_MODE_PLAYING)
8617 if (!network_player_action_received)
8621 printf("DEBUG: failed to get network player actions in time\n");
8632 printf("::: getting new tape action [%d]\n", FrameCounter);
8635 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8638 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8639 if (recorded_player_action == NULL && tape.pausing)
8644 printf("::: %d\n", stored_player[0].action);
8648 if (recorded_player_action != NULL)
8649 for (i = 0; i < MAX_PLAYERS; i++)
8650 stored_player[i].action = recorded_player_action[i];
8653 for (i = 0; i < MAX_PLAYERS; i++)
8655 summarized_player_action |= stored_player[i].action;
8657 if (!network_playing)
8658 stored_player[i].effective_action = stored_player[i].action;
8661 #if defined(NETWORK_AVALIABLE)
8662 if (network_playing)
8663 SendToServer_MovePlayer(summarized_player_action);
8666 if (!options.network && !setup.team_mode)
8667 local_player->effective_action = summarized_player_action;
8670 if (recorded_player_action != NULL)
8671 for (i = 0; i < MAX_PLAYERS; i++)
8672 stored_player[i].effective_action = recorded_player_action[i];
8676 for (i = 0; i < MAX_PLAYERS; i++)
8678 tape_action[i] = stored_player[i].effective_action;
8680 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8681 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8684 /* only save actions from input devices, but not programmed actions */
8686 TapeRecordAction(tape_action);
8689 for (i = 0; i < MAX_PLAYERS; i++)
8691 int actual_player_action = stored_player[i].effective_action;
8694 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8695 - rnd_equinox_tetrachloride 048
8696 - rnd_equinox_tetrachloride_ii 096
8697 - rnd_emanuel_schmieg 002
8698 - doctor_sloan_ww 001, 020
8700 if (stored_player[i].MovPos == 0)
8701 CheckGravityMovement(&stored_player[i]);
8705 /* overwrite programmed action with tape action */
8706 if (stored_player[i].programmed_action)
8707 actual_player_action = stored_player[i].programmed_action;
8711 if (stored_player[i].programmed_action)
8712 printf("::: %d\n", stored_player[i].programmed_action);
8715 if (recorded_player_action)
8718 if (stored_player[i].programmed_action &&
8719 stored_player[i].programmed_action != recorded_player_action[i])
8720 printf("::: %d: %d <-> %d\n", i,
8721 stored_player[i].programmed_action, recorded_player_action[i]);
8725 actual_player_action = recorded_player_action[i];
8730 /* overwrite tape action with programmed action */
8731 if (stored_player[i].programmed_action)
8732 actual_player_action = stored_player[i].programmed_action;
8737 printf("::: action: %d: %x [%d]\n",
8738 stored_player[i].MovPos, actual_player_action, FrameCounter);
8742 PlayerActions(&stored_player[i], actual_player_action);
8744 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8746 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8747 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8750 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8755 TapeRecordAction(tape_action);
8758 network_player_action_received = FALSE;
8760 ScrollScreen(NULL, SCROLL_GO_ON);
8766 for (i = 0; i < MAX_PLAYERS; i++)
8767 stored_player[i].Frame++;
8771 /* for backwards compatibility, the following code emulates a fixed bug that
8772 occured when pushing elements (causing elements that just made their last
8773 pushing step to already (if possible) make their first falling step in the
8774 same game frame, which is bad); this code is also needed to use the famous
8775 "spring push bug" which is used in older levels and might be wanted to be
8776 used also in newer levels, but in this case the buggy pushing code is only
8777 affecting the "spring" element and no other elements */
8780 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8782 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8785 for (i = 0; i < MAX_PLAYERS; i++)
8787 struct PlayerInfo *player = &stored_player[i];
8792 if (player->active && player->is_pushing && player->is_moving &&
8794 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8795 Feld[x][y] == EL_SPRING))
8797 if (player->active && player->is_pushing && player->is_moving &&
8801 ContinueMoving(x, y);
8803 /* continue moving after pushing (this is actually a bug) */
8804 if (!IS_MOVING(x, y))
8813 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8815 Changed[x][y] = CE_BITMASK_DEFAULT;
8816 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8818 #if USE_NEW_BLOCK_STYLE
8819 /* this must be handled before main playfield loop */
8820 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8823 if (MovDelay[x][y] <= 0)
8829 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8831 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8832 printf("GameActions(): This should never happen!\n");
8834 ChangePage[x][y] = -1;
8839 if (WasJustMoving[x][y] > 0)
8840 WasJustMoving[x][y]--;
8841 if (WasJustFalling[x][y] > 0)
8842 WasJustFalling[x][y]--;
8843 if (CheckCollision[x][y] > 0)
8844 CheckCollision[x][y]--;
8849 /* reset finished pushing action (not done in ContinueMoving() to allow
8850 continous pushing animation for elements with zero push delay) */
8851 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8853 ResetGfxAnimation(x, y);
8854 DrawLevelField(x, y);
8859 if (IS_BLOCKED(x, y))
8863 Blocked2Moving(x, y, &oldx, &oldy);
8864 if (!IS_MOVING(oldx, oldy))
8866 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8867 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8868 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8869 printf("GameActions(): This should never happen!\n");
8875 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8877 element = Feld[x][y];
8879 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8881 graphic = el2img(element);
8887 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8889 element = graphic = 0;
8893 if (graphic_info[graphic].anim_global_sync)
8894 GfxFrame[x][y] = FrameCounter;
8896 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8897 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8898 ResetRandomAnimationValue(x, y);
8900 SetRandomAnimationValue(x, y);
8903 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8906 if (IS_INACTIVE(element))
8908 if (IS_ANIMATED(graphic))
8909 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8915 /* this may take place after moving, so 'element' may have changed */
8917 if (IS_CHANGING(x, y))
8919 if (IS_CHANGING(x, y) &&
8920 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8924 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8925 element_info[element].event_page_nr[CE_DELAY]);
8927 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8930 element = Feld[x][y];
8931 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8935 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8940 element = Feld[x][y];
8941 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8943 if (element == EL_MOLE)
8944 printf("::: %d, %d, %d [%d]\n",
8945 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8949 if (element == EL_YAMYAM)
8950 printf("::: %d, %d, %d\n",
8951 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8955 if (IS_ANIMATED(graphic) &&
8959 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8962 if (element == EL_BUG)
8963 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8967 if (element == EL_MOLE)
8968 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8972 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8973 EdelsteinFunkeln(x, y);
8975 else if ((element == EL_ACID ||
8976 element == EL_EXIT_OPEN ||
8977 element == EL_SP_EXIT_OPEN ||
8978 element == EL_SP_TERMINAL ||
8979 element == EL_SP_TERMINAL_ACTIVE ||
8980 element == EL_EXTRA_TIME ||
8981 element == EL_SHIELD_NORMAL ||
8982 element == EL_SHIELD_DEADLY) &&
8983 IS_ANIMATED(graphic))
8984 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8985 else if (IS_MOVING(x, y))
8986 ContinueMoving(x, y);
8987 else if (IS_ACTIVE_BOMB(element))
8988 CheckDynamite(x, y);
8990 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8991 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8993 else if (element == EL_AMOEBA_GROWING)
8994 AmoebeWaechst(x, y);
8995 else if (element == EL_AMOEBA_SHRINKING)
8996 AmoebaDisappearing(x, y);
8998 #if !USE_NEW_AMOEBA_CODE
8999 else if (IS_AMOEBALIVE(element))
9000 AmoebeAbleger(x, y);
9003 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9005 else if (element == EL_EXIT_CLOSED)
9007 else if (element == EL_SP_EXIT_CLOSED)
9009 else if (element == EL_EXPANDABLE_WALL_GROWING)
9011 else if (element == EL_EXPANDABLE_WALL ||
9012 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9013 element == EL_EXPANDABLE_WALL_VERTICAL ||
9014 element == EL_EXPANDABLE_WALL_ANY)
9016 else if (element == EL_FLAMES)
9017 CheckForDragon(x, y);
9019 else if (IS_AUTO_CHANGING(element))
9020 ChangeElement(x, y);
9022 else if (element == EL_EXPLOSION)
9023 ; /* drawing of correct explosion animation is handled separately */
9024 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9025 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9028 /* this may take place after moving, so 'element' may have changed */
9029 if (IS_AUTO_CHANGING(Feld[x][y]))
9030 ChangeElement(x, y);
9033 if (IS_BELT_ACTIVE(element))
9034 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9036 if (game.magic_wall_active)
9038 int jx = local_player->jx, jy = local_player->jy;
9040 /* play the element sound at the position nearest to the player */
9041 if ((element == EL_MAGIC_WALL_FULL ||
9042 element == EL_MAGIC_WALL_ACTIVE ||
9043 element == EL_MAGIC_WALL_EMPTYING ||
9044 element == EL_BD_MAGIC_WALL_FULL ||
9045 element == EL_BD_MAGIC_WALL_ACTIVE ||
9046 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9047 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9055 #if USE_NEW_AMOEBA_CODE
9056 /* new experimental amoeba growth stuff */
9058 if (!(FrameCounter % 8))
9061 static unsigned long random = 1684108901;
9063 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9066 x = (random >> 10) % lev_fieldx;
9067 y = (random >> 20) % lev_fieldy;
9069 x = RND(lev_fieldx);
9070 y = RND(lev_fieldy);
9072 element = Feld[x][y];
9075 if (!IS_PLAYER(x,y) &&
9076 (element == EL_EMPTY ||
9077 CAN_GROW_INTO(element) ||
9078 element == EL_QUICKSAND_EMPTY ||
9079 element == EL_ACID_SPLASH_LEFT ||
9080 element == EL_ACID_SPLASH_RIGHT))
9082 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9083 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9084 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9085 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9086 Feld[x][y] = EL_AMOEBA_DROP;
9089 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9090 if (!IS_PLAYER(x,y) &&
9091 (element == EL_EMPTY ||
9092 element == EL_SAND ||
9093 element == EL_QUICKSAND_EMPTY ||
9094 element == EL_ACID_SPLASH_LEFT ||
9095 element == EL_ACID_SPLASH_RIGHT))
9097 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9098 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9099 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9100 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9101 Feld[x][y] = EL_AMOEBA_DROP;
9105 random = random * 129 + 1;
9111 if (game.explosions_delayed)
9114 game.explosions_delayed = FALSE;
9116 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9118 element = Feld[x][y];
9120 if (ExplodeField[x][y])
9121 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9122 else if (element == EL_EXPLOSION)
9123 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9125 ExplodeField[x][y] = EX_TYPE_NONE;
9128 game.explosions_delayed = TRUE;
9131 if (game.magic_wall_active)
9133 if (!(game.magic_wall_time_left % 4))
9135 int element = Feld[magic_wall_x][magic_wall_y];
9137 if (element == EL_BD_MAGIC_WALL_FULL ||
9138 element == EL_BD_MAGIC_WALL_ACTIVE ||
9139 element == EL_BD_MAGIC_WALL_EMPTYING)
9140 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9142 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9145 if (game.magic_wall_time_left > 0)
9147 game.magic_wall_time_left--;
9148 if (!game.magic_wall_time_left)
9150 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9152 element = Feld[x][y];
9154 if (element == EL_MAGIC_WALL_ACTIVE ||
9155 element == EL_MAGIC_WALL_FULL)
9157 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9158 DrawLevelField(x, y);
9160 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9161 element == EL_BD_MAGIC_WALL_FULL)
9163 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9164 DrawLevelField(x, y);
9168 game.magic_wall_active = FALSE;
9173 if (game.light_time_left > 0)
9175 game.light_time_left--;
9177 if (game.light_time_left == 0)
9178 RedrawAllLightSwitchesAndInvisibleElements();
9181 if (game.timegate_time_left > 0)
9183 game.timegate_time_left--;
9185 if (game.timegate_time_left == 0)
9186 CloseAllOpenTimegates();
9189 for (i = 0; i < MAX_PLAYERS; i++)
9191 struct PlayerInfo *player = &stored_player[i];
9193 if (SHIELD_ON(player))
9195 if (player->shield_deadly_time_left)
9196 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9197 else if (player->shield_normal_time_left)
9198 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9202 if (TimeFrames >= FRAMES_PER_SECOND)
9207 for (i = 0; i < MAX_PLAYERS; i++)
9209 struct PlayerInfo *player = &stored_player[i];
9211 if (SHIELD_ON(player))
9213 player->shield_normal_time_left--;
9215 if (player->shield_deadly_time_left > 0)
9216 player->shield_deadly_time_left--;
9220 if (!level.use_step_counter)
9228 if (TimeLeft <= 10 && setup.time_limit)
9229 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9231 DrawGameValue_Time(TimeLeft);
9233 if (!TimeLeft && setup.time_limit)
9234 for (i = 0; i < MAX_PLAYERS; i++)
9235 KillHero(&stored_player[i]);
9237 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9238 DrawGameValue_Time(TimePlayed);
9241 if (tape.recording || tape.playing)
9242 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9246 PlayAllPlayersSound();
9248 if (options.debug) /* calculate frames per second */
9250 static unsigned long fps_counter = 0;
9251 static int fps_frames = 0;
9252 unsigned long fps_delay_ms = Counter() - fps_counter;
9256 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9258 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9261 fps_counter = Counter();
9264 redraw_mask |= REDRAW_FPS;
9268 if (stored_player[0].jx != stored_player[0].last_jx ||
9269 stored_player[0].jy != stored_player[0].last_jy)
9270 printf("::: %d, %d, %d, %d, %d\n",
9271 stored_player[0].MovDir,
9272 stored_player[0].MovPos,
9273 stored_player[0].GfxPos,
9274 stored_player[0].Frame,
9275 stored_player[0].StepFrame);
9278 #if USE_NEW_MOVE_DELAY
9279 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9284 for (i = 0; i < MAX_PLAYERS; i++)
9287 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9289 stored_player[i].Frame += move_frames;
9291 if (stored_player[i].MovPos != 0)
9292 stored_player[i].StepFrame += move_frames;
9294 #if USE_NEW_MOVE_DELAY
9295 if (stored_player[i].move_delay > 0)
9296 stored_player[i].move_delay--;
9299 if (stored_player[i].drop_delay > 0)
9300 stored_player[i].drop_delay--;
9305 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9307 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9309 local_player->show_envelope = 0;
9313 #if USE_NEW_RANDOMIZE
9314 /* use random number generator in every frame to make it less predictable */
9315 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9320 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9322 int min_x = x, min_y = y, max_x = x, max_y = y;
9325 for (i = 0; i < MAX_PLAYERS; i++)
9327 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9329 if (!stored_player[i].active || &stored_player[i] == player)
9332 min_x = MIN(min_x, jx);
9333 min_y = MIN(min_y, jy);
9334 max_x = MAX(max_x, jx);
9335 max_y = MAX(max_y, jy);
9338 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9341 static boolean AllPlayersInVisibleScreen()
9345 for (i = 0; i < MAX_PLAYERS; i++)
9347 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9349 if (!stored_player[i].active)
9352 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9359 void ScrollLevel(int dx, int dy)
9361 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9364 BlitBitmap(drawto_field, drawto_field,
9365 FX + TILEX * (dx == -1) - softscroll_offset,
9366 FY + TILEY * (dy == -1) - softscroll_offset,
9367 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9368 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9369 FX + TILEX * (dx == 1) - softscroll_offset,
9370 FY + TILEY * (dy == 1) - softscroll_offset);
9374 x = (dx == 1 ? BX1 : BX2);
9375 for (y = BY1; y <= BY2; y++)
9376 DrawScreenField(x, y);
9381 y = (dy == 1 ? BY1 : BY2);
9382 for (x = BX1; x <= BX2; x++)
9383 DrawScreenField(x, y);
9386 redraw_mask |= REDRAW_FIELD;
9390 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9392 int nextx = x + dx, nexty = y + dy;
9393 int element = Feld[x][y];
9396 element != EL_SP_PORT_LEFT &&
9397 element != EL_SP_GRAVITY_PORT_LEFT &&
9398 element != EL_SP_PORT_HORIZONTAL &&
9399 element != EL_SP_PORT_ANY) ||
9401 element != EL_SP_PORT_RIGHT &&
9402 element != EL_SP_GRAVITY_PORT_RIGHT &&
9403 element != EL_SP_PORT_HORIZONTAL &&
9404 element != EL_SP_PORT_ANY) ||
9406 element != EL_SP_PORT_UP &&
9407 element != EL_SP_GRAVITY_PORT_UP &&
9408 element != EL_SP_PORT_VERTICAL &&
9409 element != EL_SP_PORT_ANY) ||
9411 element != EL_SP_PORT_DOWN &&
9412 element != EL_SP_GRAVITY_PORT_DOWN &&
9413 element != EL_SP_PORT_VERTICAL &&
9414 element != EL_SP_PORT_ANY) ||
9415 !IN_LEV_FIELD(nextx, nexty) ||
9416 !IS_FREE(nextx, nexty))
9423 static boolean canFallDown(struct PlayerInfo *player)
9425 int jx = player->jx, jy = player->jy;
9427 return (IN_LEV_FIELD(jx, jy + 1) &&
9428 (IS_FREE(jx, jy + 1) ||
9429 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9430 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9431 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9434 static boolean canPassField(int x, int y, int move_dir)
9436 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9437 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9438 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9441 int element = Feld[x][y];
9443 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9444 !CAN_MOVE(element) &&
9445 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9446 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9447 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9450 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9452 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9453 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9454 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9458 int nextx = newx + dx;
9459 int nexty = newy + dy;
9463 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9464 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9466 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9468 (IS_DIGGABLE(Feld[newx][newy]) ||
9469 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9470 canPassField(newx, newy, move_dir)));
9473 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9474 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9475 (IS_DIGGABLE(Feld[newx][newy]) ||
9476 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9477 canPassField(newx, newy, move_dir)));
9480 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9481 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9482 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9483 canPassField(newx, newy, move_dir)));
9485 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9486 (IS_DIGGABLE(Feld[newx][newy]) ||
9487 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9488 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9489 !CAN_MOVE(Feld[newx][newy]) &&
9490 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9491 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9492 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9498 static void CheckGravityMovement(struct PlayerInfo *player)
9500 if (game.gravity && !player->programmed_action)
9503 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9504 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9506 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9507 int move_dir_vertical = player->action & MV_VERTICAL;
9511 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9513 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9516 int jx = player->jx, jy = player->jy;
9518 boolean player_is_moving_to_valid_field =
9519 (!player_is_snapping &&
9520 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9521 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9525 (player->last_move_dir & MV_HORIZONTAL ?
9526 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9527 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9531 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9532 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9533 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9534 int new_jx = jx + dx, new_jy = jy + dy;
9535 int nextx = new_jx + dx, nexty = new_jy + dy;
9541 boolean player_can_fall_down = canFallDown(player);
9543 boolean player_can_fall_down =
9544 (IN_LEV_FIELD(jx, jy + 1) &&
9545 (IS_FREE(jx, jy + 1) ||
9546 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9550 boolean player_can_fall_down =
9551 (IN_LEV_FIELD(jx, jy + 1) &&
9552 (IS_FREE(jx, jy + 1)));
9556 boolean player_is_moving_to_valid_field =
9559 !player_is_snapping &&
9563 IN_LEV_FIELD(new_jx, new_jy) &&
9564 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9565 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9566 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9567 IN_LEV_FIELD(nextx, nexty) &&
9568 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9570 IN_LEV_FIELD(new_jx, new_jy) &&
9571 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9572 Feld[new_jx][new_jy] == EL_SAND ||
9573 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9574 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9575 /* !!! extend EL_SAND to anything diggable !!! */
9581 boolean player_is_standing_on_valid_field =
9582 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9583 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9587 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9588 player_can_fall_down,
9589 player_is_standing_on_valid_field,
9590 player_is_moving_to_valid_field,
9591 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9592 player->effective_action,
9593 player->can_fall_into_acid);
9596 if (player_can_fall_down &&
9598 !player_is_standing_on_valid_field &&
9600 !player_is_moving_to_valid_field)
9603 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9604 jx, jy, FrameCounter);
9607 player->programmed_action = MV_DOWN;
9612 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9615 return CheckGravityMovement(player);
9618 if (game.gravity && !player->programmed_action)
9620 int jx = player->jx, jy = player->jy;
9621 boolean field_under_player_is_free =
9622 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9623 boolean player_is_standing_on_valid_field =
9624 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9625 (IS_WALKABLE(Feld[jx][jy]) &&
9626 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9628 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9629 player->programmed_action = MV_DOWN;
9635 -----------------------------------------------------------------------------
9636 dx, dy: direction (non-diagonal) to try to move the player to
9637 real_dx, real_dy: direction as read from input device (can be diagonal)
9640 boolean MovePlayerOneStep(struct PlayerInfo *player,
9641 int dx, int dy, int real_dx, int real_dy)
9644 static int trigger_sides[4][2] =
9646 /* enter side leave side */
9647 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9648 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9649 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9650 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9652 int move_direction = (dx == -1 ? MV_LEFT :
9653 dx == +1 ? MV_RIGHT :
9655 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9656 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9657 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9659 int jx = player->jx, jy = player->jy;
9660 int new_jx = jx + dx, new_jy = jy + dy;
9664 if (!player->active || (!dx && !dy))
9665 return MF_NO_ACTION;
9667 player->MovDir = (dx < 0 ? MV_LEFT :
9670 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9672 if (!IN_LEV_FIELD(new_jx, new_jy))
9673 return MF_NO_ACTION;
9675 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9676 return MF_NO_ACTION;
9679 element = MovingOrBlocked2Element(new_jx, new_jy);
9681 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9684 if (DONT_RUN_INTO(element))
9686 if (element == EL_ACID && dx == 0 && dy == 1)
9688 SplashAcid(new_jx, new_jy);
9689 Feld[jx][jy] = EL_PLAYER_1;
9690 InitMovingField(jx, jy, MV_DOWN);
9691 Store[jx][jy] = EL_ACID;
9692 ContinueMoving(jx, jy);
9696 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9701 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9702 if (can_move != MF_MOVING)
9705 /* check if DigField() has caused relocation of the player */
9706 if (player->jx != jx || player->jy != jy)
9707 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9709 StorePlayer[jx][jy] = 0;
9710 player->last_jx = jx;
9711 player->last_jy = jy;
9712 player->jx = new_jx;
9713 player->jy = new_jy;
9714 StorePlayer[new_jx][new_jy] = player->element_nr;
9717 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9719 player->step_counter++;
9722 player->drop_delay = 0;
9725 PlayerVisit[jx][jy] = FrameCounter;
9727 ScrollPlayer(player, SCROLL_INIT);
9730 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9732 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9734 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9737 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9739 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9740 CE_OTHER_GETS_ENTERED, enter_side);
9741 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9742 CE_ENTERED_BY_PLAYER, enter_side);
9749 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9751 int jx = player->jx, jy = player->jy;
9752 int old_jx = jx, old_jy = jy;
9753 int moved = MF_NO_ACTION;
9756 if (!player->active)
9761 if (player->MovPos == 0)
9763 player->is_moving = FALSE;
9764 player->is_digging = FALSE;
9765 player->is_collecting = FALSE;
9766 player->is_snapping = FALSE;
9767 player->is_pushing = FALSE;
9773 if (!player->active || (!dx && !dy))
9778 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9786 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9787 player->move_delay + player->move_delay_value);
9790 #if USE_NEW_MOVE_DELAY
9791 if (player->move_delay > 0)
9793 if (!FrameReached(&player->move_delay, player->move_delay_value))
9797 printf("::: can NOT move\n");
9803 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9804 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9811 printf("::: COULD move now\n");
9814 #if USE_NEW_MOVE_DELAY
9815 player->move_delay = -1; /* set to "uninitialized" value */
9818 /* store if player is automatically moved to next field */
9819 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9821 /* remove the last programmed player action */
9822 player->programmed_action = 0;
9826 /* should only happen if pre-1.2 tape recordings are played */
9827 /* this is only for backward compatibility */
9829 int original_move_delay_value = player->move_delay_value;
9832 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9836 /* scroll remaining steps with finest movement resolution */
9837 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9839 while (player->MovPos)
9841 ScrollPlayer(player, SCROLL_GO_ON);
9842 ScrollScreen(NULL, SCROLL_GO_ON);
9844 #if USE_NEW_MOVE_DELAY
9845 AdvanceFrameAndPlayerCounters(player->index_nr);
9854 player->move_delay_value = original_move_delay_value;
9857 if (player->last_move_dir & MV_HORIZONTAL)
9859 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9860 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9864 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9865 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9871 if (moved & MF_MOVING && !ScreenMovPos &&
9872 (player == local_player || !options.network))
9874 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9875 int offset = (setup.scroll_delay ? 3 : 0);
9877 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9879 /* actual player has left the screen -- scroll in that direction */
9880 if (jx != old_jx) /* player has moved horizontally */
9881 scroll_x += (jx - old_jx);
9882 else /* player has moved vertically */
9883 scroll_y += (jy - old_jy);
9887 if (jx != old_jx) /* player has moved horizontally */
9889 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9890 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9891 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9893 /* don't scroll over playfield boundaries */
9894 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9895 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9897 /* don't scroll more than one field at a time */
9898 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9900 /* don't scroll against the player's moving direction */
9901 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9902 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9903 scroll_x = old_scroll_x;
9905 else /* player has moved vertically */
9907 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9908 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9909 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9911 /* don't scroll over playfield boundaries */
9912 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9913 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9915 /* don't scroll more than one field at a time */
9916 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9918 /* don't scroll against the player's moving direction */
9919 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9920 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9921 scroll_y = old_scroll_y;
9925 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9927 if (!options.network && !AllPlayersInVisibleScreen())
9929 scroll_x = old_scroll_x;
9930 scroll_y = old_scroll_y;
9934 ScrollScreen(player, SCROLL_INIT);
9935 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9942 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9944 if (!(moved & MF_MOVING) && !player->is_pushing)
9949 player->StepFrame = 0;
9951 if (moved & MF_MOVING)
9954 printf("::: REALLY moves now\n");
9957 if (old_jx != jx && old_jy == jy)
9958 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9959 else if (old_jx == jx && old_jy != jy)
9960 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9962 DrawLevelField(jx, jy); /* for "crumbled sand" */
9964 player->last_move_dir = player->MovDir;
9965 player->is_moving = TRUE;
9967 player->is_snapping = FALSE;
9971 player->is_switching = FALSE;
9974 player->is_dropping = FALSE;
9978 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9981 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9984 int move_direction = player->MovDir;
9986 int enter_side = MV_DIR_OPPOSITE(move_direction);
9987 int leave_side = move_direction;
9989 static int trigger_sides[4][2] =
9991 /* enter side leave side */
9992 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9993 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9994 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9995 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9997 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9998 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10000 int old_element = Feld[old_jx][old_jy];
10001 int new_element = Feld[jx][jy];
10004 /* !!! TEST ONLY !!! */
10005 if (IS_CUSTOM_ELEMENT(old_element))
10006 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10008 player->index_bit, leave_side);
10010 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10011 CE_OTHER_GETS_LEFT,
10012 player->index_bit, leave_side);
10014 if (IS_CUSTOM_ELEMENT(new_element))
10015 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10016 player->index_bit, enter_side);
10018 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10019 CE_OTHER_GETS_ENTERED,
10020 player->index_bit, enter_side);
10030 CheckGravityMovementWhenNotMoving(player);
10033 player->last_move_dir = MV_NO_MOVING;
10035 player->is_moving = FALSE;
10037 #if USE_NEW_MOVE_STYLE
10038 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10039 /* ensure that the player is also allowed to move in the next frame */
10040 /* (currently, the player is forced to wait eight frames before he can try
10043 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10044 player->move_delay = 0; /* allow direct movement in the next frame */
10048 #if USE_NEW_MOVE_DELAY
10049 if (player->move_delay == -1) /* not yet initialized by DigField() */
10050 player->move_delay = player->move_delay_value;
10053 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10055 TestIfHeroTouchesBadThing(jx, jy);
10056 TestIfPlayerTouchesCustomElement(jx, jy);
10059 if (!player->active)
10060 RemoveHero(player);
10065 void ScrollPlayer(struct PlayerInfo *player, int mode)
10067 int jx = player->jx, jy = player->jy;
10068 int last_jx = player->last_jx, last_jy = player->last_jy;
10069 int move_stepsize = TILEX / player->move_delay_value;
10071 if (!player->active || !player->MovPos)
10074 if (mode == SCROLL_INIT)
10076 player->actual_frame_counter = FrameCounter;
10077 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10080 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10082 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10083 player->block_delay);
10086 #if USE_NEW_BLOCK_STYLE
10089 if (player->block_delay <= 0)
10090 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10093 if (player->block_delay > 0 &&
10094 Feld[last_jx][last_jy] == EL_EMPTY)
10096 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10097 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10100 #if USE_NEW_MOVE_STYLE
10101 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10102 player->block_last_field) &&
10103 Feld[last_jx][last_jy] == EL_EMPTY)
10104 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10106 if (Feld[last_jx][last_jy] == EL_EMPTY)
10107 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10112 DrawPlayer(player);
10117 else if (!FrameReached(&player->actual_frame_counter, 1))
10120 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10121 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10123 #if USE_NEW_BLOCK_STYLE
10125 if (!player->block_last_field &&
10126 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10128 RemoveField(last_jx, last_jy);
10130 Feld[last_jx][last_jy] = EL_EMPTY;
10134 /* before DrawPlayer() to draw correct player graphic for this case */
10135 if (player->MovPos == 0)
10136 CheckGravityMovement(player);
10139 DrawPlayer(player); /* needed here only to cleanup last field */
10142 if (player->MovPos == 0) /* player reached destination field */
10145 if (player->move_delay_reset_counter > 0)
10147 player->move_delay_reset_counter--;
10149 if (player->move_delay_reset_counter == 0)
10151 /* continue with normal speed after quickly moving through gate */
10152 HALVE_PLAYER_SPEED(player);
10154 /* be able to make the next move without delay */
10155 player->move_delay = 0;
10159 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10161 /* continue with normal speed after quickly moving through gate */
10162 HALVE_PLAYER_SPEED(player);
10164 /* be able to make the next move without delay */
10165 player->move_delay = 0;
10169 #if USE_NEW_BLOCK_STYLE
10171 if (player->block_last_field &&
10172 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10174 RemoveField(last_jx, last_jy);
10176 Feld[last_jx][last_jy] = EL_EMPTY;
10180 player->last_jx = jx;
10181 player->last_jy = jy;
10183 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10184 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10185 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10187 DrawPlayer(player); /* needed here only to cleanup last field */
10188 RemoveHero(player);
10190 if (local_player->friends_still_needed == 0 ||
10191 IS_SP_ELEMENT(Feld[jx][jy]))
10192 player->LevelSolved = player->GameOver = TRUE;
10196 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10197 /* this breaks one level: "machine", level 000 */
10199 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10202 int move_direction = player->MovDir;
10204 int enter_side = MV_DIR_OPPOSITE(move_direction);
10205 int leave_side = move_direction;
10207 static int trigger_sides[4][2] =
10209 /* enter side leave side */
10210 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10211 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10212 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10213 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10215 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10216 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10218 int old_jx = last_jx;
10219 int old_jy = last_jy;
10220 int old_element = Feld[old_jx][old_jy];
10221 int new_element = Feld[jx][jy];
10224 /* !!! TEST ONLY !!! */
10225 if (IS_CUSTOM_ELEMENT(old_element))
10226 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10228 player->index_bit, leave_side);
10230 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10231 CE_OTHER_GETS_LEFT,
10232 player->index_bit, leave_side);
10234 if (IS_CUSTOM_ELEMENT(new_element))
10235 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10236 player->index_bit, enter_side);
10238 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10239 CE_OTHER_GETS_ENTERED,
10240 player->index_bit, enter_side);
10246 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10248 TestIfHeroTouchesBadThing(jx, jy);
10249 TestIfPlayerTouchesCustomElement(jx, jy);
10252 /* needed because pushed element has not yet reached its destination,
10253 so it would trigger a change event at its previous field location */
10254 if (!player->is_pushing)
10256 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10259 if (!player->active)
10260 RemoveHero(player);
10263 if (level.use_step_counter)
10273 if (TimeLeft <= 10 && setup.time_limit)
10274 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10276 DrawGameValue_Time(TimeLeft);
10278 if (!TimeLeft && setup.time_limit)
10279 for (i = 0; i < MAX_PLAYERS; i++)
10280 KillHero(&stored_player[i]);
10282 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10283 DrawGameValue_Time(TimePlayed);
10286 if (tape.single_step && tape.recording && !tape.pausing &&
10287 !player->programmed_action)
10288 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10292 void ScrollScreen(struct PlayerInfo *player, int mode)
10294 static unsigned long screen_frame_counter = 0;
10296 if (mode == SCROLL_INIT)
10298 /* set scrolling step size according to actual player's moving speed */
10299 ScrollStepSize = TILEX / player->move_delay_value;
10301 screen_frame_counter = FrameCounter;
10302 ScreenMovDir = player->MovDir;
10303 ScreenMovPos = player->MovPos;
10304 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10307 else if (!FrameReached(&screen_frame_counter, 1))
10312 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10313 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10314 redraw_mask |= REDRAW_FIELD;
10317 ScreenMovDir = MV_NO_MOVING;
10320 void TestIfPlayerTouchesCustomElement(int x, int y)
10322 static int xy[4][2] =
10329 static int trigger_sides[4][2] =
10331 /* center side border side */
10332 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10333 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10334 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10335 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10337 static int touch_dir[4] =
10339 MV_LEFT | MV_RIGHT,
10344 int center_element = Feld[x][y]; /* should always be non-moving! */
10347 for (i = 0; i < NUM_DIRECTIONS; i++)
10349 int xx = x + xy[i][0];
10350 int yy = y + xy[i][1];
10351 int center_side = trigger_sides[i][0];
10352 int border_side = trigger_sides[i][1];
10353 int border_element;
10355 if (!IN_LEV_FIELD(xx, yy))
10358 if (IS_PLAYER(x, y))
10360 struct PlayerInfo *player = PLAYERINFO(x, y);
10362 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10363 border_element = Feld[xx][yy]; /* may be moving! */
10364 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10365 border_element = Feld[xx][yy];
10366 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10367 border_element = MovingOrBlocked2Element(xx, yy);
10369 continue; /* center and border element do not touch */
10372 /* !!! TEST ONLY !!! */
10373 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10374 player->index_bit, border_side);
10375 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10376 CE_OTHER_GETS_TOUCHED,
10377 player->index_bit, border_side);
10379 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10380 CE_OTHER_GETS_TOUCHED,
10381 player->index_bit, border_side);
10382 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10383 player->index_bit, border_side);
10386 else if (IS_PLAYER(xx, yy))
10388 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10390 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10392 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10393 continue; /* center and border element do not touch */
10397 /* !!! TEST ONLY !!! */
10398 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10399 player->index_bit, center_side);
10400 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10401 CE_OTHER_GETS_TOUCHED,
10402 player->index_bit, center_side);
10404 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10405 CE_OTHER_GETS_TOUCHED,
10406 player->index_bit, center_side);
10407 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10408 player->index_bit, center_side);
10416 void TestIfElementTouchesCustomElement(int x, int y)
10418 static int xy[4][2] =
10425 static int trigger_sides[4][2] =
10427 /* center side border side */
10428 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10429 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10430 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10431 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10433 static int touch_dir[4] =
10435 MV_LEFT | MV_RIGHT,
10440 boolean change_center_element = FALSE;
10441 int center_element_change_page = 0;
10442 int center_element = Feld[x][y]; /* should always be non-moving! */
10443 int border_trigger_element = EL_UNDEFINED;
10446 for (i = 0; i < NUM_DIRECTIONS; i++)
10448 int xx = x + xy[i][0];
10449 int yy = y + xy[i][1];
10450 int center_side = trigger_sides[i][0];
10451 int border_side = trigger_sides[i][1];
10452 int border_element;
10454 if (!IN_LEV_FIELD(xx, yy))
10457 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10458 border_element = Feld[xx][yy]; /* may be moving! */
10459 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10460 border_element = Feld[xx][yy];
10461 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10462 border_element = MovingOrBlocked2Element(xx, yy);
10464 continue; /* center and border element do not touch */
10466 /* check for change of center element (but change it only once) */
10467 if (IS_CUSTOM_ELEMENT(center_element) &&
10468 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10469 !change_center_element)
10471 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10473 struct ElementChangeInfo *change =
10474 &element_info[center_element].change_page[j];
10476 if (change->can_change &&
10477 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10478 change->trigger_side & border_side &&
10480 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10482 change->trigger_element == border_element
10486 change_center_element = TRUE;
10487 center_element_change_page = j;
10488 border_trigger_element = border_element;
10495 /* check for change of border element */
10496 if (IS_CUSTOM_ELEMENT(border_element) &&
10497 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10499 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10501 struct ElementChangeInfo *change =
10502 &element_info[border_element].change_page[j];
10504 if (change->can_change &&
10505 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10506 change->trigger_side & center_side &&
10508 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10510 change->trigger_element == center_element
10515 printf("::: border_element %d, %d\n", x, y);
10518 CheckElementChangeByPage(xx, yy, border_element, center_element,
10519 CE_OTHER_IS_TOUCHING, j);
10526 if (change_center_element)
10529 printf("::: center_element %d, %d\n", x, y);
10532 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10533 CE_OTHER_IS_TOUCHING, center_element_change_page);
10537 void TestIfElementHitsCustomElement(int x, int y, int direction)
10539 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10540 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10541 int hitx = x + dx, hity = y + dy;
10542 int hitting_element = Feld[x][y];
10543 int touched_element;
10545 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10546 !IS_FREE(hitx, hity) &&
10547 (!IS_MOVING(hitx, hity) ||
10548 MovDir[hitx][hity] != direction ||
10549 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10552 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10556 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10560 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10561 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10563 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10564 CE_HITTING_SOMETHING, direction);
10566 if (IN_LEV_FIELD(hitx, hity))
10568 int opposite_direction = MV_DIR_OPPOSITE(direction);
10569 int hitting_side = direction;
10570 int touched_side = opposite_direction;
10572 int touched_element = MovingOrBlocked2Element(hitx, hity);
10575 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10576 MovDir[hitx][hity] != direction ||
10577 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10586 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10587 CE_HIT_BY_SOMETHING, opposite_direction);
10589 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10590 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10592 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10594 struct ElementChangeInfo *change =
10595 &element_info[hitting_element].change_page[i];
10597 if (change->can_change &&
10598 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10599 change->trigger_side & touched_side &&
10602 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10604 change->trigger_element == touched_element
10608 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10609 CE_OTHER_IS_HITTING, i);
10615 if (IS_CUSTOM_ELEMENT(touched_element) &&
10616 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10618 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10620 struct ElementChangeInfo *change =
10621 &element_info[touched_element].change_page[i];
10623 if (change->can_change &&
10624 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10625 change->trigger_side & hitting_side &&
10627 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10629 change->trigger_element == hitting_element
10633 CheckElementChangeByPage(hitx, hity, touched_element,
10634 hitting_element, CE_OTHER_GETS_HIT, i);
10644 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10646 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10647 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10648 int hitx = x + dx, hity = y + dy;
10649 int hitting_element = Feld[x][y];
10650 int touched_element;
10652 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10653 !IS_FREE(hitx, hity) &&
10654 (!IS_MOVING(hitx, hity) ||
10655 MovDir[hitx][hity] != direction ||
10656 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10659 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10663 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10667 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10668 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10670 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10671 EP_CAN_SMASH_EVERYTHING, direction);
10673 if (IN_LEV_FIELD(hitx, hity))
10675 int opposite_direction = MV_DIR_OPPOSITE(direction);
10676 int hitting_side = direction;
10677 int touched_side = opposite_direction;
10679 int touched_element = MovingOrBlocked2Element(hitx, hity);
10682 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10683 MovDir[hitx][hity] != direction ||
10684 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10693 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10694 CE_SMASHED_BY_SOMETHING, opposite_direction);
10696 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10697 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10699 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10701 struct ElementChangeInfo *change =
10702 &element_info[hitting_element].change_page[i];
10704 if (change->can_change &&
10705 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10706 change->trigger_side & touched_side &&
10709 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10711 change->trigger_element == touched_element
10715 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10716 CE_OTHER_IS_SMASHING, i);
10722 if (IS_CUSTOM_ELEMENT(touched_element) &&
10723 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10725 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10727 struct ElementChangeInfo *change =
10728 &element_info[touched_element].change_page[i];
10730 if (change->can_change &&
10731 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10732 change->trigger_side & hitting_side &&
10734 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10736 change->trigger_element == hitting_element
10740 CheckElementChangeByPage(hitx, hity, touched_element,
10741 hitting_element, CE_OTHER_GETS_SMASHED,i);
10751 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10753 int i, kill_x = -1, kill_y = -1;
10754 int bad_element = -1;
10755 static int test_xy[4][2] =
10762 static int test_dir[4] =
10770 for (i = 0; i < NUM_DIRECTIONS; i++)
10772 int test_x, test_y, test_move_dir, test_element;
10774 test_x = good_x + test_xy[i][0];
10775 test_y = good_y + test_xy[i][1];
10777 if (!IN_LEV_FIELD(test_x, test_y))
10781 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10784 test_element = Feld[test_x][test_y];
10786 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10789 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10790 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10792 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10793 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10797 bad_element = test_element;
10803 if (kill_x != -1 || kill_y != -1)
10805 if (IS_PLAYER(good_x, good_y))
10807 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10810 if (player->shield_deadly_time_left > 0 &&
10811 !IS_INDESTRUCTIBLE(bad_element))
10812 Bang(kill_x, kill_y);
10813 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10816 if (player->shield_deadly_time_left > 0)
10817 Bang(kill_x, kill_y);
10818 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10823 Bang(good_x, good_y);
10827 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10829 int i, kill_x = -1, kill_y = -1;
10830 int bad_element = Feld[bad_x][bad_y];
10831 static int test_xy[4][2] =
10838 static int touch_dir[4] =
10840 MV_LEFT | MV_RIGHT,
10845 static int test_dir[4] =
10853 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10856 for (i = 0; i < NUM_DIRECTIONS; i++)
10858 int test_x, test_y, test_move_dir, test_element;
10860 test_x = bad_x + test_xy[i][0];
10861 test_y = bad_y + test_xy[i][1];
10862 if (!IN_LEV_FIELD(test_x, test_y))
10866 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10868 test_element = Feld[test_x][test_y];
10870 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10871 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10873 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10874 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10876 /* good thing is player or penguin that does not move away */
10877 if (IS_PLAYER(test_x, test_y))
10879 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10881 if (bad_element == EL_ROBOT && player->is_moving)
10882 continue; /* robot does not kill player if he is moving */
10884 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10886 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10887 continue; /* center and border element do not touch */
10894 else if (test_element == EL_PENGUIN)
10903 if (kill_x != -1 || kill_y != -1)
10905 if (IS_PLAYER(kill_x, kill_y))
10907 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10910 if (player->shield_deadly_time_left > 0 &&
10911 !IS_INDESTRUCTIBLE(bad_element))
10912 Bang(bad_x, bad_y);
10913 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10916 if (player->shield_deadly_time_left > 0)
10917 Bang(bad_x, bad_y);
10918 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10923 Bang(kill_x, kill_y);
10927 void TestIfHeroTouchesBadThing(int x, int y)
10929 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10932 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10934 TestIfGoodThingHitsBadThing(x, y, move_dir);
10937 void TestIfBadThingTouchesHero(int x, int y)
10939 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10942 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10944 TestIfBadThingHitsGoodThing(x, y, move_dir);
10947 void TestIfFriendTouchesBadThing(int x, int y)
10949 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10952 void TestIfBadThingTouchesFriend(int x, int y)
10954 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10957 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10959 int i, kill_x = bad_x, kill_y = bad_y;
10960 static int xy[4][2] =
10968 for (i = 0; i < NUM_DIRECTIONS; i++)
10972 x = bad_x + xy[i][0];
10973 y = bad_y + xy[i][1];
10974 if (!IN_LEV_FIELD(x, y))
10977 element = Feld[x][y];
10978 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10979 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10987 if (kill_x != bad_x || kill_y != bad_y)
10988 Bang(bad_x, bad_y);
10991 void KillHero(struct PlayerInfo *player)
10993 int jx = player->jx, jy = player->jy;
10995 if (!player->active)
10998 /* remove accessible field at the player's position */
10999 Feld[jx][jy] = EL_EMPTY;
11001 /* deactivate shield (else Bang()/Explode() would not work right) */
11002 player->shield_normal_time_left = 0;
11003 player->shield_deadly_time_left = 0;
11009 static void KillHeroUnlessEnemyProtected(int x, int y)
11011 if (!PLAYER_ENEMY_PROTECTED(x, y))
11012 KillHero(PLAYERINFO(x, y));
11015 static void KillHeroUnlessExplosionProtected(int x, int y)
11017 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11018 KillHero(PLAYERINFO(x, y));
11021 void BuryHero(struct PlayerInfo *player)
11023 int jx = player->jx, jy = player->jy;
11025 if (!player->active)
11029 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11031 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11033 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11035 player->GameOver = TRUE;
11036 RemoveHero(player);
11039 void RemoveHero(struct PlayerInfo *player)
11041 int jx = player->jx, jy = player->jy;
11042 int i, found = FALSE;
11044 player->present = FALSE;
11045 player->active = FALSE;
11047 if (!ExplodeField[jx][jy])
11048 StorePlayer[jx][jy] = 0;
11050 for (i = 0; i < MAX_PLAYERS; i++)
11051 if (stored_player[i].active)
11055 AllPlayersGone = TRUE;
11062 =============================================================================
11063 checkDiagonalPushing()
11064 -----------------------------------------------------------------------------
11065 check if diagonal input device direction results in pushing of object
11066 (by checking if the alternative direction is walkable, diggable, ...)
11067 =============================================================================
11070 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11071 int x, int y, int real_dx, int real_dy)
11073 int jx, jy, dx, dy, xx, yy;
11075 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11078 /* diagonal direction: check alternative direction */
11083 xx = jx + (dx == 0 ? real_dx : 0);
11084 yy = jy + (dy == 0 ? real_dy : 0);
11086 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11090 =============================================================================
11092 -----------------------------------------------------------------------------
11093 x, y: field next to player (non-diagonal) to try to dig to
11094 real_dx, real_dy: direction as read from input device (can be diagonal)
11095 =============================================================================
11098 int DigField(struct PlayerInfo *player,
11099 int oldx, int oldy, int x, int y,
11100 int real_dx, int real_dy, int mode)
11103 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11105 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11106 boolean player_was_pushing = player->is_pushing;
11107 int jx = oldx, jy = oldy;
11108 int dx = x - jx, dy = y - jy;
11109 int nextx = x + dx, nexty = y + dy;
11110 int move_direction = (dx == -1 ? MV_LEFT :
11111 dx == +1 ? MV_RIGHT :
11113 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11114 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11116 int dig_side = MV_DIR_OPPOSITE(move_direction);
11118 static int trigger_sides[4] =
11120 CH_SIDE_RIGHT, /* moving left */
11121 CH_SIDE_LEFT, /* moving right */
11122 CH_SIDE_BOTTOM, /* moving up */
11123 CH_SIDE_TOP, /* moving down */
11125 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11127 int old_element = Feld[jx][jy];
11130 if (is_player) /* function can also be called by EL_PENGUIN */
11132 if (player->MovPos == 0)
11134 player->is_digging = FALSE;
11135 player->is_collecting = FALSE;
11138 if (player->MovPos == 0) /* last pushing move finished */
11139 player->is_pushing = FALSE;
11141 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11143 player->is_switching = FALSE;
11144 #if USE_NEW_PUSH_DELAY
11145 player->push_delay = -1;
11147 player->push_delay = 0;
11150 return MF_NO_ACTION;
11154 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11155 return MF_NO_ACTION;
11160 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11162 if (IS_TUBE(Feld[jx][jy]) ||
11163 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11167 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11168 int tube_leave_directions[][2] =
11170 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11171 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11172 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11173 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11174 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11175 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11176 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11177 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11178 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11179 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11180 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11181 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11184 while (tube_leave_directions[i][0] != tube_element)
11187 if (tube_leave_directions[i][0] == -1) /* should not happen */
11191 if (!(tube_leave_directions[i][1] & move_direction))
11192 return MF_NO_ACTION; /* tube has no opening in this direction */
11197 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11198 old_element = Back[jx][jy];
11202 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11203 return MF_NO_ACTION; /* field has no opening in this direction */
11205 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11206 return MF_NO_ACTION; /* field has no opening in this direction */
11208 element = Feld[x][y];
11210 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11211 return MF_NO_ACTION;
11213 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11214 game.engine_version >= VERSION_IDENT(2,2,0,0))
11215 return MF_NO_ACTION;
11218 if (game.gravity && is_player && !player->is_auto_moving &&
11219 canFallDown(player) && move_direction != MV_DOWN &&
11220 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11221 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11225 if (element == EL_EMPTY_SPACE &&
11226 game.gravity && !player->is_auto_moving &&
11227 canFallDown(player) && move_direction != MV_DOWN)
11228 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11234 case EL_SP_PORT_LEFT:
11235 case EL_SP_PORT_RIGHT:
11236 case EL_SP_PORT_UP:
11237 case EL_SP_PORT_DOWN:
11238 case EL_SP_PORT_HORIZONTAL:
11239 case EL_SP_PORT_VERTICAL:
11240 case EL_SP_PORT_ANY:
11241 case EL_SP_GRAVITY_PORT_LEFT:
11242 case EL_SP_GRAVITY_PORT_RIGHT:
11243 case EL_SP_GRAVITY_PORT_UP:
11244 case EL_SP_GRAVITY_PORT_DOWN:
11246 if (!canEnterSupaplexPort(x, y, dx, dy))
11247 return MF_NO_ACTION;
11250 element != EL_SP_PORT_LEFT &&
11251 element != EL_SP_GRAVITY_PORT_LEFT &&
11252 element != EL_SP_PORT_HORIZONTAL &&
11253 element != EL_SP_PORT_ANY) ||
11255 element != EL_SP_PORT_RIGHT &&
11256 element != EL_SP_GRAVITY_PORT_RIGHT &&
11257 element != EL_SP_PORT_HORIZONTAL &&
11258 element != EL_SP_PORT_ANY) ||
11260 element != EL_SP_PORT_UP &&
11261 element != EL_SP_GRAVITY_PORT_UP &&
11262 element != EL_SP_PORT_VERTICAL &&
11263 element != EL_SP_PORT_ANY) ||
11265 element != EL_SP_PORT_DOWN &&
11266 element != EL_SP_GRAVITY_PORT_DOWN &&
11267 element != EL_SP_PORT_VERTICAL &&
11268 element != EL_SP_PORT_ANY) ||
11269 !IN_LEV_FIELD(nextx, nexty) ||
11270 !IS_FREE(nextx, nexty))
11271 return MF_NO_ACTION;
11274 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11275 element == EL_SP_GRAVITY_PORT_RIGHT ||
11276 element == EL_SP_GRAVITY_PORT_UP ||
11277 element == EL_SP_GRAVITY_PORT_DOWN)
11278 game.gravity = !game.gravity;
11280 /* automatically move to the next field with double speed */
11281 player->programmed_action = move_direction;
11283 if (player->move_delay_reset_counter == 0)
11285 player->move_delay_reset_counter = 2; /* two double speed steps */
11287 DOUBLE_PLAYER_SPEED(player);
11290 player->move_delay_reset_counter = 2;
11292 DOUBLE_PLAYER_SPEED(player);
11296 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11299 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11305 case EL_TUBE_VERTICAL:
11306 case EL_TUBE_HORIZONTAL:
11307 case EL_TUBE_VERTICAL_LEFT:
11308 case EL_TUBE_VERTICAL_RIGHT:
11309 case EL_TUBE_HORIZONTAL_UP:
11310 case EL_TUBE_HORIZONTAL_DOWN:
11311 case EL_TUBE_LEFT_UP:
11312 case EL_TUBE_LEFT_DOWN:
11313 case EL_TUBE_RIGHT_UP:
11314 case EL_TUBE_RIGHT_DOWN:
11317 int tube_enter_directions[][2] =
11319 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11320 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11321 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11322 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11323 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11324 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11325 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11326 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11327 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11328 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11329 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11330 { -1, MV_NO_MOVING }
11333 while (tube_enter_directions[i][0] != element)
11336 if (tube_enter_directions[i][0] == -1) /* should not happen */
11340 if (!(tube_enter_directions[i][1] & move_direction))
11341 return MF_NO_ACTION; /* tube has no opening in this direction */
11343 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11351 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11353 if (IS_WALKABLE(element))
11356 int sound_element = SND_ELEMENT(element);
11357 int sound_action = ACTION_WALKING;
11360 if (!ACCESS_FROM(element, opposite_direction))
11361 return MF_NO_ACTION; /* field not accessible from this direction */
11365 if (element == EL_EMPTY_SPACE &&
11366 game.gravity && !player->is_auto_moving &&
11367 canFallDown(player) && move_direction != MV_DOWN)
11368 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11371 if (IS_GATE(element))
11373 if (!player->key[element - EL_GATE_1])
11374 return MF_NO_ACTION;
11376 else if (IS_GATE_GRAY(element))
11378 if (!player->key[element - EL_GATE_1_GRAY])
11379 return MF_NO_ACTION;
11381 else if (element == EL_EXIT_OPEN ||
11382 element == EL_SP_EXIT_OPEN ||
11383 element == EL_SP_EXIT_OPENING)
11385 sound_action = ACTION_PASSING; /* player is passing exit */
11387 else if (element == EL_EMPTY)
11389 sound_action = ACTION_MOVING; /* nothing to walk on */
11392 /* play sound from background or player, whatever is available */
11393 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11394 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11396 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11401 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11403 else if (IS_PASSABLE(element))
11407 if (!canPassField(x, y, move_direction))
11408 return MF_NO_ACTION;
11413 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11414 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11415 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11416 return MF_NO_ACTION;
11418 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11419 return MF_NO_ACTION;
11424 if (!ACCESS_FROM(element, opposite_direction))
11425 return MF_NO_ACTION; /* field not accessible from this direction */
11427 if (IS_CUSTOM_ELEMENT(element) &&
11428 !ACCESS_FROM(element, opposite_direction))
11429 return MF_NO_ACTION; /* field not accessible from this direction */
11433 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11434 return MF_NO_ACTION;
11439 if (IS_EM_GATE(element))
11441 if (!player->key[element - EL_EM_GATE_1])
11442 return MF_NO_ACTION;
11444 else if (IS_EM_GATE_GRAY(element))
11446 if (!player->key[element - EL_EM_GATE_1_GRAY])
11447 return MF_NO_ACTION;
11449 else if (IS_SP_PORT(element))
11451 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11452 element == EL_SP_GRAVITY_PORT_RIGHT ||
11453 element == EL_SP_GRAVITY_PORT_UP ||
11454 element == EL_SP_GRAVITY_PORT_DOWN)
11455 game.gravity = !game.gravity;
11456 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11457 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11458 element == EL_SP_GRAVITY_ON_PORT_UP ||
11459 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11460 game.gravity = TRUE;
11461 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11462 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11463 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11464 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11465 game.gravity = FALSE;
11468 /* automatically move to the next field with double speed */
11469 player->programmed_action = move_direction;
11471 if (player->move_delay_reset_counter == 0)
11473 player->move_delay_reset_counter = 2; /* two double speed steps */
11475 DOUBLE_PLAYER_SPEED(player);
11478 player->move_delay_reset_counter = 2;
11480 DOUBLE_PLAYER_SPEED(player);
11483 PlayLevelSoundAction(x, y, ACTION_PASSING);
11487 else if (IS_DIGGABLE(element))
11491 if (mode != DF_SNAP)
11494 GfxElement[x][y] = GFX_ELEMENT(element);
11497 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11499 player->is_digging = TRUE;
11502 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11504 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11505 player->index_bit, dig_side);
11508 if (mode == DF_SNAP)
11509 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11514 else if (IS_COLLECTIBLE(element))
11518 if (is_player && mode != DF_SNAP)
11520 GfxElement[x][y] = element;
11521 player->is_collecting = TRUE;
11524 if (element == EL_SPEED_PILL)
11525 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11526 else if (element == EL_EXTRA_TIME && level.time > 0)
11529 DrawGameValue_Time(TimeLeft);
11531 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11533 player->shield_normal_time_left += 10;
11534 if (element == EL_SHIELD_DEADLY)
11535 player->shield_deadly_time_left += 10;
11537 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11539 if (player->inventory_size < MAX_INVENTORY_SIZE)
11540 player->inventory_element[player->inventory_size++] = element;
11542 DrawGameValue_Dynamite(local_player->inventory_size);
11544 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11546 player->dynabomb_count++;
11547 player->dynabombs_left++;
11549 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11551 player->dynabomb_size++;
11553 else if (element == EL_DYNABOMB_INCREASE_POWER)
11555 player->dynabomb_xl = TRUE;
11557 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11558 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11560 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11561 element - EL_KEY_1 : element - EL_EM_KEY_1);
11563 player->key[key_nr] = TRUE;
11565 DrawGameValue_Keys(player->key);
11567 redraw_mask |= REDRAW_DOOR_1;
11569 else if (IS_ENVELOPE(element))
11572 player->show_envelope = element;
11574 ShowEnvelope(element - EL_ENVELOPE_1);
11577 else if (IS_DROPPABLE(element) ||
11578 IS_THROWABLE(element)) /* can be collected and dropped */
11582 if (element_info[element].collect_count == 0)
11583 player->inventory_infinite_element = element;
11585 for (i = 0; i < element_info[element].collect_count; i++)
11586 if (player->inventory_size < MAX_INVENTORY_SIZE)
11587 player->inventory_element[player->inventory_size++] = element;
11589 DrawGameValue_Dynamite(local_player->inventory_size);
11591 else if (element_info[element].collect_count > 0)
11593 local_player->gems_still_needed -=
11594 element_info[element].collect_count;
11595 if (local_player->gems_still_needed < 0)
11596 local_player->gems_still_needed = 0;
11598 DrawGameValue_Emeralds(local_player->gems_still_needed);
11601 RaiseScoreElement(element);
11602 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11605 CheckTriggeredElementChangeByPlayer(x, y, element,
11606 CE_OTHER_GETS_COLLECTED,
11607 player->index_bit, dig_side);
11610 if (mode == DF_SNAP)
11611 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11616 else if (IS_PUSHABLE(element))
11618 if (mode == DF_SNAP && element != EL_BD_ROCK)
11619 return MF_NO_ACTION;
11621 if (CAN_FALL(element) && dy)
11622 return MF_NO_ACTION;
11624 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11625 !(element == EL_SPRING && level.use_spring_bug))
11626 return MF_NO_ACTION;
11629 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11630 ((move_direction & MV_VERTICAL &&
11631 ((element_info[element].move_pattern & MV_LEFT &&
11632 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11633 (element_info[element].move_pattern & MV_RIGHT &&
11634 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11635 (move_direction & MV_HORIZONTAL &&
11636 ((element_info[element].move_pattern & MV_UP &&
11637 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11638 (element_info[element].move_pattern & MV_DOWN &&
11639 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11640 return MF_NO_ACTION;
11644 /* do not push elements already moving away faster than player */
11645 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11646 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11647 return MF_NO_ACTION;
11649 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11650 return MF_NO_ACTION;
11656 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11658 if (player->push_delay_value == -1 || !player_was_pushing)
11659 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11661 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11663 if (player->push_delay_value == -1)
11664 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11667 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11669 if (player->push_delay_value == -1 || !player_was_pushing)
11670 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11673 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11675 if (!player->is_pushing)
11676 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11680 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11681 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11682 !player_is_pushing))
11683 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11686 if (!player->is_pushing &&
11687 game.engine_version >= VERSION_IDENT(2,2,0,7))
11688 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11692 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11693 player->push_delay, player->push_delay_value,
11694 FrameCounter, game.engine_version,
11695 player_was_pushing, player->is_pushing,
11696 element, element_info[element].token_name,
11697 GET_NEW_PUSH_DELAY(element));
11700 player->is_pushing = TRUE;
11702 if (!(IN_LEV_FIELD(nextx, nexty) &&
11703 (IS_FREE(nextx, nexty) ||
11704 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11705 IS_SB_ELEMENT(element)))))
11706 return MF_NO_ACTION;
11708 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11709 return MF_NO_ACTION;
11711 #if USE_NEW_PUSH_DELAY
11714 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11715 printf("::: ALERT: %d, %d [%d / %d]\n",
11716 player->push_delay, player->push_delay2,
11717 FrameCounter, FrameCounter / 50);
11720 if (player->push_delay == -1) /* new pushing; restart delay */
11721 player->push_delay = 0;
11723 if (player->push_delay == 0) /* new pushing; restart delay */
11724 player->push_delay = FrameCounter;
11727 #if USE_NEW_PUSH_DELAY
11729 if ( (player->push_delay > 0) != (!xxx_fr) )
11730 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11731 player->push_delay,
11732 xxx_pdv2, player->push_delay2, player->push_delay_value,
11733 FrameCounter, FrameCounter / 50);
11737 if (player->push_delay > 0 &&
11738 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11739 element != EL_SPRING && element != EL_BALLOON)
11742 if (player->push_delay < player->push_delay_value &&
11743 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11744 element != EL_SPRING && element != EL_BALLOON)
11748 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11749 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11750 element != EL_SPRING && element != EL_BALLOON)
11753 /* make sure that there is no move delay before next try to push */
11754 #if USE_NEW_MOVE_DELAY
11755 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11756 player->move_delay = 0;
11758 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11759 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11762 return MF_NO_ACTION;
11766 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11769 if (IS_SB_ELEMENT(element))
11771 if (element == EL_SOKOBAN_FIELD_FULL)
11773 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11774 local_player->sokobanfields_still_needed++;
11777 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11779 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11780 local_player->sokobanfields_still_needed--;
11783 Feld[x][y] = EL_SOKOBAN_OBJECT;
11785 if (Back[x][y] == Back[nextx][nexty])
11786 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11787 else if (Back[x][y] != 0)
11788 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11791 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11794 if (local_player->sokobanfields_still_needed == 0 &&
11795 game.emulation == EMU_SOKOBAN)
11797 player->LevelSolved = player->GameOver = TRUE;
11798 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11802 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11804 InitMovingField(x, y, move_direction);
11805 GfxAction[x][y] = ACTION_PUSHING;
11807 if (mode == DF_SNAP)
11808 ContinueMoving(x, y);
11810 MovPos[x][y] = (dx != 0 ? dx : dy);
11812 Pushed[x][y] = TRUE;
11813 Pushed[nextx][nexty] = TRUE;
11815 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11816 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11818 player->push_delay_value = -1; /* get new value later */
11820 #if USE_PUSH_BUGFIX
11821 /* now: check for element change _after_ element has been pushed! */
11823 if (game.use_bug_change_when_pushing)
11825 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11828 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11829 player->index_bit, dig_side);
11830 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11831 player->index_bit, dig_side);
11837 /* check for element change _after_ element has been pushed! */
11841 /* !!! TEST ONLY !!! */
11842 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11843 player->index_bit, dig_side);
11844 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11845 player->index_bit, dig_side);
11847 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11848 player->index_bit, dig_side);
11849 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11850 player->index_bit, dig_side);
11858 else if (IS_SWITCHABLE(element))
11860 if (PLAYER_SWITCHING(player, x, y))
11862 CheckTriggeredElementChangeByPlayer(x,y, element,
11863 CE_OTHER_GETS_PRESSED,
11864 player->index_bit, dig_side);
11869 player->is_switching = TRUE;
11870 player->switch_x = x;
11871 player->switch_y = y;
11873 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11875 if (element == EL_ROBOT_WHEEL)
11877 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11881 DrawLevelField(x, y);
11883 else if (element == EL_SP_TERMINAL)
11887 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11889 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11891 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11892 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11895 else if (IS_BELT_SWITCH(element))
11897 ToggleBeltSwitch(x, y);
11899 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11900 element == EL_SWITCHGATE_SWITCH_DOWN)
11902 ToggleSwitchgateSwitch(x, y);
11904 else if (element == EL_LIGHT_SWITCH ||
11905 element == EL_LIGHT_SWITCH_ACTIVE)
11907 ToggleLightSwitch(x, y);
11910 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11911 SND_LIGHT_SWITCH_ACTIVATING :
11912 SND_LIGHT_SWITCH_DEACTIVATING);
11915 else if (element == EL_TIMEGATE_SWITCH)
11917 ActivateTimegateSwitch(x, y);
11919 else if (element == EL_BALLOON_SWITCH_LEFT ||
11920 element == EL_BALLOON_SWITCH_RIGHT ||
11921 element == EL_BALLOON_SWITCH_UP ||
11922 element == EL_BALLOON_SWITCH_DOWN ||
11923 element == EL_BALLOON_SWITCH_ANY)
11925 if (element == EL_BALLOON_SWITCH_ANY)
11926 game.balloon_dir = move_direction;
11928 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11929 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11930 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11931 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11934 else if (element == EL_LAMP)
11936 Feld[x][y] = EL_LAMP_ACTIVE;
11937 local_player->lights_still_needed--;
11939 DrawLevelField(x, y);
11941 else if (element == EL_TIME_ORB_FULL)
11943 Feld[x][y] = EL_TIME_ORB_EMPTY;
11945 DrawGameValue_Time(TimeLeft);
11947 DrawLevelField(x, y);
11950 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11954 CheckTriggeredElementChangeByPlayer(x, y, element,
11955 CE_OTHER_IS_SWITCHING,
11956 player->index_bit, dig_side);
11958 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11959 player->index_bit, dig_side);
11965 if (!PLAYER_SWITCHING(player, x, y))
11967 player->is_switching = TRUE;
11968 player->switch_x = x;
11969 player->switch_y = y;
11972 /* !!! TEST ONLY !!! */
11973 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11974 player->index_bit, dig_side);
11975 CheckTriggeredElementChangeByPlayer(x, y, element,
11976 CE_OTHER_IS_SWITCHING,
11977 player->index_bit, dig_side);
11979 CheckTriggeredElementChangeByPlayer(x, y, element,
11980 CE_OTHER_IS_SWITCHING,
11981 player->index_bit, dig_side);
11982 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11983 player->index_bit, dig_side);
11988 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11989 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11990 player->index_bit, dig_side);
11991 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11992 player->index_bit, dig_side);
11994 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11995 player->index_bit, dig_side);
11996 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11997 player->index_bit, dig_side);
12001 return MF_NO_ACTION;
12004 #if USE_NEW_PUSH_DELAY
12005 player->push_delay = -1;
12007 player->push_delay = 0;
12010 if (Feld[x][y] != element) /* really digged/collected something */
12011 player->is_collecting = !player->is_digging;
12016 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12018 int jx = player->jx, jy = player->jy;
12019 int x = jx + dx, y = jy + dy;
12020 int snap_direction = (dx == -1 ? MV_LEFT :
12021 dx == +1 ? MV_RIGHT :
12023 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12026 if (player->MovPos != 0)
12029 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12033 if (!player->active || !IN_LEV_FIELD(x, y))
12041 if (player->MovPos == 0)
12042 player->is_pushing = FALSE;
12044 player->is_snapping = FALSE;
12046 if (player->MovPos == 0)
12048 player->is_moving = FALSE;
12049 player->is_digging = FALSE;
12050 player->is_collecting = FALSE;
12056 if (player->is_snapping)
12059 player->MovDir = snap_direction;
12062 if (player->MovPos == 0)
12065 player->is_moving = FALSE;
12066 player->is_digging = FALSE;
12067 player->is_collecting = FALSE;
12070 player->is_dropping = FALSE;
12072 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12075 player->is_snapping = TRUE;
12078 if (player->MovPos == 0)
12081 player->is_moving = FALSE;
12082 player->is_digging = FALSE;
12083 player->is_collecting = FALSE;
12087 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12088 DrawLevelField(player->last_jx, player->last_jy);
12091 DrawLevelField(x, y);
12100 boolean DropElement(struct PlayerInfo *player)
12102 int old_element, new_element;
12103 int dropx = player->jx, dropy = player->jy;
12104 int drop_direction = player->MovDir;
12106 int drop_side = drop_direction;
12108 static int trigger_sides[4] =
12110 CH_SIDE_LEFT, /* dropping left */
12111 CH_SIDE_RIGHT, /* dropping right */
12112 CH_SIDE_TOP, /* dropping up */
12113 CH_SIDE_BOTTOM, /* dropping down */
12115 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12117 int drop_element = (player->inventory_size > 0 ?
12118 player->inventory_element[player->inventory_size - 1] :
12119 player->inventory_infinite_element != EL_UNDEFINED ?
12120 player->inventory_infinite_element :
12121 player->dynabombs_left > 0 ?
12122 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12125 if (IS_THROWABLE(drop_element))
12127 dropx += GET_DX_FROM_DIR(drop_direction);
12128 dropy += GET_DY_FROM_DIR(drop_direction);
12130 if (!IN_LEV_FIELD(dropx, dropy))
12134 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12135 new_element = drop_element; /* default: no change when dropping */
12137 /* check if player is active, not moving and ready to drop */
12138 if (!player->active || player->MovPos || player->drop_delay > 0)
12141 /* check if player has anything that can be dropped */
12143 if (new_element == EL_UNDEFINED)
12146 if (player->inventory_size == 0 &&
12147 player->inventory_infinite_element == EL_UNDEFINED &&
12148 player->dynabombs_left == 0)
12152 /* check if anything can be dropped at the current position */
12153 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12156 /* collected custom elements can only be dropped on empty fields */
12158 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12161 if (player->inventory_size > 0 &&
12162 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12163 && old_element != EL_EMPTY)
12167 if (old_element != EL_EMPTY)
12168 Back[dropx][dropy] = old_element; /* store old element on this field */
12170 ResetGfxAnimation(dropx, dropy);
12171 ResetRandomAnimationValue(dropx, dropy);
12173 if (player->inventory_size > 0 ||
12174 player->inventory_infinite_element != EL_UNDEFINED)
12176 if (player->inventory_size > 0)
12178 player->inventory_size--;
12181 new_element = player->inventory_element[player->inventory_size];
12184 DrawGameValue_Dynamite(local_player->inventory_size);
12186 if (new_element == EL_DYNAMITE)
12187 new_element = EL_DYNAMITE_ACTIVE;
12188 else if (new_element == EL_SP_DISK_RED)
12189 new_element = EL_SP_DISK_RED_ACTIVE;
12192 Feld[dropx][dropy] = new_element;
12194 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12195 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12196 el2img(Feld[dropx][dropy]), 0);
12198 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12201 /* needed if previous element just changed to "empty" in the last frame */
12202 Changed[dropx][dropy] = 0; /* allow another change */
12206 /* !!! TEST ONLY !!! */
12207 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12208 player->index_bit, drop_side);
12209 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12210 CE_OTHER_GETS_DROPPED,
12211 player->index_bit, drop_side);
12213 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12214 CE_OTHER_GETS_DROPPED,
12215 player->index_bit, drop_side);
12216 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12217 player->index_bit, drop_side);
12220 TestIfElementTouchesCustomElement(dropx, dropy);
12222 else /* player is dropping a dyna bomb */
12224 player->dynabombs_left--;
12227 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12230 Feld[dropx][dropy] = new_element;
12232 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12233 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12234 el2img(Feld[dropx][dropy]), 0);
12236 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12243 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12246 InitField_WithBug1(dropx, dropy, FALSE);
12248 InitField(dropx, dropy, FALSE);
12249 if (CAN_MOVE(Feld[dropx][dropy]))
12250 InitMovDir(dropx, dropy);
12254 new_element = Feld[dropx][dropy]; /* element might have changed */
12256 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12257 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12260 int move_stepsize = element_info[new_element].move_stepsize;
12262 int move_direction, nextx, nexty;
12264 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12265 MovDir[dropx][dropy] = drop_direction;
12267 move_direction = MovDir[dropx][dropy];
12268 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12269 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12272 Changed[dropx][dropy] = 0; /* allow another change */
12273 CheckCollision[dropx][dropy] = 2;
12276 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12279 WasJustMoving[dropx][dropy] = 3;
12282 InitMovingField(dropx, dropy, move_direction);
12283 ContinueMoving(dropx, dropy);
12288 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12291 Changed[dropx][dropy] = 0; /* allow another change */
12294 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12296 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12297 CE_HITTING_SOMETHING, move_direction);
12305 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12310 player->drop_delay = 8 + 8 + 8;
12314 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12319 player->is_dropping = TRUE;
12325 /* ------------------------------------------------------------------------- */
12326 /* game sound playing functions */
12327 /* ------------------------------------------------------------------------- */
12329 static int *loop_sound_frame = NULL;
12330 static int *loop_sound_volume = NULL;
12332 void InitPlayLevelSound()
12334 int num_sounds = getSoundListSize();
12336 checked_free(loop_sound_frame);
12337 checked_free(loop_sound_volume);
12339 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12340 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12343 static void PlayLevelSound(int x, int y, int nr)
12345 int sx = SCREENX(x), sy = SCREENY(y);
12346 int volume, stereo_position;
12347 int max_distance = 8;
12348 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12350 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12351 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12354 if (!IN_LEV_FIELD(x, y) ||
12355 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12356 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12359 volume = SOUND_MAX_VOLUME;
12361 if (!IN_SCR_FIELD(sx, sy))
12363 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12364 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12366 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12369 stereo_position = (SOUND_MAX_LEFT +
12370 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12371 (SCR_FIELDX + 2 * max_distance));
12373 if (IS_LOOP_SOUND(nr))
12375 /* This assures that quieter loop sounds do not overwrite louder ones,
12376 while restarting sound volume comparison with each new game frame. */
12378 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12381 loop_sound_volume[nr] = volume;
12382 loop_sound_frame[nr] = FrameCounter;
12385 PlaySoundExt(nr, volume, stereo_position, type);
12388 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12390 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12391 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12392 y < LEVELY(BY1) ? LEVELY(BY1) :
12393 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12397 static void PlayLevelSoundAction(int x, int y, int action)
12399 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12402 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12404 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12406 if (sound_effect != SND_UNDEFINED)
12407 PlayLevelSound(x, y, sound_effect);
12410 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12413 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12415 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12416 PlayLevelSound(x, y, sound_effect);
12419 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12421 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12423 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12424 PlayLevelSound(x, y, sound_effect);
12427 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12429 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12431 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12432 StopSound(sound_effect);
12435 static void PlayLevelMusic()
12437 if (levelset.music[level_nr] != MUS_UNDEFINED)
12438 PlayMusic(levelset.music[level_nr]); /* from config file */
12440 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12443 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12445 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12448 if (sample == SAMPLE_bug)
12449 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12455 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12459 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12463 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12467 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12471 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12475 PlayLevelSoundElementAction(x, y, EL_BUG, ACTION_MOVING);
12479 PlayLevelSoundElementAction(x, y, EL_SPACESHIP, ACTION_MOVING);
12482 case SAMPLE_android:
12483 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12486 case SAMPLE_spring:
12487 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12491 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12495 PlayLevelSoundElementAction(x, y, EL_YAMYAM, ACTION_WAITING);
12499 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12502 case SAMPLE_collect:
12503 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12506 case SAMPLE_diamond:
12507 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12510 case SAMPLE_squash:
12511 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12514 case SAMPLE_wonderfall:
12515 PlayLevelSoundElementAction(x, y, EL_MAGIC_WALL, ACTION_FILLING);
12519 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12523 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12527 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12531 PlayLevelSound(x, y, SND_ACID_SPLASHING);
12535 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12539 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12542 case SAMPLE_wonder:
12543 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12547 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12551 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12554 case SAMPLE_dynamite:
12555 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12559 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12563 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12567 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
12571 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12575 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12579 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12583 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12588 void RaiseScore(int value)
12590 local_player->score += value;
12592 DrawGameValue_Score(local_player->score);
12595 void RaiseScoreElement(int element)
12600 case EL_BD_DIAMOND:
12601 case EL_EMERALD_YELLOW:
12602 case EL_EMERALD_RED:
12603 case EL_EMERALD_PURPLE:
12604 case EL_SP_INFOTRON:
12605 RaiseScore(level.score[SC_EMERALD]);
12608 RaiseScore(level.score[SC_DIAMOND]);
12611 RaiseScore(level.score[SC_CRYSTAL]);
12614 RaiseScore(level.score[SC_PEARL]);
12617 case EL_BD_BUTTERFLY:
12618 case EL_SP_ELECTRON:
12619 RaiseScore(level.score[SC_BUG]);
12622 case EL_BD_FIREFLY:
12623 case EL_SP_SNIKSNAK:
12624 RaiseScore(level.score[SC_SPACESHIP]);
12627 case EL_DARK_YAMYAM:
12628 RaiseScore(level.score[SC_YAMYAM]);
12631 RaiseScore(level.score[SC_ROBOT]);
12634 RaiseScore(level.score[SC_PACMAN]);
12637 RaiseScore(level.score[SC_NUT]);
12640 case EL_SP_DISK_RED:
12641 case EL_DYNABOMB_INCREASE_NUMBER:
12642 case EL_DYNABOMB_INCREASE_SIZE:
12643 case EL_DYNABOMB_INCREASE_POWER:
12644 RaiseScore(level.score[SC_DYNAMITE]);
12646 case EL_SHIELD_NORMAL:
12647 case EL_SHIELD_DEADLY:
12648 RaiseScore(level.score[SC_SHIELD]);
12650 case EL_EXTRA_TIME:
12651 RaiseScore(level.score[SC_TIME_BONUS]);
12657 RaiseScore(level.score[SC_KEY]);
12660 RaiseScore(element_info[element].collect_score);
12665 void RequestQuitGame(boolean ask_if_really_quit)
12667 if (AllPlayersGone ||
12668 !ask_if_really_quit ||
12669 level_editor_test_game ||
12670 Request("Do you really want to quit the game ?",
12671 REQ_ASK | REQ_STAY_CLOSED))
12673 #if defined(NETWORK_AVALIABLE)
12674 if (options.network)
12675 SendToServer_StopPlaying();
12679 game_status = GAME_MODE_MAIN;
12687 if (tape.playing && tape.deactivate_display)
12688 TapeDeactivateDisplayOff(TRUE);
12691 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12694 if (tape.playing && tape.deactivate_display)
12695 TapeDeactivateDisplayOn();
12702 /* ---------- new game button stuff ---------------------------------------- */
12704 /* graphic position values for game buttons */
12705 #define GAME_BUTTON_XSIZE 30
12706 #define GAME_BUTTON_YSIZE 30
12707 #define GAME_BUTTON_XPOS 5
12708 #define GAME_BUTTON_YPOS 215
12709 #define SOUND_BUTTON_XPOS 5
12710 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12712 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12713 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12714 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12715 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12716 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12717 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12724 } gamebutton_info[NUM_GAME_BUTTONS] =
12727 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12732 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12733 GAME_CTRL_ID_PAUSE,
12737 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12742 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12743 SOUND_CTRL_ID_MUSIC,
12744 "background music on/off"
12747 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12748 SOUND_CTRL_ID_LOOPS,
12749 "sound loops on/off"
12752 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12753 SOUND_CTRL_ID_SIMPLE,
12754 "normal sounds on/off"
12758 void CreateGameButtons()
12762 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12764 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12765 struct GadgetInfo *gi;
12768 unsigned long event_mask;
12769 int gd_xoffset, gd_yoffset;
12770 int gd_x1, gd_x2, gd_y1, gd_y2;
12773 gd_xoffset = gamebutton_info[i].x;
12774 gd_yoffset = gamebutton_info[i].y;
12775 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12776 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12778 if (id == GAME_CTRL_ID_STOP ||
12779 id == GAME_CTRL_ID_PAUSE ||
12780 id == GAME_CTRL_ID_PLAY)
12782 button_type = GD_TYPE_NORMAL_BUTTON;
12784 event_mask = GD_EVENT_RELEASED;
12785 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12786 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12790 button_type = GD_TYPE_CHECK_BUTTON;
12792 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12793 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12794 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12795 event_mask = GD_EVENT_PRESSED;
12796 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12797 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12800 gi = CreateGadget(GDI_CUSTOM_ID, id,
12801 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12802 GDI_X, DX + gd_xoffset,
12803 GDI_Y, DY + gd_yoffset,
12804 GDI_WIDTH, GAME_BUTTON_XSIZE,
12805 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12806 GDI_TYPE, button_type,
12807 GDI_STATE, GD_BUTTON_UNPRESSED,
12808 GDI_CHECKED, checked,
12809 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12810 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12811 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12812 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12813 GDI_EVENT_MASK, event_mask,
12814 GDI_CALLBACK_ACTION, HandleGameButtons,
12818 Error(ERR_EXIT, "cannot create gadget");
12820 game_gadget[id] = gi;
12824 void FreeGameButtons()
12828 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12829 FreeGadget(game_gadget[i]);
12832 static void MapGameButtons()
12836 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12837 MapGadget(game_gadget[i]);
12840 void UnmapGameButtons()
12844 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12845 UnmapGadget(game_gadget[i]);
12848 static void HandleGameButtons(struct GadgetInfo *gi)
12850 int id = gi->custom_id;
12852 if (game_status != GAME_MODE_PLAYING)
12857 case GAME_CTRL_ID_STOP:
12858 RequestQuitGame(TRUE);
12861 case GAME_CTRL_ID_PAUSE:
12862 if (options.network)
12864 #if defined(NETWORK_AVALIABLE)
12866 SendToServer_ContinuePlaying();
12868 SendToServer_PausePlaying();
12872 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12875 case GAME_CTRL_ID_PLAY:
12878 #if defined(NETWORK_AVALIABLE)
12879 if (options.network)
12880 SendToServer_ContinuePlaying();
12884 tape.pausing = FALSE;
12885 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12890 case SOUND_CTRL_ID_MUSIC:
12891 if (setup.sound_music)
12893 setup.sound_music = FALSE;
12896 else if (audio.music_available)
12898 setup.sound = setup.sound_music = TRUE;
12900 SetAudioMode(setup.sound);
12906 case SOUND_CTRL_ID_LOOPS:
12907 if (setup.sound_loops)
12908 setup.sound_loops = FALSE;
12909 else if (audio.loops_available)
12911 setup.sound = setup.sound_loops = TRUE;
12912 SetAudioMode(setup.sound);
12916 case SOUND_CTRL_ID_SIMPLE:
12917 if (setup.sound_simple)
12918 setup.sound_simple = FALSE;
12919 else if (audio.sound_available)
12921 setup.sound = setup.sound_simple = TRUE;
12922 SetAudioMode(setup.sound);