1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF TRUE * 1
33 #define USE_NEW_MOVE_STYLE TRUE * USE_NEW_STUFF * 1
34 #define USE_NEW_MOVE_DELAY TRUE * USE_NEW_STUFF * 1
35 #define USE_NEW_PUSH_DELAY TRUE * USE_NEW_STUFF * 1
36 #define USE_NEW_BLOCK_STYLE TRUE * USE_NEW_STUFF * 1 * 1
37 #define USE_NEW_SP_SLIPPERY TRUE * USE_NEW_STUFF * 1
38 #define USE_NEW_RANDOMIZE TRUE * USE_NEW_STUFF * 1
40 #define USE_PUSH_BUGFIX TRUE * USE_NEW_STUFF * 1
47 /* for MovePlayer() */
48 #define MF_NO_ACTION 0
52 /* for ScrollPlayer() */
54 #define SCROLL_GO_ON 1
57 #define EX_PHASE_START 0
58 #define EX_TYPE_NONE 0
59 #define EX_TYPE_NORMAL (1 << 0)
60 #define EX_TYPE_CENTER (1 << 1)
61 #define EX_TYPE_BORDER (1 << 2)
62 #define EX_TYPE_CROSS (1 << 3)
63 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
65 /* special positions in the game control window (relative to control window) */
68 #define XX_EMERALDS 29
69 #define YY_EMERALDS 54
70 #define XX_DYNAMITE 29
71 #define YY_DYNAMITE 89
80 /* special positions in the game control window (relative to main window) */
81 #define DX_LEVEL (DX + XX_LEVEL)
82 #define DY_LEVEL (DY + YY_LEVEL)
83 #define DX_EMERALDS (DX + XX_EMERALDS)
84 #define DY_EMERALDS (DY + YY_EMERALDS)
85 #define DX_DYNAMITE (DX + XX_DYNAMITE)
86 #define DY_DYNAMITE (DY + YY_DYNAMITE)
87 #define DX_KEYS (DX + XX_KEYS)
88 #define DY_KEYS (DY + YY_KEYS)
89 #define DX_SCORE (DX + XX_SCORE)
90 #define DY_SCORE (DY + YY_SCORE)
91 #define DX_TIME1 (DX + XX_TIME1)
92 #define DX_TIME2 (DX + XX_TIME2)
93 #define DY_TIME (DY + YY_TIME)
95 /* values for initial player move delay (initial delay counter value) */
96 #define INITIAL_MOVE_DELAY_OFF -1
97 #define INITIAL_MOVE_DELAY_ON 0
99 /* values for player movement speed (which is in fact a delay value) */
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED 4
103 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
104 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
106 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
108 /* values for other actions */
109 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
111 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
112 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
114 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
116 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
117 RND(element_info[e].push_delay_random))
118 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
119 RND(element_info[e].drop_delay_random))
120 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
121 RND(element_info[e].move_delay_random))
122 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
123 (element_info[e].move_delay_random))
125 #define GET_TARGET_ELEMENT(e, ch) \
126 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
127 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
129 #define GET_VALID_PLAYER_ELEMENT(e) \
130 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
132 #define CAN_GROW_INTO(e) \
133 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
135 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
141 (CAN_MOVE_INTO_ACID(e) && \
142 Feld[x][y] == EL_ACID) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
154 (CAN_MOVE_INTO_ACID(e) && \
155 Feld[x][y] == EL_ACID) || \
156 (DONT_COLLIDE_WITH(e) && \
158 !PLAYER_ENEMY_PROTECTED(x, y))))
161 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
164 (DONT_COLLIDE_WITH(e) && \
166 !PLAYER_ENEMY_PROTECTED(x, y))))
169 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
173 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
174 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
176 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
181 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
184 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
189 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
192 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
195 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
198 #define PIG_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
201 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
203 IS_FOOD_PENGUIN(Feld[x][y])))
204 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
207 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
208 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
210 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
211 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
216 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
217 (CAN_MOVE_INTO_ACID(e) && \
218 Feld[x][y] == EL_ACID) || \
219 Feld[x][y] == EL_DIAMOND))
221 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID) || \
225 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
227 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
228 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
229 (CAN_MOVE_INTO_ACID(e) && \
230 Feld[x][y] == EL_ACID) || \
231 IS_AMOEBOID(Feld[x][y])))
233 #define PIG_CAN_ENTER_FIELD(e, x, y) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
235 (CAN_MOVE_INTO_ACID(e) && \
236 Feld[x][y] == EL_ACID) || \
237 IS_FOOD_PIG(Feld[x][y])))
239 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID) || \
243 IS_FOOD_PENGUIN(Feld[x][y]) || \
244 Feld[x][y] == EL_EXIT_OPEN))
246 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
247 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
248 (CAN_MOVE_INTO_ACID(e) && \
249 Feld[x][y] == EL_ACID)))
251 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
252 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
253 (CAN_MOVE_INTO_ACID(e) && \
254 Feld[x][y] == EL_ACID) || \
257 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
258 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
259 (CAN_MOVE_INTO_ACID(e) && \
260 Feld[x][y] == EL_ACID)))
264 #define GROUP_NR(e) ((e) - EL_GROUP_START)
265 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
266 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
267 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
269 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
270 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
273 #define CE_ENTER_FIELD_COND(e, x, y) \
274 (!IS_PLAYER(x, y) && \
275 (Feld[x][y] == EL_ACID || \
276 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
278 #define CE_ENTER_FIELD_COND(e, x, y) \
279 (!IS_PLAYER(x, y) && \
280 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
284 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
289 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP 0
296 #define GAME_CTRL_ID_PAUSE 1
297 #define GAME_CTRL_ID_PLAY 2
298 #define SOUND_CTRL_ID_MUSIC 3
299 #define SOUND_CTRL_ID_LOOPS 4
300 #define SOUND_CTRL_ID_SIMPLE 5
302 #define NUM_GAME_BUTTONS 6
305 /* forward declaration for internal use */
307 static void AdvanceFrameAndPlayerCounters(int);
309 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
310 static boolean MovePlayer(struct PlayerInfo *, int, int);
311 static void ScrollPlayer(struct PlayerInfo *, int);
312 static void ScrollScreen(struct PlayerInfo *, int);
314 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
316 static void InitBeltMovement(void);
317 static void CloseAllOpenTimegates(void);
318 static void CheckGravityMovement(struct PlayerInfo *);
319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
320 static void KillHeroUnlessEnemyProtected(int, int);
321 static void KillHeroUnlessExplosionProtected(int, int);
323 static void TestIfPlayerTouchesCustomElement(int, int);
324 static void TestIfElementTouchesCustomElement(int, int);
325 static void TestIfElementHitsCustomElement(int, int, int);
327 static void TestIfElementSmashesCustomElement(int, int, int);
330 static void ChangeElement(int, int, int);
332 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
333 #define CheckTriggeredElementChange(x, y, e, ev) \
334 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
336 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
337 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
338 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
339 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
340 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
341 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
344 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
345 #define CheckElementChange(x, y, e, te, ev) \
346 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
347 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
348 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
349 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
350 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
351 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
352 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
354 static void PlayLevelSound(int, int, int);
355 static void PlayLevelSoundNearest(int, int, int);
356 static void PlayLevelSoundAction(int, int, int);
357 static void PlayLevelSoundElementAction(int, int, int, int);
358 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
359 static void PlayLevelSoundActionIfLoop(int, int, int);
360 static void StopLevelSoundActionIfLoop(int, int, int);
361 static void PlayLevelMusic();
363 static void MapGameButtons();
364 static void HandleGameButtons(struct GadgetInfo *);
366 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
369 /* ------------------------------------------------------------------------- */
370 /* definition of elements that automatically change to other elements after */
371 /* a specified time, eventually calling a function when changing */
372 /* ------------------------------------------------------------------------- */
374 /* forward declaration for changer functions */
375 static void InitBuggyBase(int x, int y);
376 static void WarnBuggyBase(int x, int y);
378 static void InitTrap(int x, int y);
379 static void ActivateTrap(int x, int y);
380 static void ChangeActiveTrap(int x, int y);
382 static void InitRobotWheel(int x, int y);
383 static void RunRobotWheel(int x, int y);
384 static void StopRobotWheel(int x, int y);
386 static void InitTimegateWheel(int x, int y);
387 static void RunTimegateWheel(int x, int y);
389 struct ChangingElementInfo
394 void (*pre_change_function)(int x, int y);
395 void (*change_function)(int x, int y);
396 void (*post_change_function)(int x, int y);
399 static struct ChangingElementInfo change_delay_list[] =
450 EL_SWITCHGATE_OPENING,
458 EL_SWITCHGATE_CLOSING,
459 EL_SWITCHGATE_CLOSED,
491 EL_ACID_SPLASH_RIGHT,
500 EL_SP_BUGGY_BASE_ACTIVATING,
507 EL_SP_BUGGY_BASE_ACTIVATING,
508 EL_SP_BUGGY_BASE_ACTIVE,
515 EL_SP_BUGGY_BASE_ACTIVE,
539 EL_ROBOT_WHEEL_ACTIVE,
547 EL_TIMEGATE_SWITCH_ACTIVE,
568 int push_delay_fixed, push_delay_random;
573 { EL_BALLOON, 0, 0 },
575 { EL_SOKOBAN_OBJECT, 2, 0 },
576 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
577 { EL_SATELLITE, 2, 0 },
578 { EL_SP_DISK_YELLOW, 2, 0 },
580 { EL_UNDEFINED, 0, 0 },
588 move_stepsize_list[] =
590 { EL_AMOEBA_DROP, 2 },
591 { EL_AMOEBA_DROPPING, 2 },
592 { EL_QUICKSAND_FILLING, 1 },
593 { EL_QUICKSAND_EMPTYING, 1 },
594 { EL_MAGIC_WALL_FILLING, 2 },
595 { EL_BD_MAGIC_WALL_FILLING, 2 },
596 { EL_MAGIC_WALL_EMPTYING, 2 },
597 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
607 collect_count_list[] =
610 { EL_BD_DIAMOND, 1 },
611 { EL_EMERALD_YELLOW, 1 },
612 { EL_EMERALD_RED, 1 },
613 { EL_EMERALD_PURPLE, 1 },
615 { EL_SP_INFOTRON, 1 },
627 access_direction_list[] =
629 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
630 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
631 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
632 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
633 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
634 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
635 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
636 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
637 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
638 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
639 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
641 { EL_SP_PORT_LEFT, MV_RIGHT },
642 { EL_SP_PORT_RIGHT, MV_LEFT },
643 { EL_SP_PORT_UP, MV_DOWN },
644 { EL_SP_PORT_DOWN, MV_UP },
645 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
646 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
647 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
649 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
650 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
651 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
652 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
653 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
654 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
655 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
656 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
657 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
658 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
659 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
661 { EL_UNDEFINED, MV_NO_MOVING }
664 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
666 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
667 CH_EVENT_BIT(CE_DELAY))
668 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
669 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
670 IS_JUST_CHANGING(x, y))
672 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
675 void GetPlayerConfig()
677 if (!audio.sound_available)
678 setup.sound_simple = FALSE;
680 if (!audio.loops_available)
681 setup.sound_loops = FALSE;
683 if (!audio.music_available)
684 setup.sound_music = FALSE;
686 if (!video.fullscreen_available)
687 setup.fullscreen = FALSE;
689 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
691 SetAudioMode(setup.sound);
695 static int getBeltNrFromBeltElement(int element)
697 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
698 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
699 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
702 static int getBeltNrFromBeltActiveElement(int element)
704 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
705 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
706 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
709 static int getBeltNrFromBeltSwitchElement(int element)
711 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
712 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
713 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
716 static int getBeltDirNrFromBeltSwitchElement(int element)
718 static int belt_base_element[4] =
720 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
721 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
722 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
723 EL_CONVEYOR_BELT_4_SWITCH_LEFT
726 int belt_nr = getBeltNrFromBeltSwitchElement(element);
727 int belt_dir_nr = element - belt_base_element[belt_nr];
729 return (belt_dir_nr % 3);
732 static int getBeltDirFromBeltSwitchElement(int element)
734 static int belt_move_dir[3] =
741 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
743 return belt_move_dir[belt_dir_nr];
746 static void InitPlayerField(int x, int y, int element, boolean init_game)
748 if (element == EL_SP_MURPHY)
752 if (stored_player[0].present)
754 Feld[x][y] = EL_SP_MURPHY_CLONE;
760 stored_player[0].use_murphy_graphic = TRUE;
763 Feld[x][y] = EL_PLAYER_1;
769 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
770 int jx = player->jx, jy = player->jy;
772 player->present = TRUE;
774 player->block_last_field = (element == EL_SP_MURPHY ?
775 level.sp_block_last_field :
776 level.block_last_field);
778 #if USE_NEW_BLOCK_STYLE
780 player->block_delay = (player->block_last_field ?
781 (element == EL_SP_MURPHY ?
782 level.sp_block_delay :
783 level.block_delay) : 0);
785 player->block_delay = (element == EL_SP_MURPHY ?
786 (player->block_last_field ? 7 : 1) :
787 (player->block_last_field ? 7 : 1));
791 printf("::: block_last_field == %d, block_delay = %d\n",
792 player->block_last_field, player->block_delay);
796 if (!options.network || player->connected)
798 player->active = TRUE;
800 /* remove potentially duplicate players */
801 if (StorePlayer[jx][jy] == Feld[x][y])
802 StorePlayer[jx][jy] = 0;
804 StorePlayer[x][y] = Feld[x][y];
808 printf("Player %d activated.\n", player->element_nr);
809 printf("[Local player is %d and currently %s.]\n",
810 local_player->element_nr,
811 local_player->active ? "active" : "not active");
815 Feld[x][y] = EL_EMPTY;
817 player->jx = player->last_jx = x;
818 player->jy = player->last_jy = y;
822 static void InitField(int x, int y, boolean init_game)
824 int element = Feld[x][y];
833 InitPlayerField(x, y, element, init_game);
836 case EL_SOKOBAN_FIELD_PLAYER:
837 element = Feld[x][y] = EL_PLAYER_1;
838 InitField(x, y, init_game);
840 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
841 InitField(x, y, init_game);
844 case EL_SOKOBAN_FIELD_EMPTY:
845 local_player->sokobanfields_still_needed++;
849 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
850 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
851 else if (x > 0 && Feld[x-1][y] == EL_ACID)
852 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
853 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
854 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
855 else if (y > 0 && Feld[x][y-1] == EL_ACID)
856 Feld[x][y] = EL_ACID_POOL_BOTTOM;
857 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
858 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
866 case EL_SPACESHIP_RIGHT:
867 case EL_SPACESHIP_UP:
868 case EL_SPACESHIP_LEFT:
869 case EL_SPACESHIP_DOWN:
871 case EL_BD_BUTTERFLY_RIGHT:
872 case EL_BD_BUTTERFLY_UP:
873 case EL_BD_BUTTERFLY_LEFT:
874 case EL_BD_BUTTERFLY_DOWN:
875 case EL_BD_BUTTERFLY:
876 case EL_BD_FIREFLY_RIGHT:
877 case EL_BD_FIREFLY_UP:
878 case EL_BD_FIREFLY_LEFT:
879 case EL_BD_FIREFLY_DOWN:
881 case EL_PACMAN_RIGHT:
905 if (y == lev_fieldy - 1)
907 Feld[x][y] = EL_AMOEBA_GROWING;
908 Store[x][y] = EL_AMOEBA_WET;
912 case EL_DYNAMITE_ACTIVE:
913 case EL_SP_DISK_RED_ACTIVE:
914 case EL_DYNABOMB_PLAYER_1_ACTIVE:
915 case EL_DYNABOMB_PLAYER_2_ACTIVE:
916 case EL_DYNABOMB_PLAYER_3_ACTIVE:
917 case EL_DYNABOMB_PLAYER_4_ACTIVE:
922 local_player->lights_still_needed++;
926 local_player->friends_still_needed++;
931 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
936 Feld[x][y] = EL_EMPTY;
941 case EL_EM_KEY_1_FILE:
942 Feld[x][y] = EL_EM_KEY_1;
944 case EL_EM_KEY_2_FILE:
945 Feld[x][y] = EL_EM_KEY_2;
947 case EL_EM_KEY_3_FILE:
948 Feld[x][y] = EL_EM_KEY_3;
950 case EL_EM_KEY_4_FILE:
951 Feld[x][y] = EL_EM_KEY_4;
955 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
956 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
957 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
958 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
959 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
960 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
961 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
962 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
963 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
964 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
965 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
966 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
969 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
970 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
971 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
973 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
975 game.belt_dir[belt_nr] = belt_dir;
976 game.belt_dir_nr[belt_nr] = belt_dir_nr;
978 else /* more than one switch -- set it like the first switch */
980 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
985 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
987 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
990 case EL_LIGHT_SWITCH_ACTIVE:
992 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
996 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
998 else if (IS_GROUP_ELEMENT(element))
1000 struct ElementGroupInfo *group = element_info[element].group;
1001 int last_anim_random_frame = gfx.anim_random_frame;
1004 if (group->choice_mode == ANIM_RANDOM)
1005 gfx.anim_random_frame = RND(group->num_elements_resolved);
1007 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1008 group->choice_mode, 0,
1011 if (group->choice_mode == ANIM_RANDOM)
1012 gfx.anim_random_frame = last_anim_random_frame;
1014 group->choice_pos++;
1016 Feld[x][y] = group->element_resolved[element_pos];
1018 InitField(x, y, init_game);
1024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1026 InitField(x, y, init_game);
1028 /* not needed to call InitMovDir() -- already done by InitField()! */
1029 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1030 CAN_MOVE(Feld[x][y]))
1034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1036 int old_element = Feld[x][y];
1038 InitField(x, y, init_game);
1040 /* not needed to call InitMovDir() -- already done by InitField()! */
1041 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1042 CAN_MOVE(old_element) &&
1043 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1046 /* this case is in fact a combination of not less than three bugs:
1047 first, it calls InitMovDir() for elements that can move, although this is
1048 already done by InitField(); then, it checks the element that was at this
1049 field _before_ the call to InitField() (which can change it); lastly, it
1050 was not called for "mole with direction" elements, which were treated as
1051 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1055 inline void DrawGameValue_Emeralds(int value)
1057 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1060 inline void DrawGameValue_Dynamite(int value)
1062 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1065 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1069 /* currently only 4 of 8 possible keys are displayed */
1070 for (i = 0; i < STD_NUM_KEYS; i++)
1072 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1073 el2edimg(EL_KEY_1 + i));
1076 inline void DrawGameValue_Score(int value)
1078 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1081 inline void DrawGameValue_Time(int value)
1084 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1086 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1089 inline void DrawGameValue_Level(int value)
1092 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1095 /* misuse area for displaying emeralds to draw bigger level number */
1096 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1097 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1099 /* now copy it to the area for displaying level number */
1100 BlitBitmap(drawto, drawto,
1101 DX_EMERALDS, DY_EMERALDS + 1,
1102 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1103 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1104 DX_LEVEL - 1, DY_LEVEL + 1);
1106 /* restore the area for displaying emeralds */
1107 DrawGameValue_Emeralds(local_player->gems_still_needed);
1109 /* yes, this is all really ugly :-) */
1113 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1116 int key[MAX_NUM_KEYS];
1119 for (i = 0; i < MAX_NUM_KEYS; i++)
1120 key[i] = key_bits & (1 << i);
1122 DrawGameValue_Level(level_nr);
1124 DrawGameValue_Emeralds(emeralds);
1125 DrawGameValue_Dynamite(dynamite);
1126 DrawGameValue_Score(score);
1127 DrawGameValue_Time(time);
1129 DrawGameValue_Keys(key);
1132 void DrawGameDoorValues()
1136 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1138 DrawGameDoorValues_EM();
1143 DrawGameValue_Level(level_nr);
1145 DrawGameValue_Emeralds(local_player->gems_still_needed);
1146 DrawGameValue_Dynamite(local_player->inventory_size);
1147 DrawGameValue_Score(local_player->score);
1148 DrawGameValue_Time(TimeLeft);
1150 for (i = 0; i < MAX_PLAYERS; i++)
1151 DrawGameValue_Keys(stored_player[i].key);
1154 static void resolve_group_element(int group_element, int recursion_depth)
1156 static int group_nr;
1157 static struct ElementGroupInfo *group;
1158 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1161 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1163 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1164 group_element - EL_GROUP_START + 1);
1166 /* replace element which caused too deep recursion by question mark */
1167 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1172 if (recursion_depth == 0) /* initialization */
1174 group = element_info[group_element].group;
1175 group_nr = group_element - EL_GROUP_START;
1177 group->num_elements_resolved = 0;
1178 group->choice_pos = 0;
1181 for (i = 0; i < actual_group->num_elements; i++)
1183 int element = actual_group->element[i];
1185 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1188 if (IS_GROUP_ELEMENT(element))
1189 resolve_group_element(element, recursion_depth + 1);
1192 group->element_resolved[group->num_elements_resolved++] = element;
1193 element_info[element].in_group[group_nr] = TRUE;
1198 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1200 printf("::: group %d: %d resolved elements\n",
1201 group_element - EL_GROUP_START, group->num_elements_resolved);
1202 for (i = 0; i < group->num_elements_resolved; i++)
1203 printf("::: - %d ['%s']\n", group->element_resolved[i],
1204 element_info[group->element_resolved[i]].token_name);
1211 =============================================================================
1213 -----------------------------------------------------------------------------
1214 initialize game engine due to level / tape version number
1215 =============================================================================
1218 static void InitGameEngine()
1222 /* set game engine from tape file when re-playing, else from level file */
1223 game.engine_version = (tape.playing ? tape.engine_version :
1224 level.game_version);
1226 /* ---------------------------------------------------------------------- */
1227 /* set flags for bugs and changes according to active game engine version */
1228 /* ---------------------------------------------------------------------- */
1232 Before 3.1.0, custom elements that "change when pushing" changed directly
1233 after the player started pushing them (until then handled in "DigField()").
1234 Since 3.1.0, these custom elements are not changed until the "pushing"
1235 move of the element is finished (now handled in "ContinueMoving()").
1237 Affected levels/tapes:
1238 The first condition is generally needed for all levels/tapes before version
1239 3.1.0, which might use the old behaviour before it was changed; known tapes
1240 that are affected are some tapes from the level set "Walpurgis Gardens" by
1242 The second condition is an exception from the above case and is needed for
1243 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1244 above (including some development versions of 3.1.0), but before it was
1245 known that this change would break tapes like the above and was fixed in
1246 3.1.1, so that the changed behaviour was active although the engine version
1247 while recording maybe was before 3.1.0. There is at least one tape that is
1248 affected by this exception, which is the tape for the one-level set "Bug
1249 Machine" by Juergen Bonhagen.
1252 game.use_bug_change_when_pushing =
1253 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1255 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1256 tape.game_version < VERSION_IDENT(3,1,1,0)));
1258 /* ---------------------------------------------------------------------- */
1260 /* dynamically adjust element properties according to game engine version */
1261 InitElementPropertiesEngine(game.engine_version);
1264 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1265 printf(" tape version == %06d [%s] [file: %06d]\n",
1266 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1268 printf(" => game.engine_version == %06d\n", game.engine_version);
1271 /* ---------- recursively resolve group elements ------------------------- */
1273 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1274 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1275 element_info[i].in_group[j] = FALSE;
1277 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1278 resolve_group_element(EL_GROUP_START + i, 0);
1280 /* ---------- initialize player's initial move delay --------------------- */
1282 #if USE_NEW_MOVE_DELAY
1283 /* dynamically adjust player properties according to level information */
1284 game.initial_move_delay_value =
1285 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1287 /* dynamically adjust player properties according to game engine version */
1288 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1289 game.initial_move_delay_value : 0);
1291 /* dynamically adjust player properties according to game engine version */
1292 game.initial_move_delay =
1293 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1294 INITIAL_MOVE_DELAY_OFF);
1296 /* dynamically adjust player properties according to level information */
1297 game.initial_move_delay_value =
1298 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1301 /* ---------- initialize player's initial push delay --------------------- */
1303 /* dynamically adjust player properties according to game engine version */
1304 game.initial_push_delay_value =
1305 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1307 /* ---------- initialize changing elements ------------------------------- */
1309 /* initialize changing elements information */
1310 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1312 struct ElementInfo *ei = &element_info[i];
1314 /* this pointer might have been changed in the level editor */
1315 ei->change = &ei->change_page[0];
1317 if (!IS_CUSTOM_ELEMENT(i))
1319 ei->change->target_element = EL_EMPTY_SPACE;
1320 ei->change->delay_fixed = 0;
1321 ei->change->delay_random = 0;
1322 ei->change->delay_frames = 1;
1325 ei->change_events = CE_BITMASK_DEFAULT;
1326 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1328 ei->event_page_nr[j] = 0;
1329 ei->event_page[j] = &ei->change_page[0];
1333 /* add changing elements from pre-defined list */
1334 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1336 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1337 struct ElementInfo *ei = &element_info[ch_delay->element];
1339 ei->change->target_element = ch_delay->target_element;
1340 ei->change->delay_fixed = ch_delay->change_delay;
1342 ei->change->pre_change_function = ch_delay->pre_change_function;
1343 ei->change->change_function = ch_delay->change_function;
1344 ei->change->post_change_function = ch_delay->post_change_function;
1346 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1349 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1354 /* add change events from custom element configuration */
1355 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1357 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1359 for (j = 0; j < ei->num_change_pages; j++)
1361 if (!ei->change_page[j].can_change)
1364 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1366 /* only add event page for the first page found with this event */
1367 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1368 !(ei->change_events & CH_EVENT_BIT(k)))
1370 ei->change_events |= CH_EVENT_BIT(k);
1371 ei->event_page_nr[k] = j;
1372 ei->event_page[k] = &ei->change_page[j];
1380 /* add change events from custom element configuration */
1381 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1383 int element = EL_CUSTOM_START + i;
1385 /* only add custom elements that change after fixed/random frame delay */
1386 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1387 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1391 /* ---------- initialize run-time trigger player and element ------------- */
1393 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1395 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1397 for (j = 0; j < ei->num_change_pages; j++)
1399 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1400 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1404 /* ---------- initialize trigger events ---------------------------------- */
1406 /* initialize trigger events information */
1407 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1408 trigger_events[i] = EP_BITMASK_DEFAULT;
1411 /* add trigger events from element change event properties */
1412 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1414 struct ElementInfo *ei = &element_info[i];
1416 for (j = 0; j < ei->num_change_pages; j++)
1418 if (!ei->change_page[j].can_change)
1421 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1423 int trigger_element = ei->change_page[j].trigger_element;
1425 if (IS_GROUP_ELEMENT(trigger_element))
1427 struct ElementGroupInfo *group = element_info[trigger_element].group;
1429 for (k = 0; k < group->num_elements_resolved; k++)
1430 trigger_events[group->element_resolved[k]]
1431 |= ei->change_page[j].events;
1434 trigger_events[trigger_element] |= ei->change_page[j].events;
1439 /* add trigger events from element change event properties */
1440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1441 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1442 trigger_events[element_info[i].change->trigger_element] |=
1443 element_info[i].change->events;
1446 /* ---------- initialize push delay -------------------------------------- */
1448 /* initialize push delay values to default */
1449 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1451 if (!IS_CUSTOM_ELEMENT(i))
1453 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1454 element_info[i].push_delay_random = game.default_push_delay_random;
1458 /* set push delay value for certain elements from pre-defined list */
1459 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1461 int e = push_delay_list[i].element;
1463 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1464 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1467 /* set push delay value for Supaplex elements for newer engine versions */
1468 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1472 if (IS_SP_ELEMENT(i))
1474 #if USE_NEW_MOVE_STYLE
1475 /* set SP push delay to just enough to push under a falling zonk */
1476 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1478 element_info[i].push_delay_fixed = delay;
1479 element_info[i].push_delay_random = 0;
1481 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1482 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1488 /* ---------- initialize move stepsize ----------------------------------- */
1490 /* initialize move stepsize values to default */
1491 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1492 if (!IS_CUSTOM_ELEMENT(i))
1493 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1495 /* set move stepsize value for certain elements from pre-defined list */
1496 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1498 int e = move_stepsize_list[i].element;
1500 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1504 /* ---------- initialize move dig/leave ---------------------------------- */
1506 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1508 element_info[i].can_leave_element = FALSE;
1509 element_info[i].can_leave_element_last = FALSE;
1513 /* ---------- initialize gem count --------------------------------------- */
1515 /* initialize gem count values for each element */
1516 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1517 if (!IS_CUSTOM_ELEMENT(i))
1518 element_info[i].collect_count = 0;
1520 /* add gem count values for all elements from pre-defined list */
1521 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1522 element_info[collect_count_list[i].element].collect_count =
1523 collect_count_list[i].count;
1525 /* ---------- initialize access direction -------------------------------- */
1527 /* initialize access direction values to default (access from every side) */
1528 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1529 if (!IS_CUSTOM_ELEMENT(i))
1530 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1532 /* set access direction value for certain elements from pre-defined list */
1533 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1534 element_info[access_direction_list[i].element].access_direction =
1535 access_direction_list[i].direction;
1540 =============================================================================
1542 -----------------------------------------------------------------------------
1543 initialize and start new game
1544 =============================================================================
1549 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1550 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1551 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1558 #if USE_NEW_AMOEBA_CODE
1559 printf("Using new amoeba code.\n");
1561 printf("Using old amoeba code.\n");
1566 /* don't play tapes over network */
1567 network_playing = (options.network && !tape.playing);
1569 for (i = 0; i < MAX_PLAYERS; i++)
1571 struct PlayerInfo *player = &stored_player[i];
1573 player->index_nr = i;
1574 player->index_bit = (1 << i);
1575 player->element_nr = EL_PLAYER_1 + i;
1577 player->present = FALSE;
1578 player->active = FALSE;
1581 player->effective_action = 0;
1582 player->programmed_action = 0;
1585 player->gems_still_needed = level.gems_needed;
1586 player->sokobanfields_still_needed = 0;
1587 player->lights_still_needed = 0;
1588 player->friends_still_needed = 0;
1590 for (j = 0; j < MAX_NUM_KEYS; j++)
1591 player->key[j] = FALSE;
1593 player->dynabomb_count = 0;
1594 player->dynabomb_size = 1;
1595 player->dynabombs_left = 0;
1596 player->dynabomb_xl = FALSE;
1598 player->MovDir = MV_NO_MOVING;
1601 player->GfxDir = MV_NO_MOVING;
1602 player->GfxAction = ACTION_DEFAULT;
1604 player->StepFrame = 0;
1606 player->use_murphy_graphic = FALSE;
1608 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1609 player->block_delay = -1; /* initialized in InitPlayerField() */
1611 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1613 player->actual_frame_counter = 0;
1615 player->step_counter = 0;
1617 player->last_move_dir = MV_NO_MOVING;
1619 player->is_waiting = FALSE;
1620 player->is_moving = FALSE;
1621 player->is_auto_moving = FALSE;
1622 player->is_digging = FALSE;
1623 player->is_snapping = FALSE;
1624 player->is_collecting = FALSE;
1625 player->is_pushing = FALSE;
1626 player->is_switching = FALSE;
1627 player->is_dropping = FALSE;
1629 player->is_bored = FALSE;
1630 player->is_sleeping = FALSE;
1632 player->frame_counter_bored = -1;
1633 player->frame_counter_sleeping = -1;
1635 player->anim_delay_counter = 0;
1636 player->post_delay_counter = 0;
1638 player->action_waiting = ACTION_DEFAULT;
1639 player->last_action_waiting = ACTION_DEFAULT;
1640 player->special_action_bored = ACTION_DEFAULT;
1641 player->special_action_sleeping = ACTION_DEFAULT;
1643 player->num_special_action_bored = 0;
1644 player->num_special_action_sleeping = 0;
1646 /* determine number of special actions for bored and sleeping animation */
1647 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1649 boolean found = FALSE;
1651 for (k = 0; k < NUM_DIRECTIONS; k++)
1652 if (el_act_dir2img(player->element_nr, j, k) !=
1653 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1657 player->num_special_action_bored++;
1661 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1663 boolean found = FALSE;
1665 for (k = 0; k < NUM_DIRECTIONS; k++)
1666 if (el_act_dir2img(player->element_nr, j, k) !=
1667 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1671 player->num_special_action_sleeping++;
1676 player->switch_x = -1;
1677 player->switch_y = -1;
1679 player->show_envelope = 0;
1681 player->move_delay = game.initial_move_delay;
1682 player->move_delay_value = game.initial_move_delay_value;
1684 player->move_delay_reset_counter = 0;
1686 #if USE_NEW_PUSH_DELAY
1687 player->push_delay = -1; /* initialized when pushing starts */
1688 player->push_delay_value = game.initial_push_delay_value;
1690 player->push_delay = 0;
1691 player->push_delay_value = game.initial_push_delay_value;
1694 player->drop_delay = 0;
1696 player->last_jx = player->last_jy = 0;
1697 player->jx = player->jy = 0;
1699 player->shield_normal_time_left = 0;
1700 player->shield_deadly_time_left = 0;
1702 player->inventory_infinite_element = EL_UNDEFINED;
1703 player->inventory_size = 0;
1705 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1706 SnapField(player, 0, 0);
1708 player->LevelSolved = FALSE;
1709 player->GameOver = FALSE;
1712 network_player_action_received = FALSE;
1714 #if defined(NETWORK_AVALIABLE)
1715 /* initial null action */
1716 if (network_playing)
1717 SendToServer_MovePlayer(MV_NO_MOVING);
1726 TimeLeft = level.time;
1729 ScreenMovDir = MV_NO_MOVING;
1733 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1735 AllPlayersGone = FALSE;
1737 game.yamyam_content_nr = 0;
1738 game.magic_wall_active = FALSE;
1739 game.magic_wall_time_left = 0;
1740 game.light_time_left = 0;
1741 game.timegate_time_left = 0;
1742 game.switchgate_pos = 0;
1743 game.balloon_dir = MV_NO_MOVING;
1744 game.gravity = level.initial_gravity;
1745 game.explosions_delayed = TRUE;
1747 game.envelope_active = FALSE;
1749 for (i = 0; i < NUM_BELTS; i++)
1751 game.belt_dir[i] = MV_NO_MOVING;
1752 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1755 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1756 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1758 for (x = 0; x < lev_fieldx; x++)
1760 for (y = 0; y < lev_fieldy; y++)
1762 Feld[x][y] = level.field[x][y];
1763 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1764 ChangeDelay[x][y] = 0;
1765 ChangePage[x][y] = -1;
1766 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1768 WasJustMoving[x][y] = 0;
1769 WasJustFalling[x][y] = 0;
1770 CheckCollision[x][y] = 0;
1772 Pushed[x][y] = FALSE;
1774 Changed[x][y] = CE_BITMASK_DEFAULT;
1775 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1777 ExplodePhase[x][y] = 0;
1778 ExplodeDelay[x][y] = 0;
1779 ExplodeField[x][y] = EX_TYPE_NONE;
1781 RunnerVisit[x][y] = 0;
1782 PlayerVisit[x][y] = 0;
1785 GfxRandom[x][y] = INIT_GFX_RANDOM();
1786 GfxElement[x][y] = EL_UNDEFINED;
1787 GfxAction[x][y] = ACTION_DEFAULT;
1788 GfxDir[x][y] = MV_NO_MOVING;
1792 for (y = 0; y < lev_fieldy; y++)
1794 for (x = 0; x < lev_fieldx; x++)
1796 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1798 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1800 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1803 InitField(x, y, TRUE);
1809 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1810 emulate_sb ? EMU_SOKOBAN :
1811 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1813 /* initialize explosion and ignition delay */
1814 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1816 if (!IS_CUSTOM_ELEMENT(i))
1819 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1820 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1821 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1822 int last_phase = (num_phase + 1) * delay;
1823 int half_phase = (num_phase / 2) * delay;
1825 element_info[i].explosion_delay = last_phase - 1;
1826 element_info[i].ignition_delay = half_phase;
1829 if (i == EL_BLACK_ORB)
1830 element_info[i].ignition_delay = 0;
1832 if (i == EL_BLACK_ORB)
1833 element_info[i].ignition_delay = 1;
1838 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1839 element_info[i].explosion_delay = 1;
1841 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1842 element_info[i].ignition_delay = 1;
1846 /* correct non-moving belts to start moving left */
1847 for (i = 0; i < NUM_BELTS; i++)
1848 if (game.belt_dir[i] == MV_NO_MOVING)
1849 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1851 /* check if any connected player was not found in playfield */
1852 for (i = 0; i < MAX_PLAYERS; i++)
1854 struct PlayerInfo *player = &stored_player[i];
1856 if (player->connected && !player->present)
1858 for (j = 0; j < MAX_PLAYERS; j++)
1860 struct PlayerInfo *some_player = &stored_player[j];
1861 int jx = some_player->jx, jy = some_player->jy;
1863 /* assign first free player found that is present in the playfield */
1864 if (some_player->present && !some_player->connected)
1866 player->present = TRUE;
1867 player->active = TRUE;
1869 some_player->present = FALSE;
1870 some_player->active = FALSE;
1873 player->element_nr = some_player->element_nr;
1876 #if USE_NEW_BLOCK_STYLE
1877 player->block_last_field = some_player->block_last_field;
1878 player->block_delay = some_player->block_delay;
1881 StorePlayer[jx][jy] = player->element_nr;
1882 player->jx = player->last_jx = jx;
1883 player->jy = player->last_jy = jy;
1893 /* when playing a tape, eliminate all players which do not participate */
1895 for (i = 0; i < MAX_PLAYERS; i++)
1897 if (stored_player[i].active && !tape.player_participates[i])
1899 struct PlayerInfo *player = &stored_player[i];
1900 int jx = player->jx, jy = player->jy;
1902 player->active = FALSE;
1903 StorePlayer[jx][jy] = 0;
1904 Feld[jx][jy] = EL_EMPTY;
1908 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1910 /* when in single player mode, eliminate all but the first active player */
1912 for (i = 0; i < MAX_PLAYERS; i++)
1914 if (stored_player[i].active)
1916 for (j = i + 1; j < MAX_PLAYERS; j++)
1918 if (stored_player[j].active)
1920 struct PlayerInfo *player = &stored_player[j];
1921 int jx = player->jx, jy = player->jy;
1923 player->active = FALSE;
1924 player->present = FALSE;
1926 StorePlayer[jx][jy] = 0;
1927 Feld[jx][jy] = EL_EMPTY;
1934 /* when recording the game, store which players take part in the game */
1937 for (i = 0; i < MAX_PLAYERS; i++)
1938 if (stored_player[i].active)
1939 tape.player_participates[i] = TRUE;
1944 for (i = 0; i < MAX_PLAYERS; i++)
1946 struct PlayerInfo *player = &stored_player[i];
1948 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1953 if (local_player == player)
1954 printf("Player %d is local player.\n", i+1);
1958 if (BorderElement == EL_EMPTY)
1961 SBX_Right = lev_fieldx - SCR_FIELDX;
1963 SBY_Lower = lev_fieldy - SCR_FIELDY;
1968 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1970 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1973 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1974 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1976 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1977 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1979 /* if local player not found, look for custom element that might create
1980 the player (make some assumptions about the right custom element) */
1981 if (!local_player->present)
1983 int start_x = 0, start_y = 0;
1984 int found_rating = 0;
1985 int found_element = EL_UNDEFINED;
1987 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1989 int element = Feld[x][y];
1994 if (!IS_CUSTOM_ELEMENT(element))
1997 if (CAN_CHANGE(element))
1999 for (i = 0; i < element_info[element].num_change_pages; i++)
2001 content = element_info[element].change_page[i].target_element;
2002 is_player = ELEM_IS_PLAYER(content);
2004 if (is_player && (found_rating < 3 || element < found_element))
2010 found_element = element;
2015 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2017 content = element_info[element].content[xx][yy];
2018 is_player = ELEM_IS_PLAYER(content);
2020 if (is_player && (found_rating < 2 || element < found_element))
2022 start_x = x + xx - 1;
2023 start_y = y + yy - 1;
2026 found_element = element;
2029 if (!CAN_CHANGE(element))
2032 for (i = 0; i < element_info[element].num_change_pages; i++)
2034 content= element_info[element].change_page[i].target_content[xx][yy];
2035 is_player = ELEM_IS_PLAYER(content);
2037 if (is_player && (found_rating < 1 || element < found_element))
2039 start_x = x + xx - 1;
2040 start_y = y + yy - 1;
2043 found_element = element;
2049 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2050 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2053 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2054 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2060 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2061 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2062 local_player->jx - MIDPOSX);
2064 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2065 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2066 local_player->jy - MIDPOSY);
2068 scroll_x = SBX_Left;
2069 scroll_y = SBY_Upper;
2070 if (local_player->jx >= SBX_Left + MIDPOSX)
2071 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2072 local_player->jx - MIDPOSX :
2074 if (local_player->jy >= SBY_Upper + MIDPOSY)
2075 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2076 local_player->jy - MIDPOSY :
2081 CloseDoor(DOOR_CLOSE_1);
2083 /* !!! FIX THIS (START) !!! */
2084 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2086 InitGameEngine_EM();
2093 /* after drawing the level, correct some elements */
2094 if (game.timegate_time_left == 0)
2095 CloseAllOpenTimegates();
2097 if (setup.soft_scrolling)
2098 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2100 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2103 /* !!! FIX THIS (END) !!! */
2105 /* copy default game door content to main double buffer */
2106 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2107 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2109 DrawGameDoorValues();
2113 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2114 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2115 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2119 /* copy actual game door content to door double buffer for OpenDoor() */
2120 BlitBitmap(drawto, bitmap_db_door,
2121 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2123 OpenDoor(DOOR_OPEN_ALL);
2125 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2127 if (setup.sound_music)
2130 KeyboardAutoRepeatOffUnlessAutoplay();
2134 for (i = 0; i < MAX_PLAYERS; i++)
2135 printf("Player %d %sactive.\n",
2136 i + 1, (stored_player[i].active ? "" : "not "));
2140 printf("::: starting game [%d]\n", FrameCounter);
2144 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2146 /* this is used for non-R'n'D game engines to update certain engine values */
2148 /* needed to determine if sounds are played within the visible screen area */
2149 scroll_x = actual_scroll_x;
2150 scroll_y = actual_scroll_y;
2153 void InitMovDir(int x, int y)
2155 int i, element = Feld[x][y];
2156 static int xy[4][2] =
2163 static int direction[3][4] =
2165 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2166 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2167 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2176 Feld[x][y] = EL_BUG;
2177 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2180 case EL_SPACESHIP_RIGHT:
2181 case EL_SPACESHIP_UP:
2182 case EL_SPACESHIP_LEFT:
2183 case EL_SPACESHIP_DOWN:
2184 Feld[x][y] = EL_SPACESHIP;
2185 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2188 case EL_BD_BUTTERFLY_RIGHT:
2189 case EL_BD_BUTTERFLY_UP:
2190 case EL_BD_BUTTERFLY_LEFT:
2191 case EL_BD_BUTTERFLY_DOWN:
2192 Feld[x][y] = EL_BD_BUTTERFLY;
2193 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2196 case EL_BD_FIREFLY_RIGHT:
2197 case EL_BD_FIREFLY_UP:
2198 case EL_BD_FIREFLY_LEFT:
2199 case EL_BD_FIREFLY_DOWN:
2200 Feld[x][y] = EL_BD_FIREFLY;
2201 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2204 case EL_PACMAN_RIGHT:
2206 case EL_PACMAN_LEFT:
2207 case EL_PACMAN_DOWN:
2208 Feld[x][y] = EL_PACMAN;
2209 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2212 case EL_SP_SNIKSNAK:
2213 MovDir[x][y] = MV_UP;
2216 case EL_SP_ELECTRON:
2217 MovDir[x][y] = MV_LEFT;
2224 Feld[x][y] = EL_MOLE;
2225 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2229 if (IS_CUSTOM_ELEMENT(element))
2231 struct ElementInfo *ei = &element_info[element];
2232 int move_direction_initial = ei->move_direction_initial;
2233 int move_pattern = ei->move_pattern;
2235 if (move_direction_initial == MV_START_PREVIOUS)
2237 if (MovDir[x][y] != MV_NO_MOVING)
2240 move_direction_initial = MV_START_AUTOMATIC;
2243 if (move_direction_initial == MV_START_RANDOM)
2244 MovDir[x][y] = 1 << RND(4);
2245 else if (move_direction_initial & MV_ANY_DIRECTION)
2246 MovDir[x][y] = move_direction_initial;
2247 else if (move_pattern == MV_ALL_DIRECTIONS ||
2248 move_pattern == MV_TURNING_LEFT ||
2249 move_pattern == MV_TURNING_RIGHT ||
2250 move_pattern == MV_TURNING_LEFT_RIGHT ||
2251 move_pattern == MV_TURNING_RIGHT_LEFT ||
2252 move_pattern == MV_TURNING_RANDOM)
2253 MovDir[x][y] = 1 << RND(4);
2254 else if (move_pattern == MV_HORIZONTAL)
2255 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2256 else if (move_pattern == MV_VERTICAL)
2257 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2258 else if (move_pattern & MV_ANY_DIRECTION)
2259 MovDir[x][y] = element_info[element].move_pattern;
2260 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2261 move_pattern == MV_ALONG_RIGHT_SIDE)
2264 /* use random direction as default start direction */
2265 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2266 MovDir[x][y] = 1 << RND(4);
2269 for (i = 0; i < NUM_DIRECTIONS; i++)
2271 int x1 = x + xy[i][0];
2272 int y1 = y + xy[i][1];
2274 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2276 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2277 MovDir[x][y] = direction[0][i];
2279 MovDir[x][y] = direction[1][i];
2288 MovDir[x][y] = 1 << RND(4);
2290 if (element != EL_BUG &&
2291 element != EL_SPACESHIP &&
2292 element != EL_BD_BUTTERFLY &&
2293 element != EL_BD_FIREFLY)
2296 for (i = 0; i < NUM_DIRECTIONS; i++)
2298 int x1 = x + xy[i][0];
2299 int y1 = y + xy[i][1];
2301 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2303 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2305 MovDir[x][y] = direction[0][i];
2308 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2309 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2311 MovDir[x][y] = direction[1][i];
2320 GfxDir[x][y] = MovDir[x][y];
2323 void InitAmoebaNr(int x, int y)
2326 int group_nr = AmoebeNachbarNr(x, y);
2330 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2332 if (AmoebaCnt[i] == 0)
2340 AmoebaNr[x][y] = group_nr;
2341 AmoebaCnt[group_nr]++;
2342 AmoebaCnt2[group_nr]++;
2348 boolean raise_level = FALSE;
2350 if (local_player->MovPos)
2354 if (tape.auto_play) /* tape might already be stopped here */
2355 tape.auto_play_level_solved = TRUE;
2357 if (tape.playing && tape.auto_play)
2358 tape.auto_play_level_solved = TRUE;
2361 local_player->LevelSolved = FALSE;
2363 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2367 if (!tape.playing && setup.sound_loops)
2368 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2369 SND_CTRL_PLAY_LOOP);
2371 while (TimeLeft > 0)
2373 if (!tape.playing && !setup.sound_loops)
2374 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2375 if (TimeLeft > 0 && !(TimeLeft % 10))
2376 RaiseScore(level.score[SC_TIME_BONUS]);
2377 if (TimeLeft > 100 && !(TimeLeft % 10))
2382 DrawGameValue_Time(TimeLeft);
2390 if (!tape.playing && setup.sound_loops)
2391 StopSound(SND_GAME_LEVELTIME_BONUS);
2393 else if (level.time == 0) /* level without time limit */
2395 if (!tape.playing && setup.sound_loops)
2396 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2397 SND_CTRL_PLAY_LOOP);
2399 while (TimePlayed < 999)
2401 if (!tape.playing && !setup.sound_loops)
2402 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2403 if (TimePlayed < 999 && !(TimePlayed % 10))
2404 RaiseScore(level.score[SC_TIME_BONUS]);
2405 if (TimePlayed < 900 && !(TimePlayed % 10))
2410 DrawGameValue_Time(TimePlayed);
2418 if (!tape.playing && setup.sound_loops)
2419 StopSound(SND_GAME_LEVELTIME_BONUS);
2422 /* close exit door after last player */
2423 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2424 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2425 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2427 int element = Feld[ExitX][ExitY];
2429 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2430 EL_SP_EXIT_CLOSING);
2432 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2435 /* Hero disappears */
2436 if (ExitX >= 0 && ExitY >= 0)
2437 DrawLevelField(ExitX, ExitY);
2444 CloseDoor(DOOR_CLOSE_1);
2449 SaveTape(tape.level_nr); /* Ask to save tape */
2452 if (level_nr == leveldir_current->handicap_level)
2454 leveldir_current->handicap_level++;
2455 SaveLevelSetup_SeriesInfo();
2458 if (level_editor_test_game)
2459 local_player->score = -1; /* no highscore when playing from editor */
2460 else if (level_nr < leveldir_current->last_level)
2461 raise_level = TRUE; /* advance to next level */
2463 if ((hi_pos = NewHiScore()) >= 0)
2465 game_status = GAME_MODE_SCORES;
2466 DrawHallOfFame(hi_pos);
2475 game_status = GAME_MODE_MAIN;
2492 LoadScore(level_nr);
2494 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2495 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2498 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2500 if (local_player->score > highscore[k].Score)
2502 /* player has made it to the hall of fame */
2504 if (k < MAX_SCORE_ENTRIES - 1)
2506 int m = MAX_SCORE_ENTRIES - 1;
2509 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2510 if (!strcmp(setup.player_name, highscore[l].Name))
2512 if (m == k) /* player's new highscore overwrites his old one */
2516 for (l = m; l > k; l--)
2518 strcpy(highscore[l].Name, highscore[l - 1].Name);
2519 highscore[l].Score = highscore[l - 1].Score;
2526 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2527 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2528 highscore[k].Score = local_player->score;
2534 else if (!strncmp(setup.player_name, highscore[k].Name,
2535 MAX_PLAYER_NAME_LEN))
2536 break; /* player already there with a higher score */
2542 SaveScore(level_nr);
2547 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2549 if (player->GfxAction != action || player->GfxDir != dir)
2552 printf("Player frame reset! (%d => %d, %d => %d)\n",
2553 player->GfxAction, action, player->GfxDir, dir);
2556 player->GfxAction = action;
2557 player->GfxDir = dir;
2559 player->StepFrame = 0;
2563 static void ResetRandomAnimationValue(int x, int y)
2565 GfxRandom[x][y] = INIT_GFX_RANDOM();
2568 static void ResetGfxAnimation(int x, int y)
2571 GfxAction[x][y] = ACTION_DEFAULT;
2572 GfxDir[x][y] = MovDir[x][y];
2575 void InitMovingField(int x, int y, int direction)
2577 int element = Feld[x][y];
2578 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2579 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2583 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2584 ResetGfxAnimation(x, y);
2586 MovDir[newx][newy] = MovDir[x][y] = direction;
2587 GfxDir[x][y] = direction;
2589 if (Feld[newx][newy] == EL_EMPTY)
2590 Feld[newx][newy] = EL_BLOCKED;
2592 if (direction == MV_DOWN && CAN_FALL(element))
2593 GfxAction[x][y] = ACTION_FALLING;
2595 GfxAction[x][y] = ACTION_MOVING;
2597 GfxFrame[newx][newy] = GfxFrame[x][y];
2598 GfxRandom[newx][newy] = GfxRandom[x][y];
2599 GfxAction[newx][newy] = GfxAction[x][y];
2600 GfxDir[newx][newy] = GfxDir[x][y];
2603 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2605 int direction = MovDir[x][y];
2606 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2607 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2613 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2615 int oldx = x, oldy = y;
2616 int direction = MovDir[x][y];
2618 if (direction == MV_LEFT)
2620 else if (direction == MV_RIGHT)
2622 else if (direction == MV_UP)
2624 else if (direction == MV_DOWN)
2627 *comes_from_x = oldx;
2628 *comes_from_y = oldy;
2631 int MovingOrBlocked2Element(int x, int y)
2633 int element = Feld[x][y];
2635 if (element == EL_BLOCKED)
2639 Blocked2Moving(x, y, &oldx, &oldy);
2640 return Feld[oldx][oldy];
2646 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2648 /* like MovingOrBlocked2Element(), but if element is moving
2649 and (x,y) is the field the moving element is just leaving,
2650 return EL_BLOCKED instead of the element value */
2651 int element = Feld[x][y];
2653 if (IS_MOVING(x, y))
2655 if (element == EL_BLOCKED)
2659 Blocked2Moving(x, y, &oldx, &oldy);
2660 return Feld[oldx][oldy];
2669 static void RemoveField(int x, int y)
2671 Feld[x][y] = EL_EMPTY;
2678 ChangeDelay[x][y] = 0;
2679 ChangePage[x][y] = -1;
2680 Pushed[x][y] = FALSE;
2683 ExplodeField[x][y] = EX_TYPE_NONE;
2686 GfxElement[x][y] = EL_UNDEFINED;
2687 GfxAction[x][y] = ACTION_DEFAULT;
2688 GfxDir[x][y] = MV_NO_MOVING;
2691 void RemoveMovingField(int x, int y)
2693 int oldx = x, oldy = y, newx = x, newy = y;
2694 int element = Feld[x][y];
2695 int next_element = EL_UNDEFINED;
2697 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2700 if (IS_MOVING(x, y))
2702 Moving2Blocked(x, y, &newx, &newy);
2704 if (Feld[newx][newy] != EL_BLOCKED)
2707 if (Feld[newx][newy] != EL_BLOCKED)
2709 /* element is moving, but target field is not free (blocked), but
2710 already occupied by something different (example: acid pool);
2711 in this case, only remove the moving field, but not the target */
2713 RemoveField(oldx, oldy);
2715 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2717 DrawLevelField(oldx, oldy);
2723 else if (element == EL_BLOCKED)
2725 Blocked2Moving(x, y, &oldx, &oldy);
2726 if (!IS_MOVING(oldx, oldy))
2730 if (element == EL_BLOCKED &&
2731 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2732 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2733 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2734 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2735 next_element = get_next_element(Feld[oldx][oldy]);
2737 RemoveField(oldx, oldy);
2738 RemoveField(newx, newy);
2740 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2742 if (next_element != EL_UNDEFINED)
2743 Feld[oldx][oldy] = next_element;
2745 DrawLevelField(oldx, oldy);
2746 DrawLevelField(newx, newy);
2749 void DrawDynamite(int x, int y)
2751 int sx = SCREENX(x), sy = SCREENY(y);
2752 int graphic = el2img(Feld[x][y]);
2755 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2758 if (IS_WALKABLE_INSIDE(Back[x][y]))
2762 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2763 else if (Store[x][y])
2764 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2766 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2769 if (Back[x][y] || Store[x][y])
2770 DrawGraphicThruMask(sx, sy, graphic, frame);
2772 DrawGraphic(sx, sy, graphic, frame);
2774 if (game.emulation == EMU_SUPAPLEX)
2775 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2776 else if (Store[x][y])
2777 DrawGraphicThruMask(sx, sy, graphic, frame);
2779 DrawGraphic(sx, sy, graphic, frame);
2783 void CheckDynamite(int x, int y)
2785 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2789 if (MovDelay[x][y] != 0)
2792 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2799 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2801 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2802 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2803 StopSound(SND_DYNAMITE_ACTIVE);
2805 StopSound(SND_DYNABOMB_ACTIVE);
2811 void DrawRelocatePlayer(struct PlayerInfo *player)
2813 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2814 boolean no_delay = (tape.warp_forward);
2815 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2816 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2817 int jx = player->jx;
2818 int jy = player->jy;
2820 if (level.instant_relocation)
2823 int offset = (setup.scroll_delay ? 3 : 0);
2825 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2827 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2828 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2829 local_player->jx - MIDPOSX);
2831 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2832 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2833 local_player->jy - MIDPOSY);
2837 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2838 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2839 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2841 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2842 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2843 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2845 /* don't scroll over playfield boundaries */
2846 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2847 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2849 /* don't scroll over playfield boundaries */
2850 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2851 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2854 scroll_x += (local_player->jx - old_jx);
2855 scroll_y += (local_player->jy - old_jy);
2857 /* don't scroll over playfield boundaries */
2858 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2859 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2861 /* don't scroll over playfield boundaries */
2862 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2863 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2866 RedrawPlayfield(TRUE, 0,0,0,0);
2872 int offset = (setup.scroll_delay ? 3 : 0);
2874 int scroll_xx = -999, scroll_yy = -999;
2876 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2878 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2881 int fx = FX, fy = FY;
2883 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2884 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2885 local_player->jx - MIDPOSX);
2887 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2888 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2889 local_player->jy - MIDPOSY);
2891 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2892 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2895 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2898 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2905 fx += dx * TILEX / 2;
2906 fy += dy * TILEY / 2;
2908 ScrollLevel(dx, dy);
2911 /* scroll in two steps of half tile size to make things smoother */
2912 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2914 Delay(wait_delay_value);
2916 /* scroll second step to align at full tile size */
2918 Delay(wait_delay_value);
2921 int scroll_xx = -999, scroll_yy = -999;
2923 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2925 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2928 int fx = FX, fy = FY;
2930 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2931 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2932 local_player->jx - MIDPOSX);
2934 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2935 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2936 local_player->jy - MIDPOSY);
2938 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2939 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2942 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2945 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2952 fx += dx * TILEX / 2;
2953 fy += dy * TILEY / 2;
2955 ScrollLevel(dx, dy);
2958 /* scroll in two steps of half tile size to make things smoother */
2959 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2961 Delay(wait_delay_value);
2963 /* scroll second step to align at full tile size */
2965 Delay(wait_delay_value);
2971 Delay(wait_delay_value);
2975 void RelocatePlayer(int jx, int jy, int el_player_raw)
2978 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2980 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2982 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2983 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2984 boolean no_delay = (tape.warp_forward);
2985 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2986 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2987 int old_jx = player->jx;
2988 int old_jy = player->jy;
2989 int old_element = Feld[old_jx][old_jy];
2990 int element = Feld[jx][jy];
2991 boolean player_relocated = (old_jx != jx || old_jy != jy);
2993 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2994 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2996 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2997 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2998 int leave_side_horiz = move_dir_horiz;
2999 int leave_side_vert = move_dir_vert;
3001 static int trigger_sides[4][2] =
3003 /* enter side leave side */
3004 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3005 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3006 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3007 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3009 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3010 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3011 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3012 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3014 int enter_side = enter_side_horiz | enter_side_vert;
3015 int leave_side = leave_side_horiz | leave_side_vert;
3017 if (player->GameOver) /* do not reanimate dead player */
3020 if (!player_relocated) /* no need to relocate the player */
3023 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3025 RemoveField(jx, jy); /* temporarily remove newly placed player */
3026 DrawLevelField(jx, jy);
3029 if (player->present)
3031 while (player->MovPos)
3033 ScrollPlayer(player, SCROLL_GO_ON);
3034 ScrollScreen(NULL, SCROLL_GO_ON);
3036 #if USE_NEW_MOVE_DELAY
3037 AdvanceFrameAndPlayerCounters(player->index_nr);
3045 Delay(wait_delay_value);
3048 DrawPlayer(player); /* needed here only to cleanup last field */
3049 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3051 player->is_moving = FALSE;
3055 if (IS_CUSTOM_ELEMENT(old_element))
3056 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3058 player->index_bit, leave_side);
3060 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3062 player->index_bit, leave_side);
3065 Feld[jx][jy] = el_player;
3066 InitPlayerField(jx, jy, el_player, TRUE);
3068 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3070 Feld[jx][jy] = element;
3071 InitField(jx, jy, FALSE);
3075 if (player == local_player) /* only visually relocate local player */
3076 DrawRelocatePlayer(player);
3080 TestIfHeroTouchesBadThing(jx, jy);
3081 TestIfPlayerTouchesCustomElement(jx, jy);
3085 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3090 /* needed to allow change of walkable custom element by entering player */
3091 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3092 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3094 /* needed to allow change of walkable custom element by entering player */
3095 Changed[jx][jy] = 0; /* allow another change */
3100 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3101 enter_side == MV_LEFT ? "left" :
3102 enter_side == MV_RIGHT ? "right" :
3103 enter_side == MV_UP ? "top" :
3104 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3108 if (IS_CUSTOM_ELEMENT(element))
3109 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3110 player->index_bit, enter_side);
3112 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3113 CE_OTHER_GETS_ENTERED,
3114 player->index_bit, enter_side);
3118 void Explode(int ex, int ey, int phase, int mode)
3125 /* !!! eliminate this variable !!! */
3126 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3131 int last_phase = num_phase * delay;
3132 int half_phase = (num_phase / 2) * delay;
3133 int first_phase_after_start = EX_PHASE_START + 1;
3137 if (game.explosions_delayed)
3139 ExplodeField[ex][ey] = mode;
3143 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3145 int center_element = Feld[ex][ey];
3148 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3152 /* --- This is only really needed (and now handled) in "Impact()". --- */
3153 /* do not explode moving elements that left the explode field in time */
3154 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3155 center_element == EL_EMPTY &&
3156 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3161 if (mode == EX_TYPE_NORMAL ||
3162 mode == EX_TYPE_CENTER ||
3163 mode == EX_TYPE_CROSS)
3164 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3166 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3167 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3170 /* remove things displayed in background while burning dynamite */
3171 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3174 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3176 /* put moving element to center field (and let it explode there) */
3177 center_element = MovingOrBlocked2Element(ex, ey);
3178 RemoveMovingField(ex, ey);
3179 Feld[ex][ey] = center_element;
3185 last_phase = element_info[center_element].explosion_delay + 1;
3187 last_phase = element_info[center_element].explosion_delay;
3191 printf("::: %d -> %d\n", center_element, last_phase);
3195 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3197 int xx = x - ex + 1;
3198 int yy = y - ey + 1;
3203 if (!IN_LEV_FIELD(x, y) ||
3204 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3205 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3208 if (!IN_LEV_FIELD(x, y) ||
3209 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3213 if (!IN_LEV_FIELD(x, y) ||
3214 ((mode != EX_TYPE_NORMAL ||
3215 center_element == EL_AMOEBA_TO_DIAMOND) &&
3216 (x != ex || y != ey)))
3220 element = Feld[x][y];
3222 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3224 element = MovingOrBlocked2Element(x, y);
3226 if (!IS_EXPLOSION_PROOF(element))
3227 RemoveMovingField(x, y);
3233 if (IS_EXPLOSION_PROOF(element))
3236 /* indestructible elements can only explode in center (but not flames) */
3238 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3239 mode == EX_TYPE_BORDER)) ||
3240 element == EL_FLAMES)
3243 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3244 element == EL_FLAMES)
3250 if ((IS_INDESTRUCTIBLE(element) &&
3251 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3252 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3253 element == EL_FLAMES)
3258 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3259 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3260 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3262 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3265 if (IS_ACTIVE_BOMB(element))
3267 /* re-activate things under the bomb like gate or penguin */
3269 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3272 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3277 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3278 element_info[Feld[x][y]].token_name,
3279 Store[x][y], Store2[x][y]);
3286 /* save walkable background elements while explosion on same tile */
3288 if (IS_INDESTRUCTIBLE(element))
3289 Back[x][y] = element;
3293 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3294 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3295 Back[x][y] = element;
3297 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3298 (x != ex || y != ey))
3299 Back[x][y] = element;
3302 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3303 Back[x][y] = element;
3307 /* ignite explodable elements reached by other explosion */
3308 if (element == EL_EXPLOSION)
3309 element = Store2[x][y];
3312 if (AmoebaNr[x][y] &&
3313 (element == EL_AMOEBA_FULL ||
3314 element == EL_BD_AMOEBA ||
3315 element == EL_AMOEBA_GROWING))
3317 AmoebaCnt[AmoebaNr[x][y]]--;
3318 AmoebaCnt2[AmoebaNr[x][y]]--;
3324 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3326 switch(StorePlayer[ex][ey])
3329 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3332 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3335 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3339 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3344 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3345 Store[x][y] = EL_EMPTY;
3347 if (game.emulation == EMU_SUPAPLEX)
3348 Store[x][y] = EL_EMPTY;
3351 else if (center_element == EL_MOLE)
3352 Store[x][y] = EL_EMERALD_RED;
3353 else if (center_element == EL_PENGUIN)
3354 Store[x][y] = EL_EMERALD_PURPLE;
3355 else if (center_element == EL_BUG)
3356 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3357 else if (center_element == EL_BD_BUTTERFLY)
3358 Store[x][y] = EL_BD_DIAMOND;
3359 else if (center_element == EL_SP_ELECTRON)
3360 Store[x][y] = EL_SP_INFOTRON;
3361 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3362 Store[x][y] = level.amoeba_content;
3363 else if (center_element == EL_YAMYAM)
3364 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3365 else if (IS_CUSTOM_ELEMENT(center_element) &&
3366 element_info[center_element].content[xx][yy] != EL_EMPTY)
3367 Store[x][y] = element_info[center_element].content[xx][yy];
3368 else if (element == EL_WALL_EMERALD)
3369 Store[x][y] = EL_EMERALD;
3370 else if (element == EL_WALL_DIAMOND)
3371 Store[x][y] = EL_DIAMOND;
3372 else if (element == EL_WALL_BD_DIAMOND)
3373 Store[x][y] = EL_BD_DIAMOND;
3374 else if (element == EL_WALL_EMERALD_YELLOW)
3375 Store[x][y] = EL_EMERALD_YELLOW;
3376 else if (element == EL_WALL_EMERALD_RED)
3377 Store[x][y] = EL_EMERALD_RED;
3378 else if (element == EL_WALL_EMERALD_PURPLE)
3379 Store[x][y] = EL_EMERALD_PURPLE;
3380 else if (element == EL_WALL_PEARL)
3381 Store[x][y] = EL_PEARL;
3382 else if (element == EL_WALL_CRYSTAL)
3383 Store[x][y] = EL_CRYSTAL;
3384 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3385 Store[x][y] = element_info[element].content[1][1];
3387 Store[x][y] = EL_EMPTY;
3389 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3390 center_element == EL_AMOEBA_TO_DIAMOND)
3391 Store2[x][y] = element;
3394 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3395 element_info[Store2[x][y]].token_name);
3399 if (AmoebaNr[x][y] &&
3400 (element == EL_AMOEBA_FULL ||
3401 element == EL_BD_AMOEBA ||
3402 element == EL_AMOEBA_GROWING))
3404 AmoebaCnt[AmoebaNr[x][y]]--;
3405 AmoebaCnt2[AmoebaNr[x][y]]--;
3411 MovDir[x][y] = MovPos[x][y] = 0;
3412 GfxDir[x][y] = MovDir[x][y];
3417 Feld[x][y] = EL_EXPLOSION;
3419 GfxElement[x][y] = center_element;
3421 GfxElement[x][y] = EL_UNDEFINED;
3424 ExplodePhase[x][y] = 1;
3426 ExplodeDelay[x][y] = last_phase;
3431 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3433 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3440 if (center_element == EL_YAMYAM)
3441 game.yamyam_content_nr =
3442 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3445 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3446 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3460 GfxFrame[x][y] = 0; /* restart explosion animation */
3464 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3468 last_phase = ExplodeDelay[x][y];
3471 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3475 /* activate this even in non-DEBUG version until cause for crash in
3476 getGraphicAnimationFrame() (see below) is found and eliminated */
3480 if (GfxElement[x][y] == EL_UNDEFINED)
3483 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3484 printf("Explode(): This should never happen!\n");
3487 GfxElement[x][y] = EL_EMPTY;
3493 border_element = Store2[x][y];
3495 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3496 border_element = StorePlayer[x][y];
3498 if (IS_PLAYER(x, y))
3499 border_element = StorePlayer[x][y];
3503 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3504 element_info[border_element].token_name, Store2[x][y]);
3508 printf("::: phase == %d\n", phase);
3511 if (phase == element_info[border_element].ignition_delay ||
3512 phase == last_phase)
3514 boolean border_explosion = FALSE;
3518 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3519 !PLAYER_EXPLOSION_PROTECTED(x, y))
3521 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3524 if (IS_PLAYER(x, y))
3527 KillHeroUnlessExplosionProtected(x, y);
3528 border_explosion = TRUE;
3531 if (phase == last_phase)
3532 printf("::: IS_PLAYER\n");
3535 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3538 printf("::: %d,%d: %d %s\n", x, y, border_element,
3539 element_info[border_element].token_name);
3542 Feld[x][y] = Store2[x][y];
3545 border_explosion = TRUE;
3548 if (phase == last_phase)
3549 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3552 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3554 AmoebeUmwandeln(x, y);
3556 border_explosion = TRUE;
3559 if (phase == last_phase)
3560 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3561 element_info[border_element].explosion_delay,
3562 element_info[border_element].ignition_delay,
3568 /* if an element just explodes due to another explosion (chain-reaction),
3569 do not immediately end the new explosion when it was the last frame of
3570 the explosion (as it would be done in the following "if"-statement!) */
3571 if (border_explosion && phase == last_phase)
3578 if (phase == first_phase_after_start)
3580 int element = Store2[x][y];
3582 if (element == EL_BLACK_ORB)
3584 Feld[x][y] = Store2[x][y];
3589 else if (phase == half_phase)
3591 int element = Store2[x][y];
3593 if (IS_PLAYER(x, y))
3594 KillHeroUnlessExplosionProtected(x, y);
3595 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3597 Feld[x][y] = Store2[x][y];
3601 else if (element == EL_AMOEBA_TO_DIAMOND)
3602 AmoebeUmwandeln(x, y);
3606 if (phase == last_phase)
3611 printf("::: done: phase == %d\n", phase);
3615 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3618 element = Feld[x][y] = Store[x][y];
3619 Store[x][y] = Store2[x][y] = 0;
3620 GfxElement[x][y] = EL_UNDEFINED;
3622 /* player can escape from explosions and might therefore be still alive */
3623 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3624 element <= EL_PLAYER_IS_EXPLODING_4)
3625 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3627 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3628 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3629 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3632 /* restore probably existing indestructible background element */
3633 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3634 element = Feld[x][y] = Back[x][y];
3637 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3638 GfxDir[x][y] = MV_NO_MOVING;
3639 ChangeDelay[x][y] = 0;
3640 ChangePage[x][y] = -1;
3643 InitField_WithBug2(x, y, FALSE);
3645 InitField(x, y, FALSE);
3647 /* !!! not needed !!! */
3649 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3650 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3653 if (CAN_MOVE(element))
3658 DrawLevelField(x, y);
3660 TestIfElementTouchesCustomElement(x, y);
3662 if (GFX_CRUMBLED(element))
3663 DrawLevelFieldCrumbledSandNeighbours(x, y);
3665 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3666 StorePlayer[x][y] = 0;
3668 if (ELEM_IS_PLAYER(element))
3669 RelocatePlayer(x, y, element);
3672 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3674 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3678 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3680 int stored = Store[x][y];
3681 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3682 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3686 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3688 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3692 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3696 printf("::: %d / %d [%d - %d]\n",
3697 GfxFrame[x][y], phase - delay, phase, delay);
3701 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3702 element_info[GfxElement[x][y]].token_name,
3707 DrawLevelFieldCrumbledSand(x, y);
3709 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3711 DrawLevelElement(x, y, Back[x][y]);
3712 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3714 else if (IS_WALKABLE_UNDER(Back[x][y]))
3716 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3717 DrawLevelElementThruMask(x, y, Back[x][y]);
3719 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3720 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3724 void DynaExplode(int ex, int ey)
3727 int dynabomb_element = Feld[ex][ey];
3728 int dynabomb_size = 1;
3729 boolean dynabomb_xl = FALSE;
3730 struct PlayerInfo *player;
3731 static int xy[4][2] =
3739 if (IS_ACTIVE_BOMB(dynabomb_element))
3741 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3742 dynabomb_size = player->dynabomb_size;
3743 dynabomb_xl = player->dynabomb_xl;
3744 player->dynabombs_left++;
3747 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3749 for (i = 0; i < NUM_DIRECTIONS; i++)
3751 for (j = 1; j <= dynabomb_size; j++)
3753 int x = ex + j * xy[i][0];
3754 int y = ey + j * xy[i][1];
3757 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3760 element = Feld[x][y];
3762 /* do not restart explosions of fields with active bombs */
3763 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3766 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3770 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3771 !IS_DIGGABLE(element) && !dynabomb_xl)
3774 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3775 !CAN_GROW_INTO(element) && !dynabomb_xl)
3779 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3780 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3781 element != EL_SAND && !dynabomb_xl)
3788 void Bang(int x, int y)
3791 int element = MovingOrBlocked2Element(x, y);
3793 int element = Feld[x][y];
3797 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3799 if (IS_PLAYER(x, y))
3802 struct PlayerInfo *player = PLAYERINFO(x, y);
3804 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3805 player->element_nr);
3810 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3812 if (game.emulation == EMU_SUPAPLEX)
3813 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3815 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3820 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3828 case EL_BD_BUTTERFLY:
3831 case EL_DARK_YAMYAM:
3835 RaiseScoreElement(element);
3836 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3838 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3839 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3840 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3841 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3842 case EL_DYNABOMB_INCREASE_NUMBER:
3843 case EL_DYNABOMB_INCREASE_SIZE:
3844 case EL_DYNABOMB_INCREASE_POWER:
3849 case EL_LAMP_ACTIVE:
3851 case EL_AMOEBA_TO_DIAMOND:
3853 if (IS_PLAYER(x, y))
3854 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3856 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3860 if (element_info[element].explosion_type == EXPLODES_CROSS)
3862 if (CAN_EXPLODE_CROSS(element))
3865 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3870 else if (element_info[element].explosion_type == EXPLODES_1X1)
3872 else if (CAN_EXPLODE_1X1(element))
3874 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3876 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3880 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3883 void SplashAcid(int x, int y)
3886 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3887 (!IN_LEV_FIELD(x - 1, y - 2) ||
3888 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3889 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3891 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3892 (!IN_LEV_FIELD(x + 1, y - 2) ||
3893 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3894 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3896 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3898 /* input: position of element entering acid (obsolete) */
3900 int element = Feld[x][y];
3902 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3905 if (element != EL_ACID_SPLASH_LEFT &&
3906 element != EL_ACID_SPLASH_RIGHT)
3908 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3910 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3911 (!IN_LEV_FIELD(x - 1, y - 1) ||
3912 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3913 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3915 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3916 (!IN_LEV_FIELD(x + 1, y - 1) ||
3917 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3918 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3923 static void InitBeltMovement()
3925 static int belt_base_element[4] =
3927 EL_CONVEYOR_BELT_1_LEFT,
3928 EL_CONVEYOR_BELT_2_LEFT,
3929 EL_CONVEYOR_BELT_3_LEFT,
3930 EL_CONVEYOR_BELT_4_LEFT
3932 static int belt_base_active_element[4] =
3934 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3935 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3936 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3937 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3942 /* set frame order for belt animation graphic according to belt direction */
3943 for (i = 0; i < NUM_BELTS; i++)
3947 for (j = 0; j < NUM_BELT_PARTS; j++)
3949 int element = belt_base_active_element[belt_nr] + j;
3950 int graphic = el2img(element);
3952 if (game.belt_dir[i] == MV_LEFT)
3953 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3955 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3959 for (y = 0; y < lev_fieldy; y++)
3961 for (x = 0; x < lev_fieldx; x++)
3963 int element = Feld[x][y];
3965 for (i = 0; i < NUM_BELTS; i++)
3967 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3969 int e_belt_nr = getBeltNrFromBeltElement(element);
3972 if (e_belt_nr == belt_nr)
3974 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3976 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3984 static void ToggleBeltSwitch(int x, int y)
3986 static int belt_base_element[4] =
3988 EL_CONVEYOR_BELT_1_LEFT,
3989 EL_CONVEYOR_BELT_2_LEFT,
3990 EL_CONVEYOR_BELT_3_LEFT,
3991 EL_CONVEYOR_BELT_4_LEFT
3993 static int belt_base_active_element[4] =
3995 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3996 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3997 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3998 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4000 static int belt_base_switch_element[4] =
4002 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4003 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4004 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4005 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4007 static int belt_move_dir[4] =
4015 int element = Feld[x][y];
4016 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4017 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4018 int belt_dir = belt_move_dir[belt_dir_nr];
4021 if (!IS_BELT_SWITCH(element))
4024 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4025 game.belt_dir[belt_nr] = belt_dir;
4027 if (belt_dir_nr == 3)
4030 /* set frame order for belt animation graphic according to belt direction */
4031 for (i = 0; i < NUM_BELT_PARTS; i++)
4033 int element = belt_base_active_element[belt_nr] + i;
4034 int graphic = el2img(element);
4036 if (belt_dir == MV_LEFT)
4037 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4039 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4042 for (yy = 0; yy < lev_fieldy; yy++)
4044 for (xx = 0; xx < lev_fieldx; xx++)
4046 int element = Feld[xx][yy];
4048 if (IS_BELT_SWITCH(element))
4050 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4052 if (e_belt_nr == belt_nr)
4054 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4055 DrawLevelField(xx, yy);
4058 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4060 int e_belt_nr = getBeltNrFromBeltElement(element);
4062 if (e_belt_nr == belt_nr)
4064 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4066 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4067 DrawLevelField(xx, yy);
4070 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4072 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4074 if (e_belt_nr == belt_nr)
4076 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4078 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4079 DrawLevelField(xx, yy);
4086 static void ToggleSwitchgateSwitch(int x, int y)
4090 game.switchgate_pos = !game.switchgate_pos;
4092 for (yy = 0; yy < lev_fieldy; yy++)
4094 for (xx = 0; xx < lev_fieldx; xx++)
4096 int element = Feld[xx][yy];
4098 if (element == EL_SWITCHGATE_SWITCH_UP ||
4099 element == EL_SWITCHGATE_SWITCH_DOWN)
4101 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4102 DrawLevelField(xx, yy);
4104 else if (element == EL_SWITCHGATE_OPEN ||
4105 element == EL_SWITCHGATE_OPENING)
4107 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4109 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4111 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4114 else if (element == EL_SWITCHGATE_CLOSED ||
4115 element == EL_SWITCHGATE_CLOSING)
4117 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4119 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4121 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4128 static int getInvisibleActiveFromInvisibleElement(int element)
4130 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4131 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4132 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4136 static int getInvisibleFromInvisibleActiveElement(int element)
4138 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4139 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4140 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4144 static void RedrawAllLightSwitchesAndInvisibleElements()
4148 for (y = 0; y < lev_fieldy; y++)
4150 for (x = 0; x < lev_fieldx; x++)
4152 int element = Feld[x][y];
4154 if (element == EL_LIGHT_SWITCH &&
4155 game.light_time_left > 0)
4157 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4158 DrawLevelField(x, y);
4160 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4161 game.light_time_left == 0)
4163 Feld[x][y] = EL_LIGHT_SWITCH;
4164 DrawLevelField(x, y);
4166 else if (element == EL_INVISIBLE_STEELWALL ||
4167 element == EL_INVISIBLE_WALL ||
4168 element == EL_INVISIBLE_SAND)
4170 if (game.light_time_left > 0)
4171 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4173 DrawLevelField(x, y);
4175 /* uncrumble neighbour fields, if needed */
4176 if (element == EL_INVISIBLE_SAND)
4177 DrawLevelFieldCrumbledSandNeighbours(x, y);
4179 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4180 element == EL_INVISIBLE_WALL_ACTIVE ||
4181 element == EL_INVISIBLE_SAND_ACTIVE)
4183 if (game.light_time_left == 0)
4184 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4186 DrawLevelField(x, y);
4188 /* re-crumble neighbour fields, if needed */
4189 if (element == EL_INVISIBLE_SAND)
4190 DrawLevelFieldCrumbledSandNeighbours(x, y);
4196 static void ToggleLightSwitch(int x, int y)
4198 int element = Feld[x][y];
4200 game.light_time_left =
4201 (element == EL_LIGHT_SWITCH ?
4202 level.time_light * FRAMES_PER_SECOND : 0);
4204 RedrawAllLightSwitchesAndInvisibleElements();
4207 static void ActivateTimegateSwitch(int x, int y)
4211 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4213 for (yy = 0; yy < lev_fieldy; yy++)
4215 for (xx = 0; xx < lev_fieldx; xx++)
4217 int element = Feld[xx][yy];
4219 if (element == EL_TIMEGATE_CLOSED ||
4220 element == EL_TIMEGATE_CLOSING)
4222 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4223 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4227 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4229 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4230 DrawLevelField(xx, yy);
4237 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4240 inline static int getElementMoveStepsize(int x, int y)
4242 int element = Feld[x][y];
4243 int direction = MovDir[x][y];
4244 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4245 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4246 int horiz_move = (dx != 0);
4247 int sign = (horiz_move ? dx : dy);
4248 int step = sign * element_info[element].move_stepsize;
4250 /* special values for move stepsize for spring and things on conveyor belt */
4254 if (element == EL_SPRING)
4255 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4256 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4257 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4258 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4260 if (CAN_FALL(element) &&
4261 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4262 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4263 else if (element == EL_SPRING)
4264 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4271 void Impact(int x, int y)
4273 boolean lastline = (y == lev_fieldy-1);
4274 boolean object_hit = FALSE;
4275 boolean impact = (lastline || object_hit);
4276 int element = Feld[x][y];
4277 int smashed = EL_STEELWALL;
4279 if (!lastline) /* check if element below was hit */
4281 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4284 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4285 MovDir[x][y + 1] != MV_DOWN ||
4286 MovPos[x][y + 1] <= TILEY / 2));
4289 object_hit = !IS_FREE(x, y + 1);
4292 /* do not smash moving elements that left the smashed field in time */
4293 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4294 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4298 smashed = MovingOrBlocked2Element(x, y + 1);
4300 impact = (lastline || object_hit);
4303 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4305 SplashAcid(x, y + 1);
4309 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4310 /* only reset graphic animation if graphic really changes after impact */
4312 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4314 ResetGfxAnimation(x, y);
4315 DrawLevelField(x, y);
4318 if (impact && CAN_EXPLODE_IMPACT(element))
4323 else if (impact && element == EL_PEARL)
4325 ResetGfxAnimation(x, y);
4327 Feld[x][y] = EL_PEARL_BREAKING;
4328 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4331 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4333 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4338 if (impact && element == EL_AMOEBA_DROP)
4340 if (object_hit && IS_PLAYER(x, y + 1))
4341 KillHeroUnlessEnemyProtected(x, y + 1);
4342 else if (object_hit && smashed == EL_PENGUIN)
4346 Feld[x][y] = EL_AMOEBA_GROWING;
4347 Store[x][y] = EL_AMOEBA_WET;
4349 ResetRandomAnimationValue(x, y);
4354 if (object_hit) /* check which object was hit */
4356 if (CAN_PASS_MAGIC_WALL(element) &&
4357 (smashed == EL_MAGIC_WALL ||
4358 smashed == EL_BD_MAGIC_WALL))
4361 int activated_magic_wall =
4362 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4363 EL_BD_MAGIC_WALL_ACTIVE);
4365 /* activate magic wall / mill */
4366 for (yy = 0; yy < lev_fieldy; yy++)
4367 for (xx = 0; xx < lev_fieldx; xx++)
4368 if (Feld[xx][yy] == smashed)
4369 Feld[xx][yy] = activated_magic_wall;
4371 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4372 game.magic_wall_active = TRUE;
4374 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4375 SND_MAGIC_WALL_ACTIVATING :
4376 SND_BD_MAGIC_WALL_ACTIVATING));
4379 if (IS_PLAYER(x, y + 1))
4381 if (CAN_SMASH_PLAYER(element))
4383 KillHeroUnlessEnemyProtected(x, y + 1);
4387 else if (smashed == EL_PENGUIN)
4389 if (CAN_SMASH_PLAYER(element))
4395 else if (element == EL_BD_DIAMOND)
4397 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4403 else if (((element == EL_SP_INFOTRON ||
4404 element == EL_SP_ZONK) &&
4405 (smashed == EL_SP_SNIKSNAK ||
4406 smashed == EL_SP_ELECTRON ||
4407 smashed == EL_SP_DISK_ORANGE)) ||
4408 (element == EL_SP_INFOTRON &&
4409 smashed == EL_SP_DISK_YELLOW))
4415 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4421 else if (CAN_SMASH_EVERYTHING(element))
4423 if (IS_CLASSIC_ENEMY(smashed) ||
4424 CAN_EXPLODE_SMASHED(smashed))
4429 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4431 if (smashed == EL_LAMP ||
4432 smashed == EL_LAMP_ACTIVE)
4437 else if (smashed == EL_NUT)
4439 Feld[x][y + 1] = EL_NUT_BREAKING;
4440 PlayLevelSound(x, y, SND_NUT_BREAKING);
4441 RaiseScoreElement(EL_NUT);
4444 else if (smashed == EL_PEARL)
4446 ResetGfxAnimation(x, y);
4448 Feld[x][y + 1] = EL_PEARL_BREAKING;
4449 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4452 else if (smashed == EL_DIAMOND)
4454 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4455 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4458 else if (IS_BELT_SWITCH(smashed))
4460 ToggleBeltSwitch(x, y + 1);
4462 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4463 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4465 ToggleSwitchgateSwitch(x, y + 1);
4467 else if (smashed == EL_LIGHT_SWITCH ||
4468 smashed == EL_LIGHT_SWITCH_ACTIVE)
4470 ToggleLightSwitch(x, y + 1);
4475 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4478 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4481 /* !!! TEST ONLY !!! */
4482 CheckElementChangeBySide(x, y + 1, smashed, element,
4483 CE_SWITCHED, CH_SIDE_TOP);
4484 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4485 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4487 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4488 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4489 CheckElementChangeBySide(x, y + 1, smashed, element,
4490 CE_SWITCHED, CH_SIDE_TOP);
4496 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4501 /* play sound of magic wall / mill */
4503 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4504 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4506 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4507 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4508 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4509 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4514 /* play sound of object that hits the ground */
4515 if (lastline || object_hit)
4516 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4519 inline static void TurnRoundExt(int x, int y)
4531 { 0, 0 }, { 0, 0 }, { 0, 0 },
4536 int left, right, back;
4540 { MV_DOWN, MV_UP, MV_RIGHT },
4541 { MV_UP, MV_DOWN, MV_LEFT },
4543 { MV_LEFT, MV_RIGHT, MV_DOWN },
4547 { MV_RIGHT, MV_LEFT, MV_UP }
4550 int element = Feld[x][y];
4551 int move_pattern = element_info[element].move_pattern;
4553 int old_move_dir = MovDir[x][y];
4554 int left_dir = turn[old_move_dir].left;
4555 int right_dir = turn[old_move_dir].right;
4556 int back_dir = turn[old_move_dir].back;
4558 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4559 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4560 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4561 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4563 int left_x = x + left_dx, left_y = y + left_dy;
4564 int right_x = x + right_dx, right_y = y + right_dy;
4565 int move_x = x + move_dx, move_y = y + move_dy;
4569 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4571 TestIfBadThingTouchesOtherBadThing(x, y);
4573 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4574 MovDir[x][y] = right_dir;
4575 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4576 MovDir[x][y] = left_dir;
4578 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4580 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4584 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4585 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4587 TestIfBadThingTouchesOtherBadThing(x, y);
4589 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4590 MovDir[x][y] = left_dir;
4591 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4592 MovDir[x][y] = right_dir;
4594 if ((element == EL_SPACESHIP ||
4595 element == EL_SP_SNIKSNAK ||
4596 element == EL_SP_ELECTRON)
4597 && MovDir[x][y] != old_move_dir)
4599 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4603 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4605 TestIfBadThingTouchesOtherBadThing(x, y);
4607 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4608 MovDir[x][y] = left_dir;
4609 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4610 MovDir[x][y] = right_dir;
4612 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4614 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4617 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4619 TestIfBadThingTouchesOtherBadThing(x, y);
4621 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4622 MovDir[x][y] = left_dir;
4623 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4624 MovDir[x][y] = right_dir;
4626 if (MovDir[x][y] != old_move_dir)
4630 else if (element == EL_YAMYAM)
4632 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4633 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4635 if (can_turn_left && can_turn_right)
4636 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4637 else if (can_turn_left)
4638 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4639 else if (can_turn_right)
4640 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4642 MovDir[x][y] = back_dir;
4644 MovDelay[x][y] = 16 + 16 * RND(3);
4646 else if (element == EL_DARK_YAMYAM)
4648 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4650 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4653 if (can_turn_left && can_turn_right)
4654 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4655 else if (can_turn_left)
4656 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4657 else if (can_turn_right)
4658 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4660 MovDir[x][y] = back_dir;
4662 MovDelay[x][y] = 16 + 16 * RND(3);
4664 else if (element == EL_PACMAN)
4666 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4667 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4669 if (can_turn_left && can_turn_right)
4670 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4671 else if (can_turn_left)
4672 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4673 else if (can_turn_right)
4674 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4676 MovDir[x][y] = back_dir;
4678 MovDelay[x][y] = 6 + RND(40);
4680 else if (element == EL_PIG)
4682 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4683 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4684 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4685 boolean should_turn_left, should_turn_right, should_move_on;
4687 int rnd = RND(rnd_value);
4689 should_turn_left = (can_turn_left &&
4691 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4692 y + back_dy + left_dy)));
4693 should_turn_right = (can_turn_right &&
4695 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4696 y + back_dy + right_dy)));
4697 should_move_on = (can_move_on &&
4700 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4701 y + move_dy + left_dy) ||
4702 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4703 y + move_dy + right_dy)));
4705 if (should_turn_left || should_turn_right || should_move_on)
4707 if (should_turn_left && should_turn_right && should_move_on)
4708 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4709 rnd < 2 * rnd_value / 3 ? right_dir :
4711 else if (should_turn_left && should_turn_right)
4712 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4713 else if (should_turn_left && should_move_on)
4714 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4715 else if (should_turn_right && should_move_on)
4716 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4717 else if (should_turn_left)
4718 MovDir[x][y] = left_dir;
4719 else if (should_turn_right)
4720 MovDir[x][y] = right_dir;
4721 else if (should_move_on)
4722 MovDir[x][y] = old_move_dir;
4724 else if (can_move_on && rnd > rnd_value / 8)
4725 MovDir[x][y] = old_move_dir;
4726 else if (can_turn_left && can_turn_right)
4727 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4728 else if (can_turn_left && rnd > rnd_value / 8)
4729 MovDir[x][y] = left_dir;
4730 else if (can_turn_right && rnd > rnd_value/8)
4731 MovDir[x][y] = right_dir;
4733 MovDir[x][y] = back_dir;
4735 xx = x + move_xy[MovDir[x][y]].x;
4736 yy = y + move_xy[MovDir[x][y]].y;
4739 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4740 if (!IN_LEV_FIELD(xx, yy) ||
4741 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4742 MovDir[x][y] = old_move_dir;
4744 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4745 MovDir[x][y] = old_move_dir;
4750 else if (element == EL_DRAGON)
4752 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4753 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4754 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4756 int rnd = RND(rnd_value);
4759 if (FrameCounter < 1 && x == 0 && y == 29)
4760 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4763 if (can_move_on && rnd > rnd_value / 8)
4764 MovDir[x][y] = old_move_dir;
4765 else if (can_turn_left && can_turn_right)
4766 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4767 else if (can_turn_left && rnd > rnd_value / 8)
4768 MovDir[x][y] = left_dir;
4769 else if (can_turn_right && rnd > rnd_value / 8)
4770 MovDir[x][y] = right_dir;
4772 MovDir[x][y] = back_dir;
4774 xx = x + move_xy[MovDir[x][y]].x;
4775 yy = y + move_xy[MovDir[x][y]].y;
4778 if (FrameCounter < 1 && x == 0 && y == 29)
4779 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4780 xx, yy, Feld[xx][yy],
4785 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4786 MovDir[x][y] = old_move_dir;
4788 if (!IS_FREE(xx, yy))
4789 MovDir[x][y] = old_move_dir;
4793 if (FrameCounter < 1 && x == 0 && y == 29)
4794 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4799 else if (element == EL_MOLE)
4801 boolean can_move_on =
4802 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4803 IS_AMOEBOID(Feld[move_x][move_y]) ||
4804 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4807 boolean can_turn_left =
4808 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4809 IS_AMOEBOID(Feld[left_x][left_y])));
4811 boolean can_turn_right =
4812 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4813 IS_AMOEBOID(Feld[right_x][right_y])));
4815 if (can_turn_left && can_turn_right)
4816 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4817 else if (can_turn_left)
4818 MovDir[x][y] = left_dir;
4820 MovDir[x][y] = right_dir;
4823 if (MovDir[x][y] != old_move_dir)
4826 else if (element == EL_BALLOON)
4828 MovDir[x][y] = game.balloon_dir;
4831 else if (element == EL_SPRING)
4834 if (MovDir[x][y] & MV_HORIZONTAL &&
4835 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4836 MovDir[x][y] = MV_NO_MOVING;
4838 if (MovDir[x][y] & MV_HORIZONTAL &&
4839 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4840 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4841 MovDir[x][y] = MV_NO_MOVING;
4846 else if (element == EL_ROBOT ||
4847 element == EL_SATELLITE ||
4848 element == EL_PENGUIN)
4850 int attr_x = -1, attr_y = -1;
4861 for (i = 0; i < MAX_PLAYERS; i++)
4863 struct PlayerInfo *player = &stored_player[i];
4864 int jx = player->jx, jy = player->jy;
4866 if (!player->active)
4870 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4879 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4880 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4881 game.engine_version < VERSION_IDENT(3,1,0,0)))
4883 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4890 if (element == EL_PENGUIN)
4893 static int xy[4][2] =
4901 for (i = 0; i < NUM_DIRECTIONS; i++)
4903 int ex = x + xy[i][0];
4904 int ey = y + xy[i][1];
4906 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4915 MovDir[x][y] = MV_NO_MOVING;
4917 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4918 else if (attr_x > x)
4919 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4921 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4922 else if (attr_y > y)
4923 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4925 if (element == EL_ROBOT)
4929 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4930 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4931 Moving2Blocked(x, y, &newx, &newy);
4933 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4934 MovDelay[x][y] = 8 + 8 * !RND(3);
4936 MovDelay[x][y] = 16;
4938 else if (element == EL_PENGUIN)
4944 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4946 boolean first_horiz = RND(2);
4947 int new_move_dir = MovDir[x][y];
4950 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4951 Moving2Blocked(x, y, &newx, &newy);
4953 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4957 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4958 Moving2Blocked(x, y, &newx, &newy);
4960 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4963 MovDir[x][y] = old_move_dir;
4967 else /* (element == EL_SATELLITE) */
4973 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4975 boolean first_horiz = RND(2);
4976 int new_move_dir = MovDir[x][y];
4979 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4980 Moving2Blocked(x, y, &newx, &newy);
4982 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4986 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4987 Moving2Blocked(x, y, &newx, &newy);
4989 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4992 MovDir[x][y] = old_move_dir;
4997 else if (move_pattern == MV_TURNING_LEFT ||
4998 move_pattern == MV_TURNING_RIGHT ||
4999 move_pattern == MV_TURNING_LEFT_RIGHT ||
5000 move_pattern == MV_TURNING_RIGHT_LEFT ||
5001 move_pattern == MV_TURNING_RANDOM ||
5002 move_pattern == MV_ALL_DIRECTIONS)
5004 boolean can_turn_left =
5005 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5006 boolean can_turn_right =
5007 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5009 if (move_pattern == MV_TURNING_LEFT)
5010 MovDir[x][y] = left_dir;
5011 else if (move_pattern == MV_TURNING_RIGHT)
5012 MovDir[x][y] = right_dir;
5013 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5014 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5015 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5016 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5017 else if (move_pattern == MV_TURNING_RANDOM)
5018 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5019 can_turn_right && !can_turn_left ? right_dir :
5020 RND(2) ? left_dir : right_dir);
5021 else if (can_turn_left && can_turn_right)
5022 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5023 else if (can_turn_left)
5024 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5025 else if (can_turn_right)
5026 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5028 MovDir[x][y] = back_dir;
5030 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5032 else if (move_pattern == MV_HORIZONTAL ||
5033 move_pattern == MV_VERTICAL)
5035 if (move_pattern & old_move_dir)
5036 MovDir[x][y] = back_dir;
5037 else if (move_pattern == MV_HORIZONTAL)
5038 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5039 else if (move_pattern == MV_VERTICAL)
5040 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5042 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5044 else if (move_pattern & MV_ANY_DIRECTION)
5046 MovDir[x][y] = move_pattern;
5047 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5049 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5051 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5052 MovDir[x][y] = left_dir;
5053 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5054 MovDir[x][y] = right_dir;
5056 if (MovDir[x][y] != old_move_dir)
5057 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5059 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5061 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5062 MovDir[x][y] = right_dir;
5063 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5064 MovDir[x][y] = left_dir;
5066 if (MovDir[x][y] != old_move_dir)
5067 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5069 else if (move_pattern == MV_TOWARDS_PLAYER ||
5070 move_pattern == MV_AWAY_FROM_PLAYER)
5072 int attr_x = -1, attr_y = -1;
5074 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5085 for (i = 0; i < MAX_PLAYERS; i++)
5087 struct PlayerInfo *player = &stored_player[i];
5088 int jx = player->jx, jy = player->jy;
5090 if (!player->active)
5094 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5102 MovDir[x][y] = MV_NO_MOVING;
5104 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5105 else if (attr_x > x)
5106 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5108 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5109 else if (attr_y > y)
5110 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5112 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5114 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5116 boolean first_horiz = RND(2);
5117 int new_move_dir = MovDir[x][y];
5120 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5121 Moving2Blocked(x, y, &newx, &newy);
5123 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5127 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5128 Moving2Blocked(x, y, &newx, &newy);
5130 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5133 MovDir[x][y] = old_move_dir;
5136 else if (move_pattern == MV_WHEN_PUSHED ||
5137 move_pattern == MV_WHEN_DROPPED)
5139 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5140 MovDir[x][y] = MV_NO_MOVING;
5144 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5146 static int test_xy[7][2] =
5156 static int test_dir[7] =
5166 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5167 int move_preference = -1000000; /* start with very low preference */
5168 int new_move_dir = MV_NO_MOVING;
5169 int start_test = RND(4);
5172 for (i = 0; i < NUM_DIRECTIONS; i++)
5174 int move_dir = test_dir[start_test + i];
5175 int move_dir_preference;
5177 xx = x + test_xy[start_test + i][0];
5178 yy = y + test_xy[start_test + i][1];
5180 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5181 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5183 new_move_dir = move_dir;
5188 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5191 move_dir_preference = -1 * RunnerVisit[xx][yy];
5192 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5193 move_dir_preference = PlayerVisit[xx][yy];
5195 if (move_dir_preference > move_preference)
5197 /* prefer field that has not been visited for the longest time */
5198 move_preference = move_dir_preference;
5199 new_move_dir = move_dir;
5201 else if (move_dir_preference == move_preference &&
5202 move_dir == old_move_dir)
5204 /* prefer last direction when all directions are preferred equally */
5205 move_preference = move_dir_preference;
5206 new_move_dir = move_dir;
5210 MovDir[x][y] = new_move_dir;
5211 if (old_move_dir != new_move_dir)
5214 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5222 static void TurnRound(int x, int y)
5224 int direction = MovDir[x][y];
5227 GfxDir[x][y] = MovDir[x][y];
5233 GfxDir[x][y] = MovDir[x][y];
5236 if (direction != MovDir[x][y])
5241 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5244 GfxAction[x][y] = ACTION_WAITING;
5248 static boolean JustBeingPushed(int x, int y)
5252 for (i = 0; i < MAX_PLAYERS; i++)
5254 struct PlayerInfo *player = &stored_player[i];
5256 if (player->active && player->is_pushing && player->MovPos)
5258 int next_jx = player->jx + (player->jx - player->last_jx);
5259 int next_jy = player->jy + (player->jy - player->last_jy);
5261 if (x == next_jx && y == next_jy)
5269 void StartMoving(int x, int y)
5272 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5274 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5275 int element = Feld[x][y];
5281 if (MovDelay[x][y] == 0)
5282 GfxAction[x][y] = ACTION_DEFAULT;
5284 /* !!! this should be handled more generic (not only for mole) !!! */
5285 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5286 GfxAction[x][y] = ACTION_DEFAULT;
5289 if (CAN_FALL(element) && y < lev_fieldy - 1)
5291 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5292 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5293 if (JustBeingPushed(x, y))
5296 if (element == EL_QUICKSAND_FULL)
5298 if (IS_FREE(x, y + 1))
5300 InitMovingField(x, y, MV_DOWN);
5301 started_moving = TRUE;
5303 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5304 Store[x][y] = EL_ROCK;
5306 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5308 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5311 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5313 if (!MovDelay[x][y])
5314 MovDelay[x][y] = TILEY + 1;
5323 Feld[x][y] = EL_QUICKSAND_EMPTY;
5324 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5325 Store[x][y + 1] = Store[x][y];
5328 PlayLevelSoundAction(x, y, ACTION_FILLING);
5330 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5334 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5335 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5337 InitMovingField(x, y, MV_DOWN);
5338 started_moving = TRUE;
5340 Feld[x][y] = EL_QUICKSAND_FILLING;
5341 Store[x][y] = element;
5343 PlayLevelSoundAction(x, y, ACTION_FILLING);
5345 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5348 else if (element == EL_MAGIC_WALL_FULL)
5350 if (IS_FREE(x, y + 1))
5352 InitMovingField(x, y, MV_DOWN);
5353 started_moving = TRUE;
5355 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5356 Store[x][y] = EL_CHANGED(Store[x][y]);
5358 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5360 if (!MovDelay[x][y])
5361 MovDelay[x][y] = TILEY/4 + 1;
5370 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5371 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5372 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5376 else if (element == EL_BD_MAGIC_WALL_FULL)
5378 if (IS_FREE(x, y + 1))
5380 InitMovingField(x, y, MV_DOWN);
5381 started_moving = TRUE;
5383 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5384 Store[x][y] = EL_CHANGED2(Store[x][y]);
5386 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5388 if (!MovDelay[x][y])
5389 MovDelay[x][y] = TILEY/4 + 1;
5398 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5399 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5400 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5404 else if (CAN_PASS_MAGIC_WALL(element) &&
5405 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5406 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5408 InitMovingField(x, y, MV_DOWN);
5409 started_moving = TRUE;
5412 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5413 EL_BD_MAGIC_WALL_FILLING);
5414 Store[x][y] = element;
5417 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5419 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5422 SplashAcid(x, y + 1);
5424 InitMovingField(x, y, MV_DOWN);
5425 started_moving = TRUE;
5427 Store[x][y] = EL_ACID;
5429 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5430 GfxAction[x][y + 1] = ACTION_ACTIVE;
5434 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5435 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5437 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5438 CAN_SMASH(element) && WasJustFalling[x][y] &&
5439 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5441 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5442 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5443 (Feld[x][y + 1] == EL_BLOCKED)))
5447 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5448 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5449 WasJustMoving[x][y] && !Pushed[x][y + 1])
5451 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5452 WasJustMoving[x][y])
5457 /* this is needed for a special case not covered by calling "Impact()"
5458 from "ContinueMoving()": if an element moves to a tile directly below
5459 another element which was just falling on that tile (which was empty
5460 in the previous frame), the falling element above would just stop
5461 instead of smashing the element below (in previous version, the above
5462 element was just checked for "moving" instead of "falling", resulting
5463 in incorrect smashes caused by horizontal movement of the above
5464 element; also, the case of the player being the element to smash was
5465 simply not covered here... :-/ ) */
5468 WasJustMoving[x][y] = 0;
5469 WasJustFalling[x][y] = 0;
5472 CheckCollision[x][y] = 0;
5475 if (IS_PLAYER(x, y + 1))
5476 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5481 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5483 if (MovDir[x][y] == MV_NO_MOVING)
5485 InitMovingField(x, y, MV_DOWN);
5486 started_moving = TRUE;
5489 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5491 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5492 MovDir[x][y] = MV_DOWN;
5494 InitMovingField(x, y, MV_DOWN);
5495 started_moving = TRUE;
5497 else if (element == EL_AMOEBA_DROP)
5499 Feld[x][y] = EL_AMOEBA_GROWING;
5500 Store[x][y] = EL_AMOEBA_WET;
5502 /* Store[x][y + 1] must be zero, because:
5503 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5506 #if OLD_GAME_BEHAVIOUR
5507 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5509 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5510 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5511 element != EL_DX_SUPABOMB)
5514 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5515 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5516 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5517 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5520 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5521 (IS_FREE(x - 1, y + 1) ||
5522 Feld[x - 1][y + 1] == EL_ACID));
5523 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5524 (IS_FREE(x + 1, y + 1) ||
5525 Feld[x + 1][y + 1] == EL_ACID));
5526 boolean can_fall_any = (can_fall_left || can_fall_right);
5527 boolean can_fall_both = (can_fall_left && can_fall_right);
5529 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5531 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5533 if (slippery_type == SLIPPERY_ONLY_LEFT)
5534 can_fall_right = FALSE;
5535 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5536 can_fall_left = FALSE;
5537 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5538 can_fall_right = FALSE;
5539 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5540 can_fall_left = FALSE;
5542 can_fall_any = (can_fall_left || can_fall_right);
5543 can_fall_both = (can_fall_left && can_fall_right);
5546 #if USE_NEW_SP_SLIPPERY
5547 /* !!! better use the same properties as for custom elements here !!! */
5548 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5549 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5551 can_fall_right = FALSE; /* slip down on left side */
5552 can_fall_both = FALSE;
5559 if (game.emulation == EMU_BOULDERDASH ||
5560 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5561 can_fall_right = FALSE; /* slip down on left side */
5563 can_fall_left = !(can_fall_right = RND(2));
5565 can_fall_both = FALSE;
5572 if (can_fall_both &&
5573 (game.emulation != EMU_BOULDERDASH &&
5574 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5575 can_fall_left = !(can_fall_right = RND(2));
5578 /* if not determined otherwise, prefer left side for slipping down */
5579 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5580 started_moving = TRUE;
5584 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5586 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5589 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5590 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5591 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5592 int belt_dir = game.belt_dir[belt_nr];
5594 if ((belt_dir == MV_LEFT && left_is_free) ||
5595 (belt_dir == MV_RIGHT && right_is_free))
5598 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5601 InitMovingField(x, y, belt_dir);
5602 started_moving = TRUE;
5605 Pushed[x][y] = TRUE;
5606 Pushed[nextx][y] = TRUE;
5609 GfxAction[x][y] = ACTION_DEFAULT;
5613 MovDir[x][y] = 0; /* if element was moving, stop it */
5618 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5620 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5622 if (CAN_MOVE(element) && !started_moving)
5625 int move_pattern = element_info[element].move_pattern;
5630 if (MovDir[x][y] == MV_NO_MOVING)
5632 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5633 x, y, element, element_info[element].token_name);
5634 printf("StartMoving(): This should never happen!\n");
5639 Moving2Blocked(x, y, &newx, &newy);
5642 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5645 if ((element == EL_SATELLITE ||
5646 element == EL_BALLOON ||
5647 element == EL_SPRING)
5648 && JustBeingPushed(x, y))
5655 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5656 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5658 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5659 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5660 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5664 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5665 element, element_info[element].token_name,
5666 WasJustMoving[x][y],
5667 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5668 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5669 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5670 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5674 WasJustMoving[x][y] = 0;
5677 CheckCollision[x][y] = 0;
5679 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5682 if (Feld[x][y] != element) /* element has changed */
5684 element = Feld[x][y];
5685 move_pattern = element_info[element].move_pattern;
5687 if (!CAN_MOVE(element))
5691 if (Feld[x][y] != element) /* element has changed */
5699 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5700 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5702 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5704 Moving2Blocked(x, y, &newx, &newy);
5705 if (Feld[newx][newy] == EL_BLOCKED)
5706 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5712 if (FrameCounter < 1 && x == 0 && y == 29)
5713 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5716 if (!MovDelay[x][y]) /* start new movement phase */
5718 /* all objects that can change their move direction after each step
5719 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5721 if (element != EL_YAMYAM &&
5722 element != EL_DARK_YAMYAM &&
5723 element != EL_PACMAN &&
5724 !(move_pattern & MV_ANY_DIRECTION) &&
5725 move_pattern != MV_TURNING_LEFT &&
5726 move_pattern != MV_TURNING_RIGHT &&
5727 move_pattern != MV_TURNING_LEFT_RIGHT &&
5728 move_pattern != MV_TURNING_RIGHT_LEFT &&
5729 move_pattern != MV_TURNING_RANDOM)
5734 if (FrameCounter < 1 && x == 0 && y == 29)
5735 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5738 if (MovDelay[x][y] && (element == EL_BUG ||
5739 element == EL_SPACESHIP ||
5740 element == EL_SP_SNIKSNAK ||
5741 element == EL_SP_ELECTRON ||
5742 element == EL_MOLE))
5743 DrawLevelField(x, y);
5747 if (MovDelay[x][y]) /* wait some time before next movement */
5752 if (element == EL_YAMYAM)
5755 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5756 DrawLevelElementAnimation(x, y, element);
5760 if (MovDelay[x][y]) /* element still has to wait some time */
5763 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5764 ResetGfxAnimation(x, y);
5768 if (GfxAction[x][y] != ACTION_WAITING)
5769 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5771 GfxAction[x][y] = ACTION_WAITING;
5775 if (element == EL_ROBOT ||
5777 element == EL_PACMAN ||
5779 element == EL_YAMYAM ||
5780 element == EL_DARK_YAMYAM)
5783 DrawLevelElementAnimation(x, y, element);
5785 DrawLevelElementAnimationIfNeeded(x, y, element);
5787 PlayLevelSoundAction(x, y, ACTION_WAITING);
5789 else if (element == EL_SP_ELECTRON)
5790 DrawLevelElementAnimationIfNeeded(x, y, element);
5791 else if (element == EL_DRAGON)
5794 int dir = MovDir[x][y];
5795 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5796 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5797 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5798 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5799 dir == MV_UP ? IMG_FLAMES_1_UP :
5800 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5801 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5804 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5807 GfxAction[x][y] = ACTION_ATTACKING;
5809 if (IS_PLAYER(x, y))
5810 DrawPlayerField(x, y);
5812 DrawLevelField(x, y);
5814 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5816 for (i = 1; i <= 3; i++)
5818 int xx = x + i * dx;
5819 int yy = y + i * dy;
5820 int sx = SCREENX(xx);
5821 int sy = SCREENY(yy);
5822 int flame_graphic = graphic + (i - 1);
5824 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5829 int flamed = MovingOrBlocked2Element(xx, yy);
5833 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5835 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5836 RemoveMovingField(xx, yy);
5838 RemoveField(xx, yy);
5840 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5843 RemoveMovingField(xx, yy);
5847 if (ChangeDelay[xx][yy])
5848 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5849 Feld[xx][yy] == EL_BLOCKED));
5853 ChangeDelay[xx][yy] = 0;
5855 Feld[xx][yy] = EL_FLAMES;
5856 if (IN_SCR_FIELD(sx, sy))
5858 DrawLevelFieldCrumbledSand(xx, yy);
5859 DrawGraphic(sx, sy, flame_graphic, frame);
5864 if (Feld[xx][yy] == EL_FLAMES)
5865 Feld[xx][yy] = EL_EMPTY;
5866 DrawLevelField(xx, yy);
5871 if (MovDelay[x][y]) /* element still has to wait some time */
5873 PlayLevelSoundAction(x, y, ACTION_WAITING);
5879 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5880 for all other elements GfxAction will be set by InitMovingField() */
5881 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5882 GfxAction[x][y] = ACTION_MOVING;
5886 /* now make next step */
5888 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5890 if (DONT_COLLIDE_WITH(element) &&
5891 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5892 !PLAYER_ENEMY_PROTECTED(newx, newy))
5895 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5899 /* player killed by element which is deadly when colliding with */
5901 KillHero(PLAYERINFO(newx, newy));
5908 else if (CAN_MOVE_INTO_ACID(element) &&
5909 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5910 (MovDir[x][y] == MV_DOWN ||
5911 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5913 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5914 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5918 else if ((element == EL_PENGUIN ||
5919 element == EL_ROBOT ||
5920 element == EL_SATELLITE ||
5921 element == EL_BALLOON ||
5922 IS_CUSTOM_ELEMENT(element)) &&
5923 IN_LEV_FIELD(newx, newy) &&
5924 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5927 SplashAcid(newx, newy);
5928 Store[x][y] = EL_ACID;
5930 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5932 if (Feld[newx][newy] == EL_EXIT_OPEN)
5936 DrawLevelField(x, y);
5938 Feld[x][y] = EL_EMPTY;
5939 DrawLevelField(x, y);
5942 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5943 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5944 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5946 local_player->friends_still_needed--;
5947 if (!local_player->friends_still_needed &&
5948 !local_player->GameOver && AllPlayersGone)
5949 local_player->LevelSolved = local_player->GameOver = TRUE;
5953 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5955 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5956 DrawLevelField(newx, newy);
5958 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5960 else if (!IS_FREE(newx, newy))
5962 GfxAction[x][y] = ACTION_WAITING;
5964 if (IS_PLAYER(x, y))
5965 DrawPlayerField(x, y);
5967 DrawLevelField(x, y);
5972 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5974 if (IS_FOOD_PIG(Feld[newx][newy]))
5976 if (IS_MOVING(newx, newy))
5977 RemoveMovingField(newx, newy);
5980 Feld[newx][newy] = EL_EMPTY;
5981 DrawLevelField(newx, newy);
5984 PlayLevelSound(x, y, SND_PIG_DIGGING);
5986 else if (!IS_FREE(newx, newy))
5988 if (IS_PLAYER(x, y))
5989 DrawPlayerField(x, y);
5991 DrawLevelField(x, y);
6000 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6003 else if (IS_CUSTOM_ELEMENT(element) &&
6004 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6008 !IS_FREE(newx, newy)
6013 int new_element = Feld[newx][newy];
6016 printf("::: '%s' digs '%s' [%d]\n",
6017 element_info[element].token_name,
6018 element_info[Feld[newx][newy]].token_name,
6019 StorePlayer[newx][newy]);
6022 if (!IS_FREE(newx, newy))
6024 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6025 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6028 /* no element can dig solid indestructible elements */
6029 if (IS_INDESTRUCTIBLE(new_element) &&
6030 !IS_DIGGABLE(new_element) &&
6031 !IS_COLLECTIBLE(new_element))
6034 if (AmoebaNr[newx][newy] &&
6035 (new_element == EL_AMOEBA_FULL ||
6036 new_element == EL_BD_AMOEBA ||
6037 new_element == EL_AMOEBA_GROWING))
6039 AmoebaCnt[AmoebaNr[newx][newy]]--;
6040 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6043 if (IS_MOVING(newx, newy))
6044 RemoveMovingField(newx, newy);
6047 RemoveField(newx, newy);
6048 DrawLevelField(newx, newy);
6051 /* if digged element was about to explode, prevent the explosion */
6052 ExplodeField[newx][newy] = EX_TYPE_NONE;
6054 PlayLevelSoundAction(x, y, action);
6059 Store[newx][newy] = EL_EMPTY;
6060 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6061 Store[newx][newy] = element_info[element].move_leave_element;
6063 Store[newx][newy] = EL_EMPTY;
6064 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6065 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6066 Store[newx][newy] = element_info[element].move_leave_element;
6069 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6070 element_info[element].can_leave_element = TRUE;
6073 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6075 RunnerVisit[x][y] = FrameCounter;
6076 PlayerVisit[x][y] /= 8; /* expire player visit path */
6082 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6084 if (!IS_FREE(newx, newy))
6086 if (IS_PLAYER(x, y))
6087 DrawPlayerField(x, y);
6089 DrawLevelField(x, y);
6095 boolean wanna_flame = !RND(10);
6096 int dx = newx - x, dy = newy - y;
6097 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6098 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6099 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6100 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6101 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6102 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6105 IS_CLASSIC_ENEMY(element1) ||
6106 IS_CLASSIC_ENEMY(element2)) &&
6107 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6108 element1 != EL_FLAMES && element2 != EL_FLAMES)
6111 ResetGfxAnimation(x, y);
6112 GfxAction[x][y] = ACTION_ATTACKING;
6115 if (IS_PLAYER(x, y))
6116 DrawPlayerField(x, y);
6118 DrawLevelField(x, y);
6120 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6122 MovDelay[x][y] = 50;
6126 RemoveField(newx, newy);
6128 Feld[newx][newy] = EL_FLAMES;
6129 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6132 RemoveField(newx1, newy1);
6134 Feld[newx1][newy1] = EL_FLAMES;
6136 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6139 RemoveField(newx2, newy2);
6141 Feld[newx2][newy2] = EL_FLAMES;
6148 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6149 Feld[newx][newy] == EL_DIAMOND)
6151 if (IS_MOVING(newx, newy))
6152 RemoveMovingField(newx, newy);
6155 Feld[newx][newy] = EL_EMPTY;
6156 DrawLevelField(newx, newy);
6159 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6161 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6162 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6164 if (AmoebaNr[newx][newy])
6166 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6167 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6168 Feld[newx][newy] == EL_BD_AMOEBA)
6169 AmoebaCnt[AmoebaNr[newx][newy]]--;
6174 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6176 if (IS_MOVING(newx, newy))
6179 RemoveMovingField(newx, newy);
6183 Feld[newx][newy] = EL_EMPTY;
6184 DrawLevelField(newx, newy);
6187 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6189 else if ((element == EL_PACMAN || element == EL_MOLE)
6190 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6192 if (AmoebaNr[newx][newy])
6194 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6195 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6196 Feld[newx][newy] == EL_BD_AMOEBA)
6197 AmoebaCnt[AmoebaNr[newx][newy]]--;
6200 if (element == EL_MOLE)
6202 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6203 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6205 ResetGfxAnimation(x, y);
6206 GfxAction[x][y] = ACTION_DIGGING;
6207 DrawLevelField(x, y);
6209 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6211 return; /* wait for shrinking amoeba */
6213 else /* element == EL_PACMAN */
6215 Feld[newx][newy] = EL_EMPTY;
6216 DrawLevelField(newx, newy);
6217 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6220 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6221 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6222 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6224 /* wait for shrinking amoeba to completely disappear */
6227 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6229 /* object was running against a wall */
6234 if (move_pattern & MV_ANY_DIRECTION &&
6235 move_pattern == MovDir[x][y])
6237 int blocking_element =
6238 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6241 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6242 element_info[element].token_name,
6243 element_info[blocking_element].token_name,
6247 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6250 element = Feld[x][y]; /* element might have changed */
6255 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6256 DrawLevelElementAnimation(x, y, element);
6258 if (element == EL_BUG ||
6259 element == EL_SPACESHIP ||
6260 element == EL_SP_SNIKSNAK)
6261 DrawLevelField(x, y);
6262 else if (element == EL_MOLE)
6263 DrawLevelField(x, y);
6264 else if (element == EL_BD_BUTTERFLY ||
6265 element == EL_BD_FIREFLY)
6266 DrawLevelElementAnimationIfNeeded(x, y, element);
6267 else if (element == EL_SATELLITE)
6268 DrawLevelElementAnimationIfNeeded(x, y, element);
6269 else if (element == EL_SP_ELECTRON)
6270 DrawLevelElementAnimationIfNeeded(x, y, element);
6273 if (DONT_TOUCH(element))
6274 TestIfBadThingTouchesHero(x, y);
6277 PlayLevelSoundAction(x, y, ACTION_WAITING);
6283 InitMovingField(x, y, MovDir[x][y]);
6285 PlayLevelSoundAction(x, y, ACTION_MOVING);
6289 ContinueMoving(x, y);
6292 void ContinueMoving(int x, int y)
6294 int element = Feld[x][y];
6295 int stored = Store[x][y];
6296 struct ElementInfo *ei = &element_info[element];
6297 int direction = MovDir[x][y];
6298 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6299 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6300 int newx = x + dx, newy = y + dy;
6302 int nextx = newx + dx, nexty = newy + dy;
6305 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6306 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6308 boolean pushed_by_player = Pushed[x][y];
6311 MovPos[x][y] += getElementMoveStepsize(x, y);
6314 if (pushed_by_player && IS_PLAYER(x, y))
6316 /* special case: moving object pushed by player */
6317 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6320 if (pushed_by_player) /* special case: moving object pushed by player */
6321 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6324 if (ABS(MovPos[x][y]) < TILEX)
6326 DrawLevelField(x, y);
6328 return; /* element is still moving */
6331 /* element reached destination field */
6333 Feld[x][y] = EL_EMPTY;
6334 Feld[newx][newy] = element;
6335 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6338 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6340 element = Feld[newx][newy] = EL_ACID;
6343 else if (element == EL_MOLE)
6345 Feld[x][y] = EL_SAND;
6347 DrawLevelFieldCrumbledSandNeighbours(x, y);
6349 else if (element == EL_QUICKSAND_FILLING)
6351 element = Feld[newx][newy] = get_next_element(element);
6352 Store[newx][newy] = Store[x][y];
6354 else if (element == EL_QUICKSAND_EMPTYING)
6356 Feld[x][y] = get_next_element(element);
6357 element = Feld[newx][newy] = Store[x][y];
6359 else if (element == EL_MAGIC_WALL_FILLING)
6361 element = Feld[newx][newy] = get_next_element(element);
6362 if (!game.magic_wall_active)
6363 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6364 Store[newx][newy] = Store[x][y];
6366 else if (element == EL_MAGIC_WALL_EMPTYING)
6368 Feld[x][y] = get_next_element(element);
6369 if (!game.magic_wall_active)
6370 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6371 element = Feld[newx][newy] = Store[x][y];
6373 else if (element == EL_BD_MAGIC_WALL_FILLING)
6375 element = Feld[newx][newy] = get_next_element(element);
6376 if (!game.magic_wall_active)
6377 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6378 Store[newx][newy] = Store[x][y];
6380 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6382 Feld[x][y] = get_next_element(element);
6383 if (!game.magic_wall_active)
6384 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6385 element = Feld[newx][newy] = Store[x][y];
6387 else if (element == EL_AMOEBA_DROPPING)
6389 Feld[x][y] = get_next_element(element);
6390 element = Feld[newx][newy] = Store[x][y];
6392 else if (element == EL_SOKOBAN_OBJECT)
6395 Feld[x][y] = Back[x][y];
6397 if (Back[newx][newy])
6398 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6400 Back[x][y] = Back[newx][newy] = 0;
6403 else if (Store[x][y] == EL_ACID)
6405 element = Feld[newx][newy] = EL_ACID;
6409 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6410 ei->move_leave_element != EL_EMPTY &&
6411 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6412 Store[x][y] != EL_EMPTY))
6414 /* some elements can leave other elements behind after moving */
6416 Feld[x][y] = ei->move_leave_element;
6417 InitField(x, y, FALSE);
6419 if (GFX_CRUMBLED(Feld[x][y]))
6420 DrawLevelFieldCrumbledSandNeighbours(x, y);
6424 Store[x][y] = EL_EMPTY;
6425 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6426 MovDelay[newx][newy] = 0;
6428 if (CAN_CHANGE(element))
6430 /* copy element change control values to new field */
6431 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6432 ChangePage[newx][newy] = ChangePage[x][y];
6433 Changed[newx][newy] = Changed[x][y];
6434 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6437 ChangeDelay[x][y] = 0;
6438 ChangePage[x][y] = -1;
6439 Changed[x][y] = CE_BITMASK_DEFAULT;
6440 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6442 /* copy animation control values to new field */
6443 GfxFrame[newx][newy] = GfxFrame[x][y];
6444 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6445 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6446 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6448 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6450 ResetGfxAnimation(x, y); /* reset animation values for old field */
6453 /* some elements can leave other elements behind after moving */
6455 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6456 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6457 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6459 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6460 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6464 int move_leave_element = ei->move_leave_element;
6466 Feld[x][y] = move_leave_element;
6467 InitField(x, y, FALSE);
6469 if (GFX_CRUMBLED(Feld[x][y]))
6470 DrawLevelFieldCrumbledSandNeighbours(x, y);
6472 if (ELEM_IS_PLAYER(move_leave_element))
6473 RelocatePlayer(x, y, move_leave_element);
6478 /* some elements can leave other elements behind after moving */
6479 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6480 ei->move_leave_element != EL_EMPTY &&
6481 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6482 ei->can_leave_element_last))
6484 Feld[x][y] = ei->move_leave_element;
6485 InitField(x, y, FALSE);
6487 if (GFX_CRUMBLED(Feld[x][y]))
6488 DrawLevelFieldCrumbledSandNeighbours(x, y);
6491 ei->can_leave_element_last = ei->can_leave_element;
6492 ei->can_leave_element = FALSE;
6496 /* 2.1.1 (does not work correctly for spring) */
6497 if (!CAN_MOVE(element))
6498 MovDir[newx][newy] = 0;
6502 /* (does not work for falling objects that slide horizontally) */
6503 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6504 MovDir[newx][newy] = 0;
6507 if (!CAN_MOVE(element) ||
6508 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6509 MovDir[newx][newy] = 0;
6513 if (!CAN_MOVE(element) ||
6514 (CAN_FALL(element) && direction == MV_DOWN))
6515 GfxDir[x][y] = MovDir[newx][newy] = 0;
6517 if (!CAN_MOVE(element) ||
6518 (CAN_FALL(element) && direction == MV_DOWN &&
6519 (element == EL_SPRING ||
6520 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6521 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6522 GfxDir[x][y] = MovDir[newx][newy] = 0;
6528 DrawLevelField(x, y);
6529 DrawLevelField(newx, newy);
6531 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6533 /* prevent pushed element from moving on in pushed direction */
6534 if (pushed_by_player && CAN_MOVE(element) &&
6535 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6536 !(element_info[element].move_pattern & direction))
6537 TurnRound(newx, newy);
6540 /* prevent elements on conveyor belt from moving on in last direction */
6541 if (pushed_by_conveyor && CAN_FALL(element) &&
6542 direction & MV_HORIZONTAL)
6545 if (CAN_MOVE(element))
6546 InitMovDir(newx, newy);
6548 MovDir[newx][newy] = 0;
6550 MovDir[newx][newy] = 0;
6555 if (!pushed_by_player)
6557 int nextx = newx + dx, nexty = newy + dy;
6558 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6560 WasJustMoving[newx][newy] = 3;
6562 if (CAN_FALL(element) && direction == MV_DOWN)
6563 WasJustFalling[newx][newy] = 3;
6565 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6566 CheckCollision[newx][newy] = 2;
6569 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6571 TestIfBadThingTouchesHero(newx, newy);
6572 TestIfBadThingTouchesFriend(newx, newy);
6574 if (!IS_CUSTOM_ELEMENT(element))
6575 TestIfBadThingTouchesOtherBadThing(newx, newy);
6577 else if (element == EL_PENGUIN)
6578 TestIfFriendTouchesBadThing(newx, newy);
6580 #if USE_NEW_MOVE_STYLE
6582 if (CAN_FALL(element) && direction == MV_DOWN &&
6583 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6584 IS_PLAYER(x, newy + 1))
6585 printf("::: we would now kill the player [%d]\n", FrameCounter);
6588 /* give the player one last chance (one more frame) to move away */
6589 if (CAN_FALL(element) && direction == MV_DOWN &&
6590 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6591 (!IS_PLAYER(x, newy + 1) ||
6592 game.engine_version < VERSION_IDENT(3,1,1,0)))
6595 if (CAN_FALL(element) && direction == MV_DOWN &&
6596 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6604 if (pushed_by_player && !game.use_bug_change_when_pushing)
6606 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6609 if (pushed_by_player)
6614 int dig_side = MV_DIR_OPPOSITE(direction);
6616 static int trigger_sides[4] =
6618 CH_SIDE_RIGHT, /* moving left */
6619 CH_SIDE_LEFT, /* moving right */
6620 CH_SIDE_BOTTOM, /* moving up */
6621 CH_SIDE_TOP, /* moving down */
6623 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6625 struct PlayerInfo *player = PLAYERINFO(x, y);
6627 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6628 player->index_bit, dig_side);
6629 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6630 player->index_bit, dig_side);
6635 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6639 if (ChangePage[newx][newy] != -1) /* delayed change */
6640 ChangeElement(newx, newy, ChangePage[newx][newy]);
6645 TestIfElementHitsCustomElement(newx, newy, direction);
6649 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6651 int hitting_element = Feld[newx][newy];
6653 /* !!! fix side (direction) orientation here and elsewhere !!! */
6654 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6658 if (IN_LEV_FIELD(nextx, nexty))
6660 int opposite_direction = MV_DIR_OPPOSITE(direction);
6661 int hitting_side = direction;
6662 int touched_side = opposite_direction;
6663 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6664 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6665 MovDir[nextx][nexty] != direction ||
6666 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6672 CheckElementChangeBySide(nextx, nexty, touched_element,
6673 CE_HIT_BY_SOMETHING, opposite_direction);
6675 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6676 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6678 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6680 struct ElementChangeInfo *change =
6681 &element_info[hitting_element].change_page[i];
6683 if (change->can_change &&
6684 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6685 change->trigger_side & touched_side &&
6686 change->trigger_element == touched_element)
6688 CheckElementChangeByPage(newx, newy, hitting_element,
6689 touched_element, CE_OTHER_IS_HITTING,i);
6695 if (IS_CUSTOM_ELEMENT(touched_element) &&
6696 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6698 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6700 struct ElementChangeInfo *change =
6701 &element_info[touched_element].change_page[i];
6703 if (change->can_change &&
6704 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6705 change->trigger_side & hitting_side &&
6706 change->trigger_element == hitting_element)
6708 CheckElementChangeByPage(nextx, nexty, touched_element,
6709 hitting_element, CE_OTHER_GETS_HIT, i);
6720 TestIfPlayerTouchesCustomElement(newx, newy);
6721 TestIfElementTouchesCustomElement(newx, newy);
6724 int AmoebeNachbarNr(int ax, int ay)
6727 int element = Feld[ax][ay];
6729 static int xy[4][2] =
6737 for (i = 0; i < NUM_DIRECTIONS; i++)
6739 int x = ax + xy[i][0];
6740 int y = ay + xy[i][1];
6742 if (!IN_LEV_FIELD(x, y))
6745 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6746 group_nr = AmoebaNr[x][y];
6752 void AmoebenVereinigen(int ax, int ay)
6754 int i, x, y, xx, yy;
6755 int new_group_nr = AmoebaNr[ax][ay];
6756 static int xy[4][2] =
6764 if (new_group_nr == 0)
6767 for (i = 0; i < NUM_DIRECTIONS; i++)
6772 if (!IN_LEV_FIELD(x, y))
6775 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6776 Feld[x][y] == EL_BD_AMOEBA ||
6777 Feld[x][y] == EL_AMOEBA_DEAD) &&
6778 AmoebaNr[x][y] != new_group_nr)
6780 int old_group_nr = AmoebaNr[x][y];
6782 if (old_group_nr == 0)
6785 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6786 AmoebaCnt[old_group_nr] = 0;
6787 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6788 AmoebaCnt2[old_group_nr] = 0;
6790 for (yy = 0; yy < lev_fieldy; yy++)
6792 for (xx = 0; xx < lev_fieldx; xx++)
6794 if (AmoebaNr[xx][yy] == old_group_nr)
6795 AmoebaNr[xx][yy] = new_group_nr;
6802 void AmoebeUmwandeln(int ax, int ay)
6806 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6808 int group_nr = AmoebaNr[ax][ay];
6813 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6814 printf("AmoebeUmwandeln(): This should never happen!\n");
6819 for (y = 0; y < lev_fieldy; y++)
6821 for (x = 0; x < lev_fieldx; x++)
6823 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6826 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6830 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6831 SND_AMOEBA_TURNING_TO_GEM :
6832 SND_AMOEBA_TURNING_TO_ROCK));
6837 static int xy[4][2] =
6845 for (i = 0; i < NUM_DIRECTIONS; i++)
6850 if (!IN_LEV_FIELD(x, y))
6853 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6855 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6856 SND_AMOEBA_TURNING_TO_GEM :
6857 SND_AMOEBA_TURNING_TO_ROCK));
6864 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6867 int group_nr = AmoebaNr[ax][ay];
6868 boolean done = FALSE;
6873 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6874 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6879 for (y = 0; y < lev_fieldy; y++)
6881 for (x = 0; x < lev_fieldx; x++)
6883 if (AmoebaNr[x][y] == group_nr &&
6884 (Feld[x][y] == EL_AMOEBA_DEAD ||
6885 Feld[x][y] == EL_BD_AMOEBA ||
6886 Feld[x][y] == EL_AMOEBA_GROWING))
6889 Feld[x][y] = new_element;
6890 InitField(x, y, FALSE);
6891 DrawLevelField(x, y);
6898 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6899 SND_BD_AMOEBA_TURNING_TO_ROCK :
6900 SND_BD_AMOEBA_TURNING_TO_GEM));
6903 void AmoebeWaechst(int x, int y)
6905 static unsigned long sound_delay = 0;
6906 static unsigned long sound_delay_value = 0;
6908 if (!MovDelay[x][y]) /* start new growing cycle */
6912 if (DelayReached(&sound_delay, sound_delay_value))
6915 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6917 if (Store[x][y] == EL_BD_AMOEBA)
6918 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6920 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6922 sound_delay_value = 30;
6926 if (MovDelay[x][y]) /* wait some time before growing bigger */
6929 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6931 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6932 6 - MovDelay[x][y]);
6934 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6937 if (!MovDelay[x][y])
6939 Feld[x][y] = Store[x][y];
6941 DrawLevelField(x, y);
6946 void AmoebaDisappearing(int x, int y)
6948 static unsigned long sound_delay = 0;
6949 static unsigned long sound_delay_value = 0;
6951 if (!MovDelay[x][y]) /* start new shrinking cycle */
6955 if (DelayReached(&sound_delay, sound_delay_value))
6956 sound_delay_value = 30;
6959 if (MovDelay[x][y]) /* wait some time before shrinking */
6962 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6964 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6965 6 - MovDelay[x][y]);
6967 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6970 if (!MovDelay[x][y])
6972 Feld[x][y] = EL_EMPTY;
6973 DrawLevelField(x, y);
6975 /* don't let mole enter this field in this cycle;
6976 (give priority to objects falling to this field from above) */
6982 void AmoebeAbleger(int ax, int ay)
6985 int element = Feld[ax][ay];
6986 int graphic = el2img(element);
6987 int newax = ax, neway = ay;
6988 static int xy[4][2] =
6996 if (!level.amoeba_speed)
6998 Feld[ax][ay] = EL_AMOEBA_DEAD;
6999 DrawLevelField(ax, ay);
7003 if (IS_ANIMATED(graphic))
7004 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7006 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7007 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7009 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7012 if (MovDelay[ax][ay])
7016 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7019 int x = ax + xy[start][0];
7020 int y = ay + xy[start][1];
7022 if (!IN_LEV_FIELD(x, y))
7026 if (IS_FREE(x, y) ||
7027 CAN_GROW_INTO(Feld[x][y]) ||
7028 Feld[x][y] == EL_QUICKSAND_EMPTY)
7034 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7035 if (IS_FREE(x, y) ||
7036 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7043 if (newax == ax && neway == ay)
7046 else /* normal or "filled" (BD style) amoeba */
7049 boolean waiting_for_player = FALSE;
7051 for (i = 0; i < NUM_DIRECTIONS; i++)
7053 int j = (start + i) % 4;
7054 int x = ax + xy[j][0];
7055 int y = ay + xy[j][1];
7057 if (!IN_LEV_FIELD(x, y))
7061 if (IS_FREE(x, y) ||
7062 CAN_GROW_INTO(Feld[x][y]) ||
7063 Feld[x][y] == EL_QUICKSAND_EMPTY)
7070 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7071 if (IS_FREE(x, y) ||
7072 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7079 else if (IS_PLAYER(x, y))
7080 waiting_for_player = TRUE;
7083 if (newax == ax && neway == ay) /* amoeba cannot grow */
7086 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7088 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7091 Feld[ax][ay] = EL_AMOEBA_DEAD;
7092 DrawLevelField(ax, ay);
7093 AmoebaCnt[AmoebaNr[ax][ay]]--;
7095 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7097 if (element == EL_AMOEBA_FULL)
7098 AmoebeUmwandeln(ax, ay);
7099 else if (element == EL_BD_AMOEBA)
7100 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7105 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7107 /* amoeba gets larger by growing in some direction */
7109 int new_group_nr = AmoebaNr[ax][ay];
7112 if (new_group_nr == 0)
7114 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7115 printf("AmoebeAbleger(): This should never happen!\n");
7120 AmoebaNr[newax][neway] = new_group_nr;
7121 AmoebaCnt[new_group_nr]++;
7122 AmoebaCnt2[new_group_nr]++;
7124 /* if amoeba touches other amoeba(s) after growing, unify them */
7125 AmoebenVereinigen(newax, neway);
7127 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7129 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7135 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7136 (neway == lev_fieldy - 1 && newax != ax))
7138 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7139 Store[newax][neway] = element;
7141 else if (neway == ay)
7143 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7145 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7147 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7152 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7153 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7154 Store[ax][ay] = EL_AMOEBA_DROP;
7155 ContinueMoving(ax, ay);
7159 DrawLevelField(newax, neway);
7162 void Life(int ax, int ay)
7165 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7167 int element = Feld[ax][ay];
7168 int graphic = el2img(element);
7169 boolean changed = FALSE;
7171 if (IS_ANIMATED(graphic))
7172 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7177 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7178 MovDelay[ax][ay] = life_time;
7180 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7183 if (MovDelay[ax][ay])
7187 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7189 int xx = ax+x1, yy = ay+y1;
7192 if (!IN_LEV_FIELD(xx, yy))
7195 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7197 int x = xx+x2, y = yy+y2;
7199 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7202 if (((Feld[x][y] == element ||
7203 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7205 (IS_FREE(x, y) && Stop[x][y]))
7209 if (xx == ax && yy == ay) /* field in the middle */
7211 if (nachbarn < life[0] || nachbarn > life[1])
7213 Feld[xx][yy] = EL_EMPTY;
7215 DrawLevelField(xx, yy);
7216 Stop[xx][yy] = TRUE;
7221 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7222 { /* free border field */
7223 if (nachbarn >= life[2] && nachbarn <= life[3])
7225 Feld[xx][yy] = element;
7226 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7228 DrawLevelField(xx, yy);
7229 Stop[xx][yy] = TRUE;
7234 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7235 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7236 { /* free border field */
7237 if (nachbarn >= life[2] && nachbarn <= life[3])
7239 Feld[xx][yy] = element;
7240 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7242 DrawLevelField(xx, yy);
7243 Stop[xx][yy] = TRUE;
7251 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7252 SND_GAME_OF_LIFE_GROWING);
7255 static void InitRobotWheel(int x, int y)
7257 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7260 static void RunRobotWheel(int x, int y)
7262 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7265 static void StopRobotWheel(int x, int y)
7267 if (ZX == x && ZY == y)
7271 static void InitTimegateWheel(int x, int y)
7274 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7276 /* another brainless, "type style" bug ... :-( */
7277 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7281 static void RunTimegateWheel(int x, int y)
7283 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7286 void CheckExit(int x, int y)
7288 if (local_player->gems_still_needed > 0 ||
7289 local_player->sokobanfields_still_needed > 0 ||
7290 local_player->lights_still_needed > 0)
7292 int element = Feld[x][y];
7293 int graphic = el2img(element);
7295 if (IS_ANIMATED(graphic))
7296 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7301 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7304 Feld[x][y] = EL_EXIT_OPENING;
7306 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7309 void CheckExitSP(int x, int y)
7311 if (local_player->gems_still_needed > 0)
7313 int element = Feld[x][y];
7314 int graphic = el2img(element);
7316 if (IS_ANIMATED(graphic))
7317 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7322 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7325 Feld[x][y] = EL_SP_EXIT_OPENING;
7327 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7330 static void CloseAllOpenTimegates()
7334 for (y = 0; y < lev_fieldy; y++)
7336 for (x = 0; x < lev_fieldx; x++)
7338 int element = Feld[x][y];
7340 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7342 Feld[x][y] = EL_TIMEGATE_CLOSING;
7344 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7346 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7353 void EdelsteinFunkeln(int x, int y)
7355 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7358 if (Feld[x][y] == EL_BD_DIAMOND)
7361 if (MovDelay[x][y] == 0) /* next animation frame */
7362 MovDelay[x][y] = 11 * !SimpleRND(500);
7364 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7368 if (setup.direct_draw && MovDelay[x][y])
7369 SetDrawtoField(DRAW_BUFFERED);
7371 DrawLevelElementAnimation(x, y, Feld[x][y]);
7373 if (MovDelay[x][y] != 0)
7375 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7376 10 - MovDelay[x][y]);
7378 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7380 if (setup.direct_draw)
7384 dest_x = FX + SCREENX(x) * TILEX;
7385 dest_y = FY + SCREENY(y) * TILEY;
7387 BlitBitmap(drawto_field, window,
7388 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7389 SetDrawtoField(DRAW_DIRECT);
7395 void MauerWaechst(int x, int y)
7399 if (!MovDelay[x][y]) /* next animation frame */
7400 MovDelay[x][y] = 3 * delay;
7402 if (MovDelay[x][y]) /* wait some time before next frame */
7406 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7408 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7409 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7411 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7414 if (!MovDelay[x][y])
7416 if (MovDir[x][y] == MV_LEFT)
7418 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7419 DrawLevelField(x - 1, y);
7421 else if (MovDir[x][y] == MV_RIGHT)
7423 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7424 DrawLevelField(x + 1, y);
7426 else if (MovDir[x][y] == MV_UP)
7428 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7429 DrawLevelField(x, y - 1);
7433 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7434 DrawLevelField(x, y + 1);
7437 Feld[x][y] = Store[x][y];
7439 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7440 DrawLevelField(x, y);
7445 void MauerAbleger(int ax, int ay)
7447 int element = Feld[ax][ay];
7448 int graphic = el2img(element);
7449 boolean oben_frei = FALSE, unten_frei = FALSE;
7450 boolean links_frei = FALSE, rechts_frei = FALSE;
7451 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7452 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7453 boolean new_wall = FALSE;
7455 if (IS_ANIMATED(graphic))
7456 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7458 if (!MovDelay[ax][ay]) /* start building new wall */
7459 MovDelay[ax][ay] = 6;
7461 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7464 if (MovDelay[ax][ay])
7468 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7470 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7472 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7474 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7477 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7478 element == EL_EXPANDABLE_WALL_ANY)
7482 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7483 Store[ax][ay-1] = element;
7484 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7485 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7486 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7487 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7492 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7493 Store[ax][ay+1] = element;
7494 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7495 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7496 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7497 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7502 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7503 element == EL_EXPANDABLE_WALL_ANY ||
7504 element == EL_EXPANDABLE_WALL)
7508 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7509 Store[ax-1][ay] = element;
7510 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7511 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7512 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7513 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7519 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7520 Store[ax+1][ay] = element;
7521 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7522 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7523 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7524 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7529 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7530 DrawLevelField(ax, ay);
7532 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7534 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7535 unten_massiv = TRUE;
7536 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7537 links_massiv = TRUE;
7538 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7539 rechts_massiv = TRUE;
7541 if (((oben_massiv && unten_massiv) ||
7542 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7543 element == EL_EXPANDABLE_WALL) &&
7544 ((links_massiv && rechts_massiv) ||
7545 element == EL_EXPANDABLE_WALL_VERTICAL))
7546 Feld[ax][ay] = EL_WALL;
7550 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7552 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7556 void CheckForDragon(int x, int y)
7559 boolean dragon_found = FALSE;
7560 static int xy[4][2] =
7568 for (i = 0; i < NUM_DIRECTIONS; i++)
7570 for (j = 0; j < 4; j++)
7572 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7574 if (IN_LEV_FIELD(xx, yy) &&
7575 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7577 if (Feld[xx][yy] == EL_DRAGON)
7578 dragon_found = TRUE;
7587 for (i = 0; i < NUM_DIRECTIONS; i++)
7589 for (j = 0; j < 3; j++)
7591 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7593 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7595 Feld[xx][yy] = EL_EMPTY;
7596 DrawLevelField(xx, yy);
7605 static void InitBuggyBase(int x, int y)
7607 int element = Feld[x][y];
7608 int activating_delay = FRAMES_PER_SECOND / 4;
7611 (element == EL_SP_BUGGY_BASE ?
7612 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7613 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7615 element == EL_SP_BUGGY_BASE_ACTIVE ?
7616 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7619 static void WarnBuggyBase(int x, int y)
7622 static int xy[4][2] =
7630 for (i = 0; i < NUM_DIRECTIONS; i++)
7632 int xx = x + xy[i][0], yy = y + xy[i][1];
7634 if (IS_PLAYER(xx, yy))
7636 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7643 static void InitTrap(int x, int y)
7645 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7648 static void ActivateTrap(int x, int y)
7650 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7653 static void ChangeActiveTrap(int x, int y)
7655 int graphic = IMG_TRAP_ACTIVE;
7657 /* if new animation frame was drawn, correct crumbled sand border */
7658 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7659 DrawLevelFieldCrumbledSand(x, y);
7662 static void ChangeElementNowExt(int x, int y, int target_element)
7664 int previous_move_direction = MovDir[x][y];
7666 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7667 IS_WALKABLE(Feld[x][y]));
7669 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7670 IS_WALKABLE(Feld[x][y]) &&
7674 /* check if element under player changes from accessible to unaccessible
7675 (needed for special case of dropping element which then changes) */
7676 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7677 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7680 printf("::: BOOOM! [%d, '%s']\n", target_element,
7681 element_info[target_element].token_name);
7693 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7694 RemoveMovingField(x, y);
7698 Feld[x][y] = target_element;
7701 Feld[x][y] = target_element;
7704 ResetGfxAnimation(x, y);
7705 ResetRandomAnimationValue(x, y);
7707 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7708 MovDir[x][y] = previous_move_direction;
7711 InitField_WithBug1(x, y, FALSE);
7713 InitField(x, y, FALSE);
7714 if (CAN_MOVE(Feld[x][y]))
7718 DrawLevelField(x, y);
7720 if (GFX_CRUMBLED(Feld[x][y]))
7721 DrawLevelFieldCrumbledSandNeighbours(x, y);
7725 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7729 TestIfBadThingTouchesHero(x, y);
7730 TestIfPlayerTouchesCustomElement(x, y);
7731 TestIfElementTouchesCustomElement(x, y);
7734 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7735 if (ELEM_IS_PLAYER(target_element))
7736 RelocatePlayer(x, y, target_element);
7739 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7743 TestIfBadThingTouchesHero(x, y);
7744 TestIfPlayerTouchesCustomElement(x, y);
7745 TestIfElementTouchesCustomElement(x, y);
7749 static boolean ChangeElementNow(int x, int y, int element, int page)
7751 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7753 int old_element = Feld[x][y];
7755 /* always use default change event to prevent running into a loop */
7756 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7757 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7759 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7761 /* reset actual trigger element and player */
7762 change->actual_trigger_element = EL_EMPTY;
7763 change->actual_trigger_player = EL_PLAYER_1;
7766 /* do not change already changed elements with same change event */
7768 if (Changed[x][y] & ChangeEvent[x][y])
7775 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7778 /* !!! indirect change before direct change !!! */
7779 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7782 if (change->explode)
7789 if (change->use_target_content)
7791 boolean complete_replace = TRUE;
7792 boolean can_replace[3][3];
7795 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7798 boolean is_walkable;
7799 boolean is_diggable;
7800 boolean is_collectible;
7801 boolean is_removable;
7802 boolean is_destructible;
7803 int ex = x + xx - 1;
7804 int ey = y + yy - 1;
7805 int content_element = change->target_content[xx][yy];
7808 can_replace[xx][yy] = TRUE;
7810 if (ex == x && ey == y) /* do not check changing element itself */
7813 if (content_element == EL_EMPTY_SPACE)
7815 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7820 if (!IN_LEV_FIELD(ex, ey))
7822 can_replace[xx][yy] = FALSE;
7823 complete_replace = FALSE;
7829 if (Changed[ex][ey]) /* do not change already changed elements */
7831 can_replace[xx][yy] = FALSE;
7832 complete_replace = FALSE;
7840 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7841 e = MovingOrBlocked2Element(ex, ey);
7846 is_empty = (IS_FREE(ex, ey) ||
7847 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7848 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7849 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7853 is_empty = (IS_FREE(ex, ey) ||
7854 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7856 is_empty = (IS_FREE(ex, ey) ||
7857 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7862 is_walkable = (is_empty || IS_WALKABLE(e));
7863 is_diggable = (is_empty || IS_DIGGABLE(e));
7864 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7865 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7866 is_removable = (is_diggable || is_collectible);
7868 can_replace[xx][yy] =
7869 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7870 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7871 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7872 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7873 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7874 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7875 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7877 if (!can_replace[xx][yy])
7878 complete_replace = FALSE;
7880 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7881 IS_WALKABLE(content_element)));
7883 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7885 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7888 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7889 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7890 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7892 can_replace[xx][yy] = FALSE;
7893 complete_replace = FALSE;
7898 if (!change->only_if_complete || complete_replace)
7900 boolean something_has_changed = FALSE;
7902 if (change->only_if_complete && change->use_random_replace &&
7903 RND(100) < change->random_percentage)
7906 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7908 int ex = x + xx - 1;
7909 int ey = y + yy - 1;
7910 int content_element;
7912 if (can_replace[xx][yy] && (!change->use_random_replace ||
7913 RND(100) < change->random_percentage))
7915 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7916 RemoveMovingField(ex, ey);
7918 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7920 content_element = change->target_content[xx][yy];
7921 target_element = GET_TARGET_ELEMENT(content_element, change);
7923 ChangeElementNowExt(ex, ey, target_element);
7925 something_has_changed = TRUE;
7927 /* for symmetry reasons, freeze newly created border elements */
7928 if (ex != x || ey != y)
7929 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7933 if (something_has_changed)
7934 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7939 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7941 ChangeElementNowExt(x, y, target_element);
7943 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7947 /* this uses direct change before indirect change */
7948 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7954 static void ChangeElement(int x, int y, int page)
7956 int element = MovingOrBlocked2Element(x, y);
7957 struct ElementInfo *ei = &element_info[element];
7958 struct ElementChangeInfo *change = &ei->change_page[page];
7961 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7964 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7965 x, y, element, element_info[element].token_name);
7966 printf("ChangeElement(): This should never happen!\n");
7971 /* this can happen with classic bombs on walkable, changing elements */
7972 if (!CAN_CHANGE(element))
7975 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7976 ChangeDelay[x][y] = 0;
7982 if (ChangeDelay[x][y] == 0) /* initialize element change */
7984 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7985 RND(change->delay_random * change->delay_frames)) + 1;
7987 ResetGfxAnimation(x, y);
7988 ResetRandomAnimationValue(x, y);
7990 if (change->pre_change_function)
7991 change->pre_change_function(x, y);
7994 ChangeDelay[x][y]--;
7996 if (ChangeDelay[x][y] != 0) /* continue element change */
7998 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8000 if (IS_ANIMATED(graphic))
8001 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8003 if (change->change_function)
8004 change->change_function(x, y);
8006 else /* finish element change */
8008 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8010 page = ChangePage[x][y];
8011 ChangePage[x][y] = -1;
8013 change = &ei->change_page[page];
8017 if (IS_MOVING(x, y) && !change->explode)
8019 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8022 ChangeDelay[x][y] = 1; /* try change after next move step */
8023 ChangePage[x][y] = page; /* remember page to use for change */
8028 if (ChangeElementNow(x, y, element, page))
8030 if (change->post_change_function)
8031 change->post_change_function(x, y);
8036 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8037 int trigger_element,
8044 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8046 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8049 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8051 int element = EL_CUSTOM_START + i;
8053 boolean change_element = FALSE;
8056 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8059 for (j = 0; j < element_info[element].num_change_pages; j++)
8061 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8063 if (change->can_change &&
8064 change->events & CH_EVENT_BIT(trigger_event) &&
8065 change->trigger_side & trigger_side &&
8066 change->trigger_player & trigger_player &&
8067 change->trigger_page & trigger_page_bits &&
8068 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8071 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8072 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8073 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8076 change_element = TRUE;
8079 change->actual_trigger_element = trigger_element;
8080 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8086 if (!change_element)
8089 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8092 if (x == lx && y == ly) /* do not change trigger element itself */
8096 if (Feld[x][y] == element)
8098 ChangeDelay[x][y] = 1;
8099 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8100 ChangeElement(x, y, page);
8108 static boolean CheckElementChangeExt(int x, int y,
8110 int trigger_element,
8116 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8119 if (Feld[x][y] == EL_BLOCKED)
8121 Blocked2Moving(x, y, &x, &y);
8122 element = Feld[x][y];
8126 if (Feld[x][y] != element) /* check if element has already changed */
8129 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8130 Feld[x][y], element_info[Feld[x][y]].token_name,
8131 element, element_info[element].token_name,
8140 if (trigger_page < 0)
8142 boolean change_element = FALSE;
8145 for (i = 0; i < element_info[element].num_change_pages; i++)
8147 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8149 if (change->can_change &&
8150 change->events & CH_EVENT_BIT(trigger_event) &&
8151 change->trigger_side & trigger_side &&
8152 change->trigger_player & trigger_player)
8154 change_element = TRUE;
8157 change->actual_trigger_element = trigger_element;
8158 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8164 if (!change_element)
8169 struct ElementInfo *ei = &element_info[element];
8170 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8172 change->actual_trigger_element = trigger_element;
8173 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8178 /* !!! this check misses pages with same event, but different side !!! */
8180 if (trigger_page < 0)
8181 trigger_page = element_info[element].event_page_nr[trigger_event];
8183 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8187 ChangeDelay[x][y] = 1;
8188 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8189 ChangeElement(x, y, trigger_page);
8194 static void PlayPlayerSound(struct PlayerInfo *player)
8196 int jx = player->jx, jy = player->jy;
8197 int element = player->element_nr;
8198 int last_action = player->last_action_waiting;
8199 int action = player->action_waiting;
8201 if (player->is_waiting)
8203 if (action != last_action)
8204 PlayLevelSoundElementAction(jx, jy, element, action);
8206 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8210 if (action != last_action)
8211 StopSound(element_info[element].sound[last_action]);
8213 if (last_action == ACTION_SLEEPING)
8214 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8218 static void PlayAllPlayersSound()
8222 for (i = 0; i < MAX_PLAYERS; i++)
8223 if (stored_player[i].active)
8224 PlayPlayerSound(&stored_player[i]);
8227 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8229 boolean last_waiting = player->is_waiting;
8230 int move_dir = player->MovDir;
8232 player->last_action_waiting = player->action_waiting;
8236 if (!last_waiting) /* not waiting -> waiting */
8238 player->is_waiting = TRUE;
8240 player->frame_counter_bored =
8242 game.player_boring_delay_fixed +
8243 SimpleRND(game.player_boring_delay_random);
8244 player->frame_counter_sleeping =
8246 game.player_sleeping_delay_fixed +
8247 SimpleRND(game.player_sleeping_delay_random);
8249 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8252 if (game.player_sleeping_delay_fixed +
8253 game.player_sleeping_delay_random > 0 &&
8254 player->anim_delay_counter == 0 &&
8255 player->post_delay_counter == 0 &&
8256 FrameCounter >= player->frame_counter_sleeping)
8257 player->is_sleeping = TRUE;
8258 else if (game.player_boring_delay_fixed +
8259 game.player_boring_delay_random > 0 &&
8260 FrameCounter >= player->frame_counter_bored)
8261 player->is_bored = TRUE;
8263 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8264 player->is_bored ? ACTION_BORING :
8267 if (player->is_sleeping)
8269 if (player->num_special_action_sleeping > 0)
8271 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8273 int last_special_action = player->special_action_sleeping;
8274 int num_special_action = player->num_special_action_sleeping;
8275 int special_action =
8276 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8277 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8278 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8279 last_special_action + 1 : ACTION_SLEEPING);
8280 int special_graphic =
8281 el_act_dir2img(player->element_nr, special_action, move_dir);
8283 player->anim_delay_counter =
8284 graphic_info[special_graphic].anim_delay_fixed +
8285 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8286 player->post_delay_counter =
8287 graphic_info[special_graphic].post_delay_fixed +
8288 SimpleRND(graphic_info[special_graphic].post_delay_random);
8290 player->special_action_sleeping = special_action;
8293 if (player->anim_delay_counter > 0)
8295 player->action_waiting = player->special_action_sleeping;
8296 player->anim_delay_counter--;
8298 else if (player->post_delay_counter > 0)
8300 player->post_delay_counter--;
8304 else if (player->is_bored)
8306 if (player->num_special_action_bored > 0)
8308 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8310 int special_action =
8311 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8312 int special_graphic =
8313 el_act_dir2img(player->element_nr, special_action, move_dir);
8315 player->anim_delay_counter =
8316 graphic_info[special_graphic].anim_delay_fixed +
8317 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8318 player->post_delay_counter =
8319 graphic_info[special_graphic].post_delay_fixed +
8320 SimpleRND(graphic_info[special_graphic].post_delay_random);
8322 player->special_action_bored = special_action;
8325 if (player->anim_delay_counter > 0)
8327 player->action_waiting = player->special_action_bored;
8328 player->anim_delay_counter--;
8330 else if (player->post_delay_counter > 0)
8332 player->post_delay_counter--;
8337 else if (last_waiting) /* waiting -> not waiting */
8339 player->is_waiting = FALSE;
8340 player->is_bored = FALSE;
8341 player->is_sleeping = FALSE;
8343 player->frame_counter_bored = -1;
8344 player->frame_counter_sleeping = -1;
8346 player->anim_delay_counter = 0;
8347 player->post_delay_counter = 0;
8349 player->action_waiting = ACTION_DEFAULT;
8351 player->special_action_bored = ACTION_DEFAULT;
8352 player->special_action_sleeping = ACTION_DEFAULT;
8357 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8360 static byte stored_player_action[MAX_PLAYERS];
8361 static int num_stored_actions = 0;
8363 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8364 int left = player_action & JOY_LEFT;
8365 int right = player_action & JOY_RIGHT;
8366 int up = player_action & JOY_UP;
8367 int down = player_action & JOY_DOWN;
8368 int button1 = player_action & JOY_BUTTON_1;
8369 int button2 = player_action & JOY_BUTTON_2;
8370 int dx = (left ? -1 : right ? 1 : 0);
8371 int dy = (up ? -1 : down ? 1 : 0);
8374 stored_player_action[player->index_nr] = 0;
8375 num_stored_actions++;
8379 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8382 if (!player->active || tape.pausing)
8386 printf("::: [%d %d %d %d] [%d %d]\n",
8387 left, right, up, down, button1, button2);
8393 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8398 if (player->MovPos == 0)
8399 CheckGravityMovement(player);
8402 snapped = SnapField(player, dx, dy);
8406 dropped = DropElement(player);
8408 moved = MovePlayer(player, dx, dy);
8411 if (tape.single_step && tape.recording && !tape.pausing)
8413 if (button1 || (dropped && !moved))
8415 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8416 SnapField(player, 0, 0); /* stop snapping */
8420 SetPlayerWaiting(player, FALSE);
8423 return player_action;
8425 stored_player_action[player->index_nr] = player_action;
8431 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8434 /* no actions for this player (no input at player's configured device) */
8436 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8437 SnapField(player, 0, 0);
8438 CheckGravityMovementWhenNotMoving(player);
8440 if (player->MovPos == 0)
8441 SetPlayerWaiting(player, TRUE);
8443 if (player->MovPos == 0) /* needed for tape.playing */
8444 player->is_moving = FALSE;
8446 player->is_dropping = FALSE;
8452 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8454 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8456 TapeRecordAction(stored_player_action);
8457 num_stored_actions = 0;
8464 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8466 static byte stored_player_action[MAX_PLAYERS];
8467 static int num_stored_actions = 0;
8468 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8469 int left = player_action & JOY_LEFT;
8470 int right = player_action & JOY_RIGHT;
8471 int up = player_action & JOY_UP;
8472 int down = player_action & JOY_DOWN;
8473 int button1 = player_action & JOY_BUTTON_1;
8474 int button2 = player_action & JOY_BUTTON_2;
8475 int dx = (left ? -1 : right ? 1 : 0);
8476 int dy = (up ? -1 : down ? 1 : 0);
8478 stored_player_action[player->index_nr] = 0;
8479 num_stored_actions++;
8481 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8483 if (!player->active || tape.pausing)
8488 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8491 snapped = SnapField(player, dx, dy);
8495 dropped = DropElement(player);
8497 moved = MovePlayer(player, dx, dy);
8500 if (tape.single_step && tape.recording && !tape.pausing)
8502 if (button1 || (dropped && !moved))
8504 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8505 SnapField(player, 0, 0); /* stop snapping */
8509 stored_player_action[player->index_nr] = player_action;
8513 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8515 /* no actions for this player (no input at player's configured device) */
8517 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8518 SnapField(player, 0, 0);
8519 CheckGravityMovementWhenNotMoving(player);
8521 if (player->MovPos == 0)
8522 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8524 if (player->MovPos == 0) /* needed for tape.playing */
8525 player->is_moving = FALSE;
8528 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8530 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8532 TapeRecordAction(stored_player_action);
8533 num_stored_actions = 0;
8538 void AdvanceFrameAndPlayerCounters(int player_nr)
8542 /* advance frame counters (global frame counter and time frame counter) */
8546 /* advance player counters (counters for move delay, move animation etc.) */
8547 for (i = 0; i < MAX_PLAYERS; i++)
8549 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8551 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8553 if (!advance_player_counters) /* not all players may be affected */
8556 stored_player[i].Frame += move_frames;
8558 if (stored_player[i].MovPos != 0)
8559 stored_player[i].StepFrame += move_frames;
8561 #if USE_NEW_MOVE_DELAY
8562 if (stored_player[i].move_delay > 0)
8563 stored_player[i].move_delay--;
8566 #if USE_NEW_PUSH_DELAY
8567 /* due to bugs in previous versions, counter must count up, not down */
8568 if (stored_player[i].push_delay != -1)
8569 stored_player[i].push_delay++;
8572 if (stored_player[i].drop_delay > 0)
8573 stored_player[i].drop_delay--;
8579 static unsigned long game_frame_delay = 0;
8580 unsigned long game_frame_delay_value;
8581 int magic_wall_x = 0, magic_wall_y = 0;
8582 int i, x, y, element, graphic;
8583 byte *recorded_player_action;
8584 byte summarized_player_action = 0;
8586 byte tape_action[MAX_PLAYERS];
8589 if (game_status != GAME_MODE_PLAYING)
8592 game_frame_delay_value =
8593 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8595 if (tape.playing && tape.warp_forward && !tape.pausing)
8596 game_frame_delay_value = 0;
8598 /* ---------- main game synchronization point ---------- */
8600 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8602 if (network_playing && !network_player_action_received)
8606 printf("DEBUG: try to get network player actions in time\n");
8610 #if defined(NETWORK_AVALIABLE)
8611 /* last chance to get network player actions without main loop delay */
8615 if (game_status != GAME_MODE_PLAYING)
8618 if (!network_player_action_received)
8622 printf("DEBUG: failed to get network player actions in time\n");
8633 printf("::: getting new tape action [%d]\n", FrameCounter);
8636 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8639 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8640 if (recorded_player_action == NULL && tape.pausing)
8645 printf("::: %d\n", stored_player[0].action);
8649 if (recorded_player_action != NULL)
8650 for (i = 0; i < MAX_PLAYERS; i++)
8651 stored_player[i].action = recorded_player_action[i];
8654 for (i = 0; i < MAX_PLAYERS; i++)
8656 summarized_player_action |= stored_player[i].action;
8658 if (!network_playing)
8659 stored_player[i].effective_action = stored_player[i].action;
8662 #if defined(NETWORK_AVALIABLE)
8663 if (network_playing)
8664 SendToServer_MovePlayer(summarized_player_action);
8667 if (!options.network && !setup.team_mode)
8668 local_player->effective_action = summarized_player_action;
8671 if (recorded_player_action != NULL)
8672 for (i = 0; i < MAX_PLAYERS; i++)
8673 stored_player[i].effective_action = recorded_player_action[i];
8677 for (i = 0; i < MAX_PLAYERS; i++)
8679 tape_action[i] = stored_player[i].effective_action;
8681 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8682 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8685 /* only save actions from input devices, but not programmed actions */
8687 TapeRecordAction(tape_action);
8690 for (i = 0; i < MAX_PLAYERS; i++)
8692 int actual_player_action = stored_player[i].effective_action;
8695 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8696 - rnd_equinox_tetrachloride 048
8697 - rnd_equinox_tetrachloride_ii 096
8698 - rnd_emanuel_schmieg 002
8699 - doctor_sloan_ww 001, 020
8701 if (stored_player[i].MovPos == 0)
8702 CheckGravityMovement(&stored_player[i]);
8706 /* overwrite programmed action with tape action */
8707 if (stored_player[i].programmed_action)
8708 actual_player_action = stored_player[i].programmed_action;
8712 if (stored_player[i].programmed_action)
8713 printf("::: %d\n", stored_player[i].programmed_action);
8716 if (recorded_player_action)
8719 if (stored_player[i].programmed_action &&
8720 stored_player[i].programmed_action != recorded_player_action[i])
8721 printf("::: %d: %d <-> %d\n", i,
8722 stored_player[i].programmed_action, recorded_player_action[i]);
8726 actual_player_action = recorded_player_action[i];
8731 /* overwrite tape action with programmed action */
8732 if (stored_player[i].programmed_action)
8733 actual_player_action = stored_player[i].programmed_action;
8738 printf("::: action: %d: %x [%d]\n",
8739 stored_player[i].MovPos, actual_player_action, FrameCounter);
8743 PlayerActions(&stored_player[i], actual_player_action);
8745 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8747 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8748 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8751 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8756 TapeRecordAction(tape_action);
8759 network_player_action_received = FALSE;
8761 ScrollScreen(NULL, SCROLL_GO_ON);
8767 for (i = 0; i < MAX_PLAYERS; i++)
8768 stored_player[i].Frame++;
8772 /* for backwards compatibility, the following code emulates a fixed bug that
8773 occured when pushing elements (causing elements that just made their last
8774 pushing step to already (if possible) make their first falling step in the
8775 same game frame, which is bad); this code is also needed to use the famous
8776 "spring push bug" which is used in older levels and might be wanted to be
8777 used also in newer levels, but in this case the buggy pushing code is only
8778 affecting the "spring" element and no other elements */
8781 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8783 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8786 for (i = 0; i < MAX_PLAYERS; i++)
8788 struct PlayerInfo *player = &stored_player[i];
8793 if (player->active && player->is_pushing && player->is_moving &&
8795 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8796 Feld[x][y] == EL_SPRING))
8798 if (player->active && player->is_pushing && player->is_moving &&
8802 ContinueMoving(x, y);
8804 /* continue moving after pushing (this is actually a bug) */
8805 if (!IS_MOVING(x, y))
8814 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8816 Changed[x][y] = CE_BITMASK_DEFAULT;
8817 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8819 #if USE_NEW_BLOCK_STYLE
8820 /* this must be handled before main playfield loop */
8821 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8824 if (MovDelay[x][y] <= 0)
8830 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8832 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8833 printf("GameActions(): This should never happen!\n");
8835 ChangePage[x][y] = -1;
8840 if (WasJustMoving[x][y] > 0)
8841 WasJustMoving[x][y]--;
8842 if (WasJustFalling[x][y] > 0)
8843 WasJustFalling[x][y]--;
8844 if (CheckCollision[x][y] > 0)
8845 CheckCollision[x][y]--;
8850 /* reset finished pushing action (not done in ContinueMoving() to allow
8851 continous pushing animation for elements with zero push delay) */
8852 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8854 ResetGfxAnimation(x, y);
8855 DrawLevelField(x, y);
8860 if (IS_BLOCKED(x, y))
8864 Blocked2Moving(x, y, &oldx, &oldy);
8865 if (!IS_MOVING(oldx, oldy))
8867 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8868 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8869 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8870 printf("GameActions(): This should never happen!\n");
8876 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8878 element = Feld[x][y];
8880 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8882 graphic = el2img(element);
8888 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8890 element = graphic = 0;
8894 if (graphic_info[graphic].anim_global_sync)
8895 GfxFrame[x][y] = FrameCounter;
8897 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8898 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8899 ResetRandomAnimationValue(x, y);
8901 SetRandomAnimationValue(x, y);
8904 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8907 if (IS_INACTIVE(element))
8909 if (IS_ANIMATED(graphic))
8910 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8916 /* this may take place after moving, so 'element' may have changed */
8918 if (IS_CHANGING(x, y))
8920 if (IS_CHANGING(x, y) &&
8921 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8925 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8926 element_info[element].event_page_nr[CE_DELAY]);
8928 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8931 element = Feld[x][y];
8932 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8936 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8941 element = Feld[x][y];
8942 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8944 if (element == EL_MOLE)
8945 printf("::: %d, %d, %d [%d]\n",
8946 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8950 if (element == EL_YAMYAM)
8951 printf("::: %d, %d, %d\n",
8952 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8956 if (IS_ANIMATED(graphic) &&
8960 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8963 if (element == EL_BUG)
8964 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8968 if (element == EL_MOLE)
8969 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8973 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8974 EdelsteinFunkeln(x, y);
8976 else if ((element == EL_ACID ||
8977 element == EL_EXIT_OPEN ||
8978 element == EL_SP_EXIT_OPEN ||
8979 element == EL_SP_TERMINAL ||
8980 element == EL_SP_TERMINAL_ACTIVE ||
8981 element == EL_EXTRA_TIME ||
8982 element == EL_SHIELD_NORMAL ||
8983 element == EL_SHIELD_DEADLY) &&
8984 IS_ANIMATED(graphic))
8985 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8986 else if (IS_MOVING(x, y))
8987 ContinueMoving(x, y);
8988 else if (IS_ACTIVE_BOMB(element))
8989 CheckDynamite(x, y);
8991 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8992 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8994 else if (element == EL_AMOEBA_GROWING)
8995 AmoebeWaechst(x, y);
8996 else if (element == EL_AMOEBA_SHRINKING)
8997 AmoebaDisappearing(x, y);
8999 #if !USE_NEW_AMOEBA_CODE
9000 else if (IS_AMOEBALIVE(element))
9001 AmoebeAbleger(x, y);
9004 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9006 else if (element == EL_EXIT_CLOSED)
9008 else if (element == EL_SP_EXIT_CLOSED)
9010 else if (element == EL_EXPANDABLE_WALL_GROWING)
9012 else if (element == EL_EXPANDABLE_WALL ||
9013 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9014 element == EL_EXPANDABLE_WALL_VERTICAL ||
9015 element == EL_EXPANDABLE_WALL_ANY)
9017 else if (element == EL_FLAMES)
9018 CheckForDragon(x, y);
9020 else if (IS_AUTO_CHANGING(element))
9021 ChangeElement(x, y);
9023 else if (element == EL_EXPLOSION)
9024 ; /* drawing of correct explosion animation is handled separately */
9025 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9029 /* this may take place after moving, so 'element' may have changed */
9030 if (IS_AUTO_CHANGING(Feld[x][y]))
9031 ChangeElement(x, y);
9034 if (IS_BELT_ACTIVE(element))
9035 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9037 if (game.magic_wall_active)
9039 int jx = local_player->jx, jy = local_player->jy;
9041 /* play the element sound at the position nearest to the player */
9042 if ((element == EL_MAGIC_WALL_FULL ||
9043 element == EL_MAGIC_WALL_ACTIVE ||
9044 element == EL_MAGIC_WALL_EMPTYING ||
9045 element == EL_BD_MAGIC_WALL_FULL ||
9046 element == EL_BD_MAGIC_WALL_ACTIVE ||
9047 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9048 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9056 #if USE_NEW_AMOEBA_CODE
9057 /* new experimental amoeba growth stuff */
9059 if (!(FrameCounter % 8))
9062 static unsigned long random = 1684108901;
9064 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9067 x = (random >> 10) % lev_fieldx;
9068 y = (random >> 20) % lev_fieldy;
9070 x = RND(lev_fieldx);
9071 y = RND(lev_fieldy);
9073 element = Feld[x][y];
9076 if (!IS_PLAYER(x,y) &&
9077 (element == EL_EMPTY ||
9078 CAN_GROW_INTO(element) ||
9079 element == EL_QUICKSAND_EMPTY ||
9080 element == EL_ACID_SPLASH_LEFT ||
9081 element == EL_ACID_SPLASH_RIGHT))
9083 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9084 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9085 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9086 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9087 Feld[x][y] = EL_AMOEBA_DROP;
9090 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9091 if (!IS_PLAYER(x,y) &&
9092 (element == EL_EMPTY ||
9093 element == EL_SAND ||
9094 element == EL_QUICKSAND_EMPTY ||
9095 element == EL_ACID_SPLASH_LEFT ||
9096 element == EL_ACID_SPLASH_RIGHT))
9098 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9099 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9100 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9101 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9102 Feld[x][y] = EL_AMOEBA_DROP;
9106 random = random * 129 + 1;
9112 if (game.explosions_delayed)
9115 game.explosions_delayed = FALSE;
9117 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9119 element = Feld[x][y];
9121 if (ExplodeField[x][y])
9122 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9123 else if (element == EL_EXPLOSION)
9124 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9126 ExplodeField[x][y] = EX_TYPE_NONE;
9129 game.explosions_delayed = TRUE;
9132 if (game.magic_wall_active)
9134 if (!(game.magic_wall_time_left % 4))
9136 int element = Feld[magic_wall_x][magic_wall_y];
9138 if (element == EL_BD_MAGIC_WALL_FULL ||
9139 element == EL_BD_MAGIC_WALL_ACTIVE ||
9140 element == EL_BD_MAGIC_WALL_EMPTYING)
9141 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9143 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9146 if (game.magic_wall_time_left > 0)
9148 game.magic_wall_time_left--;
9149 if (!game.magic_wall_time_left)
9151 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9153 element = Feld[x][y];
9155 if (element == EL_MAGIC_WALL_ACTIVE ||
9156 element == EL_MAGIC_WALL_FULL)
9158 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9159 DrawLevelField(x, y);
9161 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9162 element == EL_BD_MAGIC_WALL_FULL)
9164 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9165 DrawLevelField(x, y);
9169 game.magic_wall_active = FALSE;
9174 if (game.light_time_left > 0)
9176 game.light_time_left--;
9178 if (game.light_time_left == 0)
9179 RedrawAllLightSwitchesAndInvisibleElements();
9182 if (game.timegate_time_left > 0)
9184 game.timegate_time_left--;
9186 if (game.timegate_time_left == 0)
9187 CloseAllOpenTimegates();
9190 for (i = 0; i < MAX_PLAYERS; i++)
9192 struct PlayerInfo *player = &stored_player[i];
9194 if (SHIELD_ON(player))
9196 if (player->shield_deadly_time_left)
9197 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9198 else if (player->shield_normal_time_left)
9199 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9203 if (TimeFrames >= FRAMES_PER_SECOND)
9208 for (i = 0; i < MAX_PLAYERS; i++)
9210 struct PlayerInfo *player = &stored_player[i];
9212 if (SHIELD_ON(player))
9214 player->shield_normal_time_left--;
9216 if (player->shield_deadly_time_left > 0)
9217 player->shield_deadly_time_left--;
9221 if (!level.use_step_counter)
9229 if (TimeLeft <= 10 && setup.time_limit)
9230 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9232 DrawGameValue_Time(TimeLeft);
9234 if (!TimeLeft && setup.time_limit)
9235 for (i = 0; i < MAX_PLAYERS; i++)
9236 KillHero(&stored_player[i]);
9238 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9239 DrawGameValue_Time(TimePlayed);
9242 if (tape.recording || tape.playing)
9243 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9247 PlayAllPlayersSound();
9249 if (options.debug) /* calculate frames per second */
9251 static unsigned long fps_counter = 0;
9252 static int fps_frames = 0;
9253 unsigned long fps_delay_ms = Counter() - fps_counter;
9257 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9259 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9262 fps_counter = Counter();
9265 redraw_mask |= REDRAW_FPS;
9269 if (stored_player[0].jx != stored_player[0].last_jx ||
9270 stored_player[0].jy != stored_player[0].last_jy)
9271 printf("::: %d, %d, %d, %d, %d\n",
9272 stored_player[0].MovDir,
9273 stored_player[0].MovPos,
9274 stored_player[0].GfxPos,
9275 stored_player[0].Frame,
9276 stored_player[0].StepFrame);
9279 #if USE_NEW_MOVE_DELAY
9280 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9285 for (i = 0; i < MAX_PLAYERS; i++)
9288 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9290 stored_player[i].Frame += move_frames;
9292 if (stored_player[i].MovPos != 0)
9293 stored_player[i].StepFrame += move_frames;
9295 #if USE_NEW_MOVE_DELAY
9296 if (stored_player[i].move_delay > 0)
9297 stored_player[i].move_delay--;
9300 if (stored_player[i].drop_delay > 0)
9301 stored_player[i].drop_delay--;
9306 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9308 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9310 local_player->show_envelope = 0;
9314 #if USE_NEW_RANDOMIZE
9315 /* use random number generator in every frame to make it less predictable */
9316 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9321 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9323 int min_x = x, min_y = y, max_x = x, max_y = y;
9326 for (i = 0; i < MAX_PLAYERS; i++)
9328 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9330 if (!stored_player[i].active || &stored_player[i] == player)
9333 min_x = MIN(min_x, jx);
9334 min_y = MIN(min_y, jy);
9335 max_x = MAX(max_x, jx);
9336 max_y = MAX(max_y, jy);
9339 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9342 static boolean AllPlayersInVisibleScreen()
9346 for (i = 0; i < MAX_PLAYERS; i++)
9348 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9350 if (!stored_player[i].active)
9353 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9360 void ScrollLevel(int dx, int dy)
9362 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9365 BlitBitmap(drawto_field, drawto_field,
9366 FX + TILEX * (dx == -1) - softscroll_offset,
9367 FY + TILEY * (dy == -1) - softscroll_offset,
9368 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9369 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9370 FX + TILEX * (dx == 1) - softscroll_offset,
9371 FY + TILEY * (dy == 1) - softscroll_offset);
9375 x = (dx == 1 ? BX1 : BX2);
9376 for (y = BY1; y <= BY2; y++)
9377 DrawScreenField(x, y);
9382 y = (dy == 1 ? BY1 : BY2);
9383 for (x = BX1; x <= BX2; x++)
9384 DrawScreenField(x, y);
9387 redraw_mask |= REDRAW_FIELD;
9391 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9393 int nextx = x + dx, nexty = y + dy;
9394 int element = Feld[x][y];
9397 element != EL_SP_PORT_LEFT &&
9398 element != EL_SP_GRAVITY_PORT_LEFT &&
9399 element != EL_SP_PORT_HORIZONTAL &&
9400 element != EL_SP_PORT_ANY) ||
9402 element != EL_SP_PORT_RIGHT &&
9403 element != EL_SP_GRAVITY_PORT_RIGHT &&
9404 element != EL_SP_PORT_HORIZONTAL &&
9405 element != EL_SP_PORT_ANY) ||
9407 element != EL_SP_PORT_UP &&
9408 element != EL_SP_GRAVITY_PORT_UP &&
9409 element != EL_SP_PORT_VERTICAL &&
9410 element != EL_SP_PORT_ANY) ||
9412 element != EL_SP_PORT_DOWN &&
9413 element != EL_SP_GRAVITY_PORT_DOWN &&
9414 element != EL_SP_PORT_VERTICAL &&
9415 element != EL_SP_PORT_ANY) ||
9416 !IN_LEV_FIELD(nextx, nexty) ||
9417 !IS_FREE(nextx, nexty))
9424 static boolean canFallDown(struct PlayerInfo *player)
9426 int jx = player->jx, jy = player->jy;
9428 return (IN_LEV_FIELD(jx, jy + 1) &&
9429 (IS_FREE(jx, jy + 1) ||
9430 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9431 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9432 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9435 static boolean canPassField(int x, int y, int move_dir)
9437 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9438 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9439 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9442 int element = Feld[x][y];
9444 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9445 !CAN_MOVE(element) &&
9446 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9447 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9448 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9451 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9453 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9454 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9455 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9459 int nextx = newx + dx;
9460 int nexty = newy + dy;
9464 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9465 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9467 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9469 (IS_DIGGABLE(Feld[newx][newy]) ||
9470 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9471 canPassField(newx, newy, move_dir)));
9474 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9475 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9476 (IS_DIGGABLE(Feld[newx][newy]) ||
9477 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9478 canPassField(newx, newy, move_dir)));
9481 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9482 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9483 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9484 canPassField(newx, newy, move_dir)));
9486 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9487 (IS_DIGGABLE(Feld[newx][newy]) ||
9488 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9489 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9490 !CAN_MOVE(Feld[newx][newy]) &&
9491 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9492 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9493 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9499 static void CheckGravityMovement(struct PlayerInfo *player)
9501 if (game.gravity && !player->programmed_action)
9504 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9505 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9507 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9508 int move_dir_vertical = player->action & MV_VERTICAL;
9512 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9514 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9517 int jx = player->jx, jy = player->jy;
9519 boolean player_is_moving_to_valid_field =
9520 (!player_is_snapping &&
9521 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9522 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9526 (player->last_move_dir & MV_HORIZONTAL ?
9527 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9528 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9532 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9533 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9534 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9535 int new_jx = jx + dx, new_jy = jy + dy;
9536 int nextx = new_jx + dx, nexty = new_jy + dy;
9542 boolean player_can_fall_down = canFallDown(player);
9544 boolean player_can_fall_down =
9545 (IN_LEV_FIELD(jx, jy + 1) &&
9546 (IS_FREE(jx, jy + 1) ||
9547 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9551 boolean player_can_fall_down =
9552 (IN_LEV_FIELD(jx, jy + 1) &&
9553 (IS_FREE(jx, jy + 1)));
9557 boolean player_is_moving_to_valid_field =
9560 !player_is_snapping &&
9564 IN_LEV_FIELD(new_jx, new_jy) &&
9565 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9566 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9567 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9568 IN_LEV_FIELD(nextx, nexty) &&
9569 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9571 IN_LEV_FIELD(new_jx, new_jy) &&
9572 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9573 Feld[new_jx][new_jy] == EL_SAND ||
9574 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9575 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9576 /* !!! extend EL_SAND to anything diggable !!! */
9582 boolean player_is_standing_on_valid_field =
9583 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9584 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9588 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9589 player_can_fall_down,
9590 player_is_standing_on_valid_field,
9591 player_is_moving_to_valid_field,
9592 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9593 player->effective_action,
9594 player->can_fall_into_acid);
9597 if (player_can_fall_down &&
9599 !player_is_standing_on_valid_field &&
9601 !player_is_moving_to_valid_field)
9604 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9605 jx, jy, FrameCounter);
9608 player->programmed_action = MV_DOWN;
9613 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9616 return CheckGravityMovement(player);
9619 if (game.gravity && !player->programmed_action)
9621 int jx = player->jx, jy = player->jy;
9622 boolean field_under_player_is_free =
9623 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9624 boolean player_is_standing_on_valid_field =
9625 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9626 (IS_WALKABLE(Feld[jx][jy]) &&
9627 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9629 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9630 player->programmed_action = MV_DOWN;
9636 -----------------------------------------------------------------------------
9637 dx, dy: direction (non-diagonal) to try to move the player to
9638 real_dx, real_dy: direction as read from input device (can be diagonal)
9641 boolean MovePlayerOneStep(struct PlayerInfo *player,
9642 int dx, int dy, int real_dx, int real_dy)
9645 static int trigger_sides[4][2] =
9647 /* enter side leave side */
9648 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9649 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9650 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9651 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9653 int move_direction = (dx == -1 ? MV_LEFT :
9654 dx == +1 ? MV_RIGHT :
9656 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9657 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9658 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9660 int jx = player->jx, jy = player->jy;
9661 int new_jx = jx + dx, new_jy = jy + dy;
9665 if (!player->active || (!dx && !dy))
9666 return MF_NO_ACTION;
9668 player->MovDir = (dx < 0 ? MV_LEFT :
9671 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9673 if (!IN_LEV_FIELD(new_jx, new_jy))
9674 return MF_NO_ACTION;
9676 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9677 return MF_NO_ACTION;
9680 element = MovingOrBlocked2Element(new_jx, new_jy);
9682 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9685 if (DONT_RUN_INTO(element))
9687 if (element == EL_ACID && dx == 0 && dy == 1)
9689 SplashAcid(new_jx, new_jy);
9690 Feld[jx][jy] = EL_PLAYER_1;
9691 InitMovingField(jx, jy, MV_DOWN);
9692 Store[jx][jy] = EL_ACID;
9693 ContinueMoving(jx, jy);
9697 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9702 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9703 if (can_move != MF_MOVING)
9706 /* check if DigField() has caused relocation of the player */
9707 if (player->jx != jx || player->jy != jy)
9708 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9710 StorePlayer[jx][jy] = 0;
9711 player->last_jx = jx;
9712 player->last_jy = jy;
9713 player->jx = new_jx;
9714 player->jy = new_jy;
9715 StorePlayer[new_jx][new_jy] = player->element_nr;
9718 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9720 player->step_counter++;
9723 player->drop_delay = 0;
9726 PlayerVisit[jx][jy] = FrameCounter;
9728 ScrollPlayer(player, SCROLL_INIT);
9731 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9733 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9735 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9738 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9740 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9741 CE_OTHER_GETS_ENTERED, enter_side);
9742 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9743 CE_ENTERED_BY_PLAYER, enter_side);
9750 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9752 int jx = player->jx, jy = player->jy;
9753 int old_jx = jx, old_jy = jy;
9754 int moved = MF_NO_ACTION;
9757 if (!player->active)
9762 if (player->MovPos == 0)
9764 player->is_moving = FALSE;
9765 player->is_digging = FALSE;
9766 player->is_collecting = FALSE;
9767 player->is_snapping = FALSE;
9768 player->is_pushing = FALSE;
9774 if (!player->active || (!dx && !dy))
9779 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9787 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9788 player->move_delay + player->move_delay_value);
9791 #if USE_NEW_MOVE_DELAY
9792 if (player->move_delay > 0)
9794 if (!FrameReached(&player->move_delay, player->move_delay_value))
9798 printf("::: can NOT move\n");
9804 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9805 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9812 printf("::: COULD move now\n");
9815 #if USE_NEW_MOVE_DELAY
9816 player->move_delay = -1; /* set to "uninitialized" value */
9819 /* store if player is automatically moved to next field */
9820 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9822 /* remove the last programmed player action */
9823 player->programmed_action = 0;
9827 /* should only happen if pre-1.2 tape recordings are played */
9828 /* this is only for backward compatibility */
9830 int original_move_delay_value = player->move_delay_value;
9833 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9837 /* scroll remaining steps with finest movement resolution */
9838 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9840 while (player->MovPos)
9842 ScrollPlayer(player, SCROLL_GO_ON);
9843 ScrollScreen(NULL, SCROLL_GO_ON);
9845 #if USE_NEW_MOVE_DELAY
9846 AdvanceFrameAndPlayerCounters(player->index_nr);
9855 player->move_delay_value = original_move_delay_value;
9858 if (player->last_move_dir & MV_HORIZONTAL)
9860 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9861 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9865 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9866 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9872 if (moved & MF_MOVING && !ScreenMovPos &&
9873 (player == local_player || !options.network))
9875 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9876 int offset = (setup.scroll_delay ? 3 : 0);
9878 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9880 /* actual player has left the screen -- scroll in that direction */
9881 if (jx != old_jx) /* player has moved horizontally */
9882 scroll_x += (jx - old_jx);
9883 else /* player has moved vertically */
9884 scroll_y += (jy - old_jy);
9888 if (jx != old_jx) /* player has moved horizontally */
9890 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9891 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9892 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9894 /* don't scroll over playfield boundaries */
9895 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9896 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9898 /* don't scroll more than one field at a time */
9899 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9901 /* don't scroll against the player's moving direction */
9902 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9903 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9904 scroll_x = old_scroll_x;
9906 else /* player has moved vertically */
9908 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9909 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9910 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9912 /* don't scroll over playfield boundaries */
9913 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9914 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9916 /* don't scroll more than one field at a time */
9917 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9919 /* don't scroll against the player's moving direction */
9920 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9921 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9922 scroll_y = old_scroll_y;
9926 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9928 if (!options.network && !AllPlayersInVisibleScreen())
9930 scroll_x = old_scroll_x;
9931 scroll_y = old_scroll_y;
9935 ScrollScreen(player, SCROLL_INIT);
9936 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9943 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9945 if (!(moved & MF_MOVING) && !player->is_pushing)
9950 player->StepFrame = 0;
9952 if (moved & MF_MOVING)
9955 printf("::: REALLY moves now\n");
9958 if (old_jx != jx && old_jy == jy)
9959 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9960 else if (old_jx == jx && old_jy != jy)
9961 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9963 DrawLevelField(jx, jy); /* for "crumbled sand" */
9965 player->last_move_dir = player->MovDir;
9966 player->is_moving = TRUE;
9968 player->is_snapping = FALSE;
9972 player->is_switching = FALSE;
9975 player->is_dropping = FALSE;
9979 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9982 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9985 int move_direction = player->MovDir;
9987 int enter_side = MV_DIR_OPPOSITE(move_direction);
9988 int leave_side = move_direction;
9990 static int trigger_sides[4][2] =
9992 /* enter side leave side */
9993 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9994 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9995 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9996 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9998 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9999 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10001 int old_element = Feld[old_jx][old_jy];
10002 int new_element = Feld[jx][jy];
10005 /* !!! TEST ONLY !!! */
10006 if (IS_CUSTOM_ELEMENT(old_element))
10007 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10009 player->index_bit, leave_side);
10011 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10012 CE_OTHER_GETS_LEFT,
10013 player->index_bit, leave_side);
10015 if (IS_CUSTOM_ELEMENT(new_element))
10016 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10017 player->index_bit, enter_side);
10019 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10020 CE_OTHER_GETS_ENTERED,
10021 player->index_bit, enter_side);
10031 CheckGravityMovementWhenNotMoving(player);
10034 player->last_move_dir = MV_NO_MOVING;
10036 player->is_moving = FALSE;
10038 #if USE_NEW_MOVE_STYLE
10039 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10040 /* ensure that the player is also allowed to move in the next frame */
10041 /* (currently, the player is forced to wait eight frames before he can try
10044 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10045 player->move_delay = 0; /* allow direct movement in the next frame */
10049 #if USE_NEW_MOVE_DELAY
10050 if (player->move_delay == -1) /* not yet initialized by DigField() */
10051 player->move_delay = player->move_delay_value;
10054 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10056 TestIfHeroTouchesBadThing(jx, jy);
10057 TestIfPlayerTouchesCustomElement(jx, jy);
10060 if (!player->active)
10061 RemoveHero(player);
10066 void ScrollPlayer(struct PlayerInfo *player, int mode)
10068 int jx = player->jx, jy = player->jy;
10069 int last_jx = player->last_jx, last_jy = player->last_jy;
10070 int move_stepsize = TILEX / player->move_delay_value;
10072 if (!player->active || !player->MovPos)
10075 if (mode == SCROLL_INIT)
10077 player->actual_frame_counter = FrameCounter;
10078 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10081 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10083 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10084 player->block_delay);
10087 #if USE_NEW_BLOCK_STYLE
10090 if (player->block_delay <= 0)
10091 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10094 if (player->block_delay > 0 &&
10095 Feld[last_jx][last_jy] == EL_EMPTY)
10097 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10098 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10101 #if USE_NEW_MOVE_STYLE
10102 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10103 player->block_last_field) &&
10104 Feld[last_jx][last_jy] == EL_EMPTY)
10105 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10107 if (Feld[last_jx][last_jy] == EL_EMPTY)
10108 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10113 DrawPlayer(player);
10118 else if (!FrameReached(&player->actual_frame_counter, 1))
10121 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10122 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10124 #if USE_NEW_BLOCK_STYLE
10126 if (!player->block_last_field &&
10127 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10129 RemoveField(last_jx, last_jy);
10131 Feld[last_jx][last_jy] = EL_EMPTY;
10135 /* before DrawPlayer() to draw correct player graphic for this case */
10136 if (player->MovPos == 0)
10137 CheckGravityMovement(player);
10140 DrawPlayer(player); /* needed here only to cleanup last field */
10143 if (player->MovPos == 0) /* player reached destination field */
10146 if (player->move_delay_reset_counter > 0)
10148 player->move_delay_reset_counter--;
10150 if (player->move_delay_reset_counter == 0)
10152 /* continue with normal speed after quickly moving through gate */
10153 HALVE_PLAYER_SPEED(player);
10155 /* be able to make the next move without delay */
10156 player->move_delay = 0;
10160 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10162 /* continue with normal speed after quickly moving through gate */
10163 HALVE_PLAYER_SPEED(player);
10165 /* be able to make the next move without delay */
10166 player->move_delay = 0;
10170 #if USE_NEW_BLOCK_STYLE
10172 if (player->block_last_field &&
10173 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10175 RemoveField(last_jx, last_jy);
10177 Feld[last_jx][last_jy] = EL_EMPTY;
10181 player->last_jx = jx;
10182 player->last_jy = jy;
10184 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10185 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10186 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10188 DrawPlayer(player); /* needed here only to cleanup last field */
10189 RemoveHero(player);
10191 if (local_player->friends_still_needed == 0 ||
10192 IS_SP_ELEMENT(Feld[jx][jy]))
10193 player->LevelSolved = player->GameOver = TRUE;
10197 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10198 /* this breaks one level: "machine", level 000 */
10200 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10203 int move_direction = player->MovDir;
10205 int enter_side = MV_DIR_OPPOSITE(move_direction);
10206 int leave_side = move_direction;
10208 static int trigger_sides[4][2] =
10210 /* enter side leave side */
10211 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10212 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10213 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10214 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10216 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10217 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10219 int old_jx = last_jx;
10220 int old_jy = last_jy;
10221 int old_element = Feld[old_jx][old_jy];
10222 int new_element = Feld[jx][jy];
10225 /* !!! TEST ONLY !!! */
10226 if (IS_CUSTOM_ELEMENT(old_element))
10227 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10229 player->index_bit, leave_side);
10231 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10232 CE_OTHER_GETS_LEFT,
10233 player->index_bit, leave_side);
10235 if (IS_CUSTOM_ELEMENT(new_element))
10236 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10237 player->index_bit, enter_side);
10239 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10240 CE_OTHER_GETS_ENTERED,
10241 player->index_bit, enter_side);
10247 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10249 TestIfHeroTouchesBadThing(jx, jy);
10250 TestIfPlayerTouchesCustomElement(jx, jy);
10253 /* needed because pushed element has not yet reached its destination,
10254 so it would trigger a change event at its previous field location */
10255 if (!player->is_pushing)
10257 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10260 if (!player->active)
10261 RemoveHero(player);
10264 if (level.use_step_counter)
10274 if (TimeLeft <= 10 && setup.time_limit)
10275 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10277 DrawGameValue_Time(TimeLeft);
10279 if (!TimeLeft && setup.time_limit)
10280 for (i = 0; i < MAX_PLAYERS; i++)
10281 KillHero(&stored_player[i]);
10283 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10284 DrawGameValue_Time(TimePlayed);
10287 if (tape.single_step && tape.recording && !tape.pausing &&
10288 !player->programmed_action)
10289 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10293 void ScrollScreen(struct PlayerInfo *player, int mode)
10295 static unsigned long screen_frame_counter = 0;
10297 if (mode == SCROLL_INIT)
10299 /* set scrolling step size according to actual player's moving speed */
10300 ScrollStepSize = TILEX / player->move_delay_value;
10302 screen_frame_counter = FrameCounter;
10303 ScreenMovDir = player->MovDir;
10304 ScreenMovPos = player->MovPos;
10305 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10308 else if (!FrameReached(&screen_frame_counter, 1))
10313 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10314 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10315 redraw_mask |= REDRAW_FIELD;
10318 ScreenMovDir = MV_NO_MOVING;
10321 void TestIfPlayerTouchesCustomElement(int x, int y)
10323 static int xy[4][2] =
10330 static int trigger_sides[4][2] =
10332 /* center side border side */
10333 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10334 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10335 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10336 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10338 static int touch_dir[4] =
10340 MV_LEFT | MV_RIGHT,
10345 int center_element = Feld[x][y]; /* should always be non-moving! */
10348 for (i = 0; i < NUM_DIRECTIONS; i++)
10350 int xx = x + xy[i][0];
10351 int yy = y + xy[i][1];
10352 int center_side = trigger_sides[i][0];
10353 int border_side = trigger_sides[i][1];
10354 int border_element;
10356 if (!IN_LEV_FIELD(xx, yy))
10359 if (IS_PLAYER(x, y))
10361 struct PlayerInfo *player = PLAYERINFO(x, y);
10363 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10364 border_element = Feld[xx][yy]; /* may be moving! */
10365 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10366 border_element = Feld[xx][yy];
10367 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10368 border_element = MovingOrBlocked2Element(xx, yy);
10370 continue; /* center and border element do not touch */
10373 /* !!! TEST ONLY !!! */
10374 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10375 player->index_bit, border_side);
10376 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10377 CE_OTHER_GETS_TOUCHED,
10378 player->index_bit, border_side);
10380 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10381 CE_OTHER_GETS_TOUCHED,
10382 player->index_bit, border_side);
10383 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10384 player->index_bit, border_side);
10387 else if (IS_PLAYER(xx, yy))
10389 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10391 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10393 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10394 continue; /* center and border element do not touch */
10398 /* !!! TEST ONLY !!! */
10399 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10400 player->index_bit, center_side);
10401 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10402 CE_OTHER_GETS_TOUCHED,
10403 player->index_bit, center_side);
10405 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10406 CE_OTHER_GETS_TOUCHED,
10407 player->index_bit, center_side);
10408 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10409 player->index_bit, center_side);
10417 void TestIfElementTouchesCustomElement(int x, int y)
10419 static int xy[4][2] =
10426 static int trigger_sides[4][2] =
10428 /* center side border side */
10429 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10430 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10431 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10432 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10434 static int touch_dir[4] =
10436 MV_LEFT | MV_RIGHT,
10441 boolean change_center_element = FALSE;
10442 int center_element_change_page = 0;
10443 int center_element = Feld[x][y]; /* should always be non-moving! */
10444 int border_trigger_element = EL_UNDEFINED;
10447 for (i = 0; i < NUM_DIRECTIONS; i++)
10449 int xx = x + xy[i][0];
10450 int yy = y + xy[i][1];
10451 int center_side = trigger_sides[i][0];
10452 int border_side = trigger_sides[i][1];
10453 int border_element;
10455 if (!IN_LEV_FIELD(xx, yy))
10458 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10459 border_element = Feld[xx][yy]; /* may be moving! */
10460 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10461 border_element = Feld[xx][yy];
10462 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10463 border_element = MovingOrBlocked2Element(xx, yy);
10465 continue; /* center and border element do not touch */
10467 /* check for change of center element (but change it only once) */
10468 if (IS_CUSTOM_ELEMENT(center_element) &&
10469 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10470 !change_center_element)
10472 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10474 struct ElementChangeInfo *change =
10475 &element_info[center_element].change_page[j];
10477 if (change->can_change &&
10478 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10479 change->trigger_side & border_side &&
10481 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10483 change->trigger_element == border_element
10487 change_center_element = TRUE;
10488 center_element_change_page = j;
10489 border_trigger_element = border_element;
10496 /* check for change of border element */
10497 if (IS_CUSTOM_ELEMENT(border_element) &&
10498 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10500 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10502 struct ElementChangeInfo *change =
10503 &element_info[border_element].change_page[j];
10505 if (change->can_change &&
10506 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10507 change->trigger_side & center_side &&
10509 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10511 change->trigger_element == center_element
10516 printf("::: border_element %d, %d\n", x, y);
10519 CheckElementChangeByPage(xx, yy, border_element, center_element,
10520 CE_OTHER_IS_TOUCHING, j);
10527 if (change_center_element)
10530 printf("::: center_element %d, %d\n", x, y);
10533 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10534 CE_OTHER_IS_TOUCHING, center_element_change_page);
10538 void TestIfElementHitsCustomElement(int x, int y, int direction)
10540 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10541 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10542 int hitx = x + dx, hity = y + dy;
10543 int hitting_element = Feld[x][y];
10544 int touched_element;
10546 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10547 !IS_FREE(hitx, hity) &&
10548 (!IS_MOVING(hitx, hity) ||
10549 MovDir[hitx][hity] != direction ||
10550 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10553 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10557 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10561 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10562 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10564 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10565 CE_HITTING_SOMETHING, direction);
10567 if (IN_LEV_FIELD(hitx, hity))
10569 int opposite_direction = MV_DIR_OPPOSITE(direction);
10570 int hitting_side = direction;
10571 int touched_side = opposite_direction;
10573 int touched_element = MovingOrBlocked2Element(hitx, hity);
10576 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10577 MovDir[hitx][hity] != direction ||
10578 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10587 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10588 CE_HIT_BY_SOMETHING, opposite_direction);
10590 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10591 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10593 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10595 struct ElementChangeInfo *change =
10596 &element_info[hitting_element].change_page[i];
10598 if (change->can_change &&
10599 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10600 change->trigger_side & touched_side &&
10603 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10605 change->trigger_element == touched_element
10609 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10610 CE_OTHER_IS_HITTING, i);
10616 if (IS_CUSTOM_ELEMENT(touched_element) &&
10617 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10619 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10621 struct ElementChangeInfo *change =
10622 &element_info[touched_element].change_page[i];
10624 if (change->can_change &&
10625 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10626 change->trigger_side & hitting_side &&
10628 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10630 change->trigger_element == hitting_element
10634 CheckElementChangeByPage(hitx, hity, touched_element,
10635 hitting_element, CE_OTHER_GETS_HIT, i);
10645 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10647 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10648 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10649 int hitx = x + dx, hity = y + dy;
10650 int hitting_element = Feld[x][y];
10651 int touched_element;
10653 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10654 !IS_FREE(hitx, hity) &&
10655 (!IS_MOVING(hitx, hity) ||
10656 MovDir[hitx][hity] != direction ||
10657 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10660 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10664 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10668 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10669 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10671 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10672 EP_CAN_SMASH_EVERYTHING, direction);
10674 if (IN_LEV_FIELD(hitx, hity))
10676 int opposite_direction = MV_DIR_OPPOSITE(direction);
10677 int hitting_side = direction;
10678 int touched_side = opposite_direction;
10680 int touched_element = MovingOrBlocked2Element(hitx, hity);
10683 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10684 MovDir[hitx][hity] != direction ||
10685 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10694 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10695 CE_SMASHED_BY_SOMETHING, opposite_direction);
10697 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10698 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10700 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10702 struct ElementChangeInfo *change =
10703 &element_info[hitting_element].change_page[i];
10705 if (change->can_change &&
10706 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10707 change->trigger_side & touched_side &&
10710 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10712 change->trigger_element == touched_element
10716 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10717 CE_OTHER_IS_SMASHING, i);
10723 if (IS_CUSTOM_ELEMENT(touched_element) &&
10724 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10726 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10728 struct ElementChangeInfo *change =
10729 &element_info[touched_element].change_page[i];
10731 if (change->can_change &&
10732 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10733 change->trigger_side & hitting_side &&
10735 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10737 change->trigger_element == hitting_element
10741 CheckElementChangeByPage(hitx, hity, touched_element,
10742 hitting_element, CE_OTHER_GETS_SMASHED,i);
10752 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10754 int i, kill_x = -1, kill_y = -1;
10755 int bad_element = -1;
10756 static int test_xy[4][2] =
10763 static int test_dir[4] =
10771 for (i = 0; i < NUM_DIRECTIONS; i++)
10773 int test_x, test_y, test_move_dir, test_element;
10775 test_x = good_x + test_xy[i][0];
10776 test_y = good_y + test_xy[i][1];
10778 if (!IN_LEV_FIELD(test_x, test_y))
10782 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10785 test_element = Feld[test_x][test_y];
10787 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10790 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10791 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10793 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10794 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10798 bad_element = test_element;
10804 if (kill_x != -1 || kill_y != -1)
10806 if (IS_PLAYER(good_x, good_y))
10808 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10811 if (player->shield_deadly_time_left > 0 &&
10812 !IS_INDESTRUCTIBLE(bad_element))
10813 Bang(kill_x, kill_y);
10814 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10817 if (player->shield_deadly_time_left > 0)
10818 Bang(kill_x, kill_y);
10819 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10824 Bang(good_x, good_y);
10828 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10830 int i, kill_x = -1, kill_y = -1;
10831 int bad_element = Feld[bad_x][bad_y];
10832 static int test_xy[4][2] =
10839 static int touch_dir[4] =
10841 MV_LEFT | MV_RIGHT,
10846 static int test_dir[4] =
10854 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10857 for (i = 0; i < NUM_DIRECTIONS; i++)
10859 int test_x, test_y, test_move_dir, test_element;
10861 test_x = bad_x + test_xy[i][0];
10862 test_y = bad_y + test_xy[i][1];
10863 if (!IN_LEV_FIELD(test_x, test_y))
10867 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10869 test_element = Feld[test_x][test_y];
10871 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10872 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10874 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10875 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10877 /* good thing is player or penguin that does not move away */
10878 if (IS_PLAYER(test_x, test_y))
10880 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10882 if (bad_element == EL_ROBOT && player->is_moving)
10883 continue; /* robot does not kill player if he is moving */
10885 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10887 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10888 continue; /* center and border element do not touch */
10895 else if (test_element == EL_PENGUIN)
10904 if (kill_x != -1 || kill_y != -1)
10906 if (IS_PLAYER(kill_x, kill_y))
10908 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10911 if (player->shield_deadly_time_left > 0 &&
10912 !IS_INDESTRUCTIBLE(bad_element))
10913 Bang(bad_x, bad_y);
10914 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10917 if (player->shield_deadly_time_left > 0)
10918 Bang(bad_x, bad_y);
10919 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10924 Bang(kill_x, kill_y);
10928 void TestIfHeroTouchesBadThing(int x, int y)
10930 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10933 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10935 TestIfGoodThingHitsBadThing(x, y, move_dir);
10938 void TestIfBadThingTouchesHero(int x, int y)
10940 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10943 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10945 TestIfBadThingHitsGoodThing(x, y, move_dir);
10948 void TestIfFriendTouchesBadThing(int x, int y)
10950 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10953 void TestIfBadThingTouchesFriend(int x, int y)
10955 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10958 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10960 int i, kill_x = bad_x, kill_y = bad_y;
10961 static int xy[4][2] =
10969 for (i = 0; i < NUM_DIRECTIONS; i++)
10973 x = bad_x + xy[i][0];
10974 y = bad_y + xy[i][1];
10975 if (!IN_LEV_FIELD(x, y))
10978 element = Feld[x][y];
10979 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10980 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10988 if (kill_x != bad_x || kill_y != bad_y)
10989 Bang(bad_x, bad_y);
10992 void KillHero(struct PlayerInfo *player)
10994 int jx = player->jx, jy = player->jy;
10996 if (!player->active)
10999 /* remove accessible field at the player's position */
11000 Feld[jx][jy] = EL_EMPTY;
11002 /* deactivate shield (else Bang()/Explode() would not work right) */
11003 player->shield_normal_time_left = 0;
11004 player->shield_deadly_time_left = 0;
11010 static void KillHeroUnlessEnemyProtected(int x, int y)
11012 if (!PLAYER_ENEMY_PROTECTED(x, y))
11013 KillHero(PLAYERINFO(x, y));
11016 static void KillHeroUnlessExplosionProtected(int x, int y)
11018 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11019 KillHero(PLAYERINFO(x, y));
11022 void BuryHero(struct PlayerInfo *player)
11024 int jx = player->jx, jy = player->jy;
11026 if (!player->active)
11030 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11032 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11034 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11036 player->GameOver = TRUE;
11037 RemoveHero(player);
11040 void RemoveHero(struct PlayerInfo *player)
11042 int jx = player->jx, jy = player->jy;
11043 int i, found = FALSE;
11045 player->present = FALSE;
11046 player->active = FALSE;
11048 if (!ExplodeField[jx][jy])
11049 StorePlayer[jx][jy] = 0;
11051 for (i = 0; i < MAX_PLAYERS; i++)
11052 if (stored_player[i].active)
11056 AllPlayersGone = TRUE;
11063 =============================================================================
11064 checkDiagonalPushing()
11065 -----------------------------------------------------------------------------
11066 check if diagonal input device direction results in pushing of object
11067 (by checking if the alternative direction is walkable, diggable, ...)
11068 =============================================================================
11071 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11072 int x, int y, int real_dx, int real_dy)
11074 int jx, jy, dx, dy, xx, yy;
11076 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11079 /* diagonal direction: check alternative direction */
11084 xx = jx + (dx == 0 ? real_dx : 0);
11085 yy = jy + (dy == 0 ? real_dy : 0);
11087 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11091 =============================================================================
11093 -----------------------------------------------------------------------------
11094 x, y: field next to player (non-diagonal) to try to dig to
11095 real_dx, real_dy: direction as read from input device (can be diagonal)
11096 =============================================================================
11099 int DigField(struct PlayerInfo *player,
11100 int oldx, int oldy, int x, int y,
11101 int real_dx, int real_dy, int mode)
11104 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11106 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11107 boolean player_was_pushing = player->is_pushing;
11108 int jx = oldx, jy = oldy;
11109 int dx = x - jx, dy = y - jy;
11110 int nextx = x + dx, nexty = y + dy;
11111 int move_direction = (dx == -1 ? MV_LEFT :
11112 dx == +1 ? MV_RIGHT :
11114 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11115 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11117 int dig_side = MV_DIR_OPPOSITE(move_direction);
11119 static int trigger_sides[4] =
11121 CH_SIDE_RIGHT, /* moving left */
11122 CH_SIDE_LEFT, /* moving right */
11123 CH_SIDE_BOTTOM, /* moving up */
11124 CH_SIDE_TOP, /* moving down */
11126 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11128 int old_element = Feld[jx][jy];
11131 if (is_player) /* function can also be called by EL_PENGUIN */
11133 if (player->MovPos == 0)
11135 player->is_digging = FALSE;
11136 player->is_collecting = FALSE;
11139 if (player->MovPos == 0) /* last pushing move finished */
11140 player->is_pushing = FALSE;
11142 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11144 player->is_switching = FALSE;
11145 #if USE_NEW_PUSH_DELAY
11146 player->push_delay = -1;
11148 player->push_delay = 0;
11151 return MF_NO_ACTION;
11155 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11156 return MF_NO_ACTION;
11161 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11163 if (IS_TUBE(Feld[jx][jy]) ||
11164 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11168 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11169 int tube_leave_directions[][2] =
11171 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11172 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11173 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11174 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11175 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11176 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11177 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11178 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11179 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11180 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11181 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11182 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11185 while (tube_leave_directions[i][0] != tube_element)
11188 if (tube_leave_directions[i][0] == -1) /* should not happen */
11192 if (!(tube_leave_directions[i][1] & move_direction))
11193 return MF_NO_ACTION; /* tube has no opening in this direction */
11198 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11199 old_element = Back[jx][jy];
11203 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11204 return MF_NO_ACTION; /* field has no opening in this direction */
11206 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11207 return MF_NO_ACTION; /* field has no opening in this direction */
11209 element = Feld[x][y];
11211 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11212 return MF_NO_ACTION;
11214 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11215 game.engine_version >= VERSION_IDENT(2,2,0,0))
11216 return MF_NO_ACTION;
11219 if (game.gravity && is_player && !player->is_auto_moving &&
11220 canFallDown(player) && move_direction != MV_DOWN &&
11221 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11222 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11226 if (element == EL_EMPTY_SPACE &&
11227 game.gravity && !player->is_auto_moving &&
11228 canFallDown(player) && move_direction != MV_DOWN)
11229 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11235 case EL_SP_PORT_LEFT:
11236 case EL_SP_PORT_RIGHT:
11237 case EL_SP_PORT_UP:
11238 case EL_SP_PORT_DOWN:
11239 case EL_SP_PORT_HORIZONTAL:
11240 case EL_SP_PORT_VERTICAL:
11241 case EL_SP_PORT_ANY:
11242 case EL_SP_GRAVITY_PORT_LEFT:
11243 case EL_SP_GRAVITY_PORT_RIGHT:
11244 case EL_SP_GRAVITY_PORT_UP:
11245 case EL_SP_GRAVITY_PORT_DOWN:
11247 if (!canEnterSupaplexPort(x, y, dx, dy))
11248 return MF_NO_ACTION;
11251 element != EL_SP_PORT_LEFT &&
11252 element != EL_SP_GRAVITY_PORT_LEFT &&
11253 element != EL_SP_PORT_HORIZONTAL &&
11254 element != EL_SP_PORT_ANY) ||
11256 element != EL_SP_PORT_RIGHT &&
11257 element != EL_SP_GRAVITY_PORT_RIGHT &&
11258 element != EL_SP_PORT_HORIZONTAL &&
11259 element != EL_SP_PORT_ANY) ||
11261 element != EL_SP_PORT_UP &&
11262 element != EL_SP_GRAVITY_PORT_UP &&
11263 element != EL_SP_PORT_VERTICAL &&
11264 element != EL_SP_PORT_ANY) ||
11266 element != EL_SP_PORT_DOWN &&
11267 element != EL_SP_GRAVITY_PORT_DOWN &&
11268 element != EL_SP_PORT_VERTICAL &&
11269 element != EL_SP_PORT_ANY) ||
11270 !IN_LEV_FIELD(nextx, nexty) ||
11271 !IS_FREE(nextx, nexty))
11272 return MF_NO_ACTION;
11275 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11276 element == EL_SP_GRAVITY_PORT_RIGHT ||
11277 element == EL_SP_GRAVITY_PORT_UP ||
11278 element == EL_SP_GRAVITY_PORT_DOWN)
11279 game.gravity = !game.gravity;
11281 /* automatically move to the next field with double speed */
11282 player->programmed_action = move_direction;
11284 if (player->move_delay_reset_counter == 0)
11286 player->move_delay_reset_counter = 2; /* two double speed steps */
11288 DOUBLE_PLAYER_SPEED(player);
11291 player->move_delay_reset_counter = 2;
11293 DOUBLE_PLAYER_SPEED(player);
11297 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11300 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11306 case EL_TUBE_VERTICAL:
11307 case EL_TUBE_HORIZONTAL:
11308 case EL_TUBE_VERTICAL_LEFT:
11309 case EL_TUBE_VERTICAL_RIGHT:
11310 case EL_TUBE_HORIZONTAL_UP:
11311 case EL_TUBE_HORIZONTAL_DOWN:
11312 case EL_TUBE_LEFT_UP:
11313 case EL_TUBE_LEFT_DOWN:
11314 case EL_TUBE_RIGHT_UP:
11315 case EL_TUBE_RIGHT_DOWN:
11318 int tube_enter_directions[][2] =
11320 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11321 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11322 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11323 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11324 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11325 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11326 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11327 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11328 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11329 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11330 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11331 { -1, MV_NO_MOVING }
11334 while (tube_enter_directions[i][0] != element)
11337 if (tube_enter_directions[i][0] == -1) /* should not happen */
11341 if (!(tube_enter_directions[i][1] & move_direction))
11342 return MF_NO_ACTION; /* tube has no opening in this direction */
11344 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11352 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11354 if (IS_WALKABLE(element))
11357 int sound_element = SND_ELEMENT(element);
11358 int sound_action = ACTION_WALKING;
11361 if (!ACCESS_FROM(element, opposite_direction))
11362 return MF_NO_ACTION; /* field not accessible from this direction */
11366 if (element == EL_EMPTY_SPACE &&
11367 game.gravity && !player->is_auto_moving &&
11368 canFallDown(player) && move_direction != MV_DOWN)
11369 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11372 if (IS_RND_GATE(element))
11374 if (!player->key[RND_GATE_NR(element)])
11375 return MF_NO_ACTION;
11377 else if (IS_RND_GATE_GRAY(element))
11379 if (!player->key[RND_GATE_GRAY_NR(element)])
11380 return MF_NO_ACTION;
11382 else if (element == EL_EXIT_OPEN ||
11383 element == EL_SP_EXIT_OPEN ||
11384 element == EL_SP_EXIT_OPENING)
11386 sound_action = ACTION_PASSING; /* player is passing exit */
11388 else if (element == EL_EMPTY)
11390 sound_action = ACTION_MOVING; /* nothing to walk on */
11393 /* play sound from background or player, whatever is available */
11394 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11395 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11397 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11402 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11404 else if (IS_PASSABLE(element))
11408 if (!canPassField(x, y, move_direction))
11409 return MF_NO_ACTION;
11414 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11415 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11416 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11417 return MF_NO_ACTION;
11419 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11420 return MF_NO_ACTION;
11425 if (!ACCESS_FROM(element, opposite_direction))
11426 return MF_NO_ACTION; /* field not accessible from this direction */
11428 if (IS_CUSTOM_ELEMENT(element) &&
11429 !ACCESS_FROM(element, opposite_direction))
11430 return MF_NO_ACTION; /* field not accessible from this direction */
11434 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11435 return MF_NO_ACTION;
11440 if (IS_EM_GATE(element))
11442 if (!player->key[EM_GATE_NR(element)])
11443 return MF_NO_ACTION;
11445 else if (IS_EM_GATE_GRAY(element))
11447 if (!player->key[EM_GATE_GRAY_NR(element)])
11448 return MF_NO_ACTION;
11450 else if (IS_SP_PORT(element))
11452 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11453 element == EL_SP_GRAVITY_PORT_RIGHT ||
11454 element == EL_SP_GRAVITY_PORT_UP ||
11455 element == EL_SP_GRAVITY_PORT_DOWN)
11456 game.gravity = !game.gravity;
11457 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11458 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11459 element == EL_SP_GRAVITY_ON_PORT_UP ||
11460 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11461 game.gravity = TRUE;
11462 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11463 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11464 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11465 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11466 game.gravity = FALSE;
11469 /* automatically move to the next field with double speed */
11470 player->programmed_action = move_direction;
11472 if (player->move_delay_reset_counter == 0)
11474 player->move_delay_reset_counter = 2; /* two double speed steps */
11476 DOUBLE_PLAYER_SPEED(player);
11479 player->move_delay_reset_counter = 2;
11481 DOUBLE_PLAYER_SPEED(player);
11484 PlayLevelSoundAction(x, y, ACTION_PASSING);
11488 else if (IS_DIGGABLE(element))
11492 if (mode != DF_SNAP)
11495 GfxElement[x][y] = GFX_ELEMENT(element);
11498 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11500 player->is_digging = TRUE;
11503 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11505 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11506 player->index_bit, dig_side);
11509 if (mode == DF_SNAP)
11510 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11515 else if (IS_COLLECTIBLE(element))
11519 if (is_player && mode != DF_SNAP)
11521 GfxElement[x][y] = element;
11522 player->is_collecting = TRUE;
11525 if (element == EL_SPEED_PILL)
11526 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11527 else if (element == EL_EXTRA_TIME && level.time > 0)
11530 DrawGameValue_Time(TimeLeft);
11532 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11534 player->shield_normal_time_left += 10;
11535 if (element == EL_SHIELD_DEADLY)
11536 player->shield_deadly_time_left += 10;
11538 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11540 if (player->inventory_size < MAX_INVENTORY_SIZE)
11541 player->inventory_element[player->inventory_size++] = element;
11543 DrawGameValue_Dynamite(local_player->inventory_size);
11545 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11547 player->dynabomb_count++;
11548 player->dynabombs_left++;
11550 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11552 player->dynabomb_size++;
11554 else if (element == EL_DYNABOMB_INCREASE_POWER)
11556 player->dynabomb_xl = TRUE;
11558 else if (IS_KEY(element))
11560 player->key[KEY_NR(element)] = TRUE;
11562 DrawGameValue_Keys(player->key);
11564 redraw_mask |= REDRAW_DOOR_1;
11566 else if (IS_ENVELOPE(element))
11569 player->show_envelope = element;
11571 ShowEnvelope(element - EL_ENVELOPE_1);
11574 else if (IS_DROPPABLE(element) ||
11575 IS_THROWABLE(element)) /* can be collected and dropped */
11579 if (element_info[element].collect_count == 0)
11580 player->inventory_infinite_element = element;
11582 for (i = 0; i < element_info[element].collect_count; i++)
11583 if (player->inventory_size < MAX_INVENTORY_SIZE)
11584 player->inventory_element[player->inventory_size++] = element;
11586 DrawGameValue_Dynamite(local_player->inventory_size);
11588 else if (element_info[element].collect_count > 0)
11590 local_player->gems_still_needed -=
11591 element_info[element].collect_count;
11592 if (local_player->gems_still_needed < 0)
11593 local_player->gems_still_needed = 0;
11595 DrawGameValue_Emeralds(local_player->gems_still_needed);
11598 RaiseScoreElement(element);
11599 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11602 CheckTriggeredElementChangeByPlayer(x, y, element,
11603 CE_OTHER_GETS_COLLECTED,
11604 player->index_bit, dig_side);
11607 if (mode == DF_SNAP)
11608 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11613 else if (IS_PUSHABLE(element))
11615 if (mode == DF_SNAP && element != EL_BD_ROCK)
11616 return MF_NO_ACTION;
11618 if (CAN_FALL(element) && dy)
11619 return MF_NO_ACTION;
11621 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11622 !(element == EL_SPRING && level.use_spring_bug))
11623 return MF_NO_ACTION;
11626 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11627 ((move_direction & MV_VERTICAL &&
11628 ((element_info[element].move_pattern & MV_LEFT &&
11629 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11630 (element_info[element].move_pattern & MV_RIGHT &&
11631 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11632 (move_direction & MV_HORIZONTAL &&
11633 ((element_info[element].move_pattern & MV_UP &&
11634 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11635 (element_info[element].move_pattern & MV_DOWN &&
11636 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11637 return MF_NO_ACTION;
11641 /* do not push elements already moving away faster than player */
11642 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11643 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11644 return MF_NO_ACTION;
11646 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11647 return MF_NO_ACTION;
11653 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11655 if (player->push_delay_value == -1 || !player_was_pushing)
11656 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11658 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11660 if (player->push_delay_value == -1)
11661 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11664 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11666 if (player->push_delay_value == -1 || !player_was_pushing)
11667 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11670 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11672 if (!player->is_pushing)
11673 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11677 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11678 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11679 !player_is_pushing))
11680 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11683 if (!player->is_pushing &&
11684 game.engine_version >= VERSION_IDENT(2,2,0,7))
11685 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11689 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11690 player->push_delay, player->push_delay_value,
11691 FrameCounter, game.engine_version,
11692 player_was_pushing, player->is_pushing,
11693 element, element_info[element].token_name,
11694 GET_NEW_PUSH_DELAY(element));
11697 player->is_pushing = TRUE;
11699 if (!(IN_LEV_FIELD(nextx, nexty) &&
11700 (IS_FREE(nextx, nexty) ||
11701 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11702 IS_SB_ELEMENT(element)))))
11703 return MF_NO_ACTION;
11705 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11706 return MF_NO_ACTION;
11708 #if USE_NEW_PUSH_DELAY
11711 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11712 printf("::: ALERT: %d, %d [%d / %d]\n",
11713 player->push_delay, player->push_delay2,
11714 FrameCounter, FrameCounter / 50);
11717 if (player->push_delay == -1) /* new pushing; restart delay */
11718 player->push_delay = 0;
11720 if (player->push_delay == 0) /* new pushing; restart delay */
11721 player->push_delay = FrameCounter;
11724 #if USE_NEW_PUSH_DELAY
11726 if ( (player->push_delay > 0) != (!xxx_fr) )
11727 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11728 player->push_delay,
11729 xxx_pdv2, player->push_delay2, player->push_delay_value,
11730 FrameCounter, FrameCounter / 50);
11734 if (player->push_delay > 0 &&
11735 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11736 element != EL_SPRING && element != EL_BALLOON)
11739 if (player->push_delay < player->push_delay_value &&
11740 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11741 element != EL_SPRING && element != EL_BALLOON)
11745 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11746 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11747 element != EL_SPRING && element != EL_BALLOON)
11750 /* make sure that there is no move delay before next try to push */
11751 #if USE_NEW_MOVE_DELAY
11752 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11753 player->move_delay = 0;
11755 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11756 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11759 return MF_NO_ACTION;
11763 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11766 if (IS_SB_ELEMENT(element))
11768 if (element == EL_SOKOBAN_FIELD_FULL)
11770 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11771 local_player->sokobanfields_still_needed++;
11774 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11776 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11777 local_player->sokobanfields_still_needed--;
11780 Feld[x][y] = EL_SOKOBAN_OBJECT;
11782 if (Back[x][y] == Back[nextx][nexty])
11783 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11784 else if (Back[x][y] != 0)
11785 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11788 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11791 if (local_player->sokobanfields_still_needed == 0 &&
11792 game.emulation == EMU_SOKOBAN)
11794 player->LevelSolved = player->GameOver = TRUE;
11795 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11799 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11801 InitMovingField(x, y, move_direction);
11802 GfxAction[x][y] = ACTION_PUSHING;
11804 if (mode == DF_SNAP)
11805 ContinueMoving(x, y);
11807 MovPos[x][y] = (dx != 0 ? dx : dy);
11809 Pushed[x][y] = TRUE;
11810 Pushed[nextx][nexty] = TRUE;
11812 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11813 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11815 player->push_delay_value = -1; /* get new value later */
11817 #if USE_PUSH_BUGFIX
11818 /* now: check for element change _after_ element has been pushed! */
11820 if (game.use_bug_change_when_pushing)
11822 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11825 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11826 player->index_bit, dig_side);
11827 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11828 player->index_bit, dig_side);
11834 /* check for element change _after_ element has been pushed! */
11838 /* !!! TEST ONLY !!! */
11839 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11840 player->index_bit, dig_side);
11841 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11842 player->index_bit, dig_side);
11844 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11845 player->index_bit, dig_side);
11846 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11847 player->index_bit, dig_side);
11855 else if (IS_SWITCHABLE(element))
11857 if (PLAYER_SWITCHING(player, x, y))
11859 CheckTriggeredElementChangeByPlayer(x,y, element,
11860 CE_OTHER_GETS_PRESSED,
11861 player->index_bit, dig_side);
11866 player->is_switching = TRUE;
11867 player->switch_x = x;
11868 player->switch_y = y;
11870 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11872 if (element == EL_ROBOT_WHEEL)
11874 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11878 DrawLevelField(x, y);
11880 else if (element == EL_SP_TERMINAL)
11884 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11886 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11888 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11889 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11892 else if (IS_BELT_SWITCH(element))
11894 ToggleBeltSwitch(x, y);
11896 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11897 element == EL_SWITCHGATE_SWITCH_DOWN)
11899 ToggleSwitchgateSwitch(x, y);
11901 else if (element == EL_LIGHT_SWITCH ||
11902 element == EL_LIGHT_SWITCH_ACTIVE)
11904 ToggleLightSwitch(x, y);
11907 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11908 SND_LIGHT_SWITCH_ACTIVATING :
11909 SND_LIGHT_SWITCH_DEACTIVATING);
11912 else if (element == EL_TIMEGATE_SWITCH)
11914 ActivateTimegateSwitch(x, y);
11916 else if (element == EL_BALLOON_SWITCH_LEFT ||
11917 element == EL_BALLOON_SWITCH_RIGHT ||
11918 element == EL_BALLOON_SWITCH_UP ||
11919 element == EL_BALLOON_SWITCH_DOWN ||
11920 element == EL_BALLOON_SWITCH_ANY)
11922 if (element == EL_BALLOON_SWITCH_ANY)
11923 game.balloon_dir = move_direction;
11925 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11926 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11927 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11928 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11931 else if (element == EL_LAMP)
11933 Feld[x][y] = EL_LAMP_ACTIVE;
11934 local_player->lights_still_needed--;
11936 DrawLevelField(x, y);
11938 else if (element == EL_TIME_ORB_FULL)
11940 Feld[x][y] = EL_TIME_ORB_EMPTY;
11942 DrawGameValue_Time(TimeLeft);
11944 DrawLevelField(x, y);
11947 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11951 CheckTriggeredElementChangeByPlayer(x, y, element,
11952 CE_OTHER_IS_SWITCHING,
11953 player->index_bit, dig_side);
11955 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11956 player->index_bit, dig_side);
11962 if (!PLAYER_SWITCHING(player, x, y))
11964 player->is_switching = TRUE;
11965 player->switch_x = x;
11966 player->switch_y = y;
11969 /* !!! TEST ONLY !!! */
11970 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11971 player->index_bit, dig_side);
11972 CheckTriggeredElementChangeByPlayer(x, y, element,
11973 CE_OTHER_IS_SWITCHING,
11974 player->index_bit, dig_side);
11976 CheckTriggeredElementChangeByPlayer(x, y, element,
11977 CE_OTHER_IS_SWITCHING,
11978 player->index_bit, dig_side);
11979 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11980 player->index_bit, dig_side);
11985 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11986 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11987 player->index_bit, dig_side);
11988 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11989 player->index_bit, dig_side);
11991 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11992 player->index_bit, dig_side);
11993 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11994 player->index_bit, dig_side);
11998 return MF_NO_ACTION;
12001 #if USE_NEW_PUSH_DELAY
12002 player->push_delay = -1;
12004 player->push_delay = 0;
12007 if (Feld[x][y] != element) /* really digged/collected something */
12008 player->is_collecting = !player->is_digging;
12013 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12015 int jx = player->jx, jy = player->jy;
12016 int x = jx + dx, y = jy + dy;
12017 int snap_direction = (dx == -1 ? MV_LEFT :
12018 dx == +1 ? MV_RIGHT :
12020 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12023 if (player->MovPos != 0)
12026 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12030 if (!player->active || !IN_LEV_FIELD(x, y))
12038 if (player->MovPos == 0)
12039 player->is_pushing = FALSE;
12041 player->is_snapping = FALSE;
12043 if (player->MovPos == 0)
12045 player->is_moving = FALSE;
12046 player->is_digging = FALSE;
12047 player->is_collecting = FALSE;
12053 if (player->is_snapping)
12056 player->MovDir = snap_direction;
12059 if (player->MovPos == 0)
12062 player->is_moving = FALSE;
12063 player->is_digging = FALSE;
12064 player->is_collecting = FALSE;
12067 player->is_dropping = FALSE;
12069 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12072 player->is_snapping = TRUE;
12075 if (player->MovPos == 0)
12078 player->is_moving = FALSE;
12079 player->is_digging = FALSE;
12080 player->is_collecting = FALSE;
12084 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12085 DrawLevelField(player->last_jx, player->last_jy);
12088 DrawLevelField(x, y);
12097 boolean DropElement(struct PlayerInfo *player)
12099 int old_element, new_element;
12100 int dropx = player->jx, dropy = player->jy;
12101 int drop_direction = player->MovDir;
12103 int drop_side = drop_direction;
12105 static int trigger_sides[4] =
12107 CH_SIDE_LEFT, /* dropping left */
12108 CH_SIDE_RIGHT, /* dropping right */
12109 CH_SIDE_TOP, /* dropping up */
12110 CH_SIDE_BOTTOM, /* dropping down */
12112 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12114 int drop_element = (player->inventory_size > 0 ?
12115 player->inventory_element[player->inventory_size - 1] :
12116 player->inventory_infinite_element != EL_UNDEFINED ?
12117 player->inventory_infinite_element :
12118 player->dynabombs_left > 0 ?
12119 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12122 if (IS_THROWABLE(drop_element))
12124 dropx += GET_DX_FROM_DIR(drop_direction);
12125 dropy += GET_DY_FROM_DIR(drop_direction);
12127 if (!IN_LEV_FIELD(dropx, dropy))
12131 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12132 new_element = drop_element; /* default: no change when dropping */
12134 /* check if player is active, not moving and ready to drop */
12135 if (!player->active || player->MovPos || player->drop_delay > 0)
12138 /* check if player has anything that can be dropped */
12140 if (new_element == EL_UNDEFINED)
12143 if (player->inventory_size == 0 &&
12144 player->inventory_infinite_element == EL_UNDEFINED &&
12145 player->dynabombs_left == 0)
12149 /* check if anything can be dropped at the current position */
12150 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12153 /* collected custom elements can only be dropped on empty fields */
12155 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12158 if (player->inventory_size > 0 &&
12159 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12160 && old_element != EL_EMPTY)
12164 if (old_element != EL_EMPTY)
12165 Back[dropx][dropy] = old_element; /* store old element on this field */
12167 ResetGfxAnimation(dropx, dropy);
12168 ResetRandomAnimationValue(dropx, dropy);
12170 if (player->inventory_size > 0 ||
12171 player->inventory_infinite_element != EL_UNDEFINED)
12173 if (player->inventory_size > 0)
12175 player->inventory_size--;
12178 new_element = player->inventory_element[player->inventory_size];
12181 DrawGameValue_Dynamite(local_player->inventory_size);
12183 if (new_element == EL_DYNAMITE)
12184 new_element = EL_DYNAMITE_ACTIVE;
12185 else if (new_element == EL_SP_DISK_RED)
12186 new_element = EL_SP_DISK_RED_ACTIVE;
12189 Feld[dropx][dropy] = new_element;
12191 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12192 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12193 el2img(Feld[dropx][dropy]), 0);
12195 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12198 /* needed if previous element just changed to "empty" in the last frame */
12199 Changed[dropx][dropy] = 0; /* allow another change */
12203 /* !!! TEST ONLY !!! */
12204 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12205 player->index_bit, drop_side);
12206 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12207 CE_OTHER_GETS_DROPPED,
12208 player->index_bit, drop_side);
12210 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12211 CE_OTHER_GETS_DROPPED,
12212 player->index_bit, drop_side);
12213 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12214 player->index_bit, drop_side);
12217 TestIfElementTouchesCustomElement(dropx, dropy);
12219 else /* player is dropping a dyna bomb */
12221 player->dynabombs_left--;
12224 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12227 Feld[dropx][dropy] = new_element;
12229 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12230 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12231 el2img(Feld[dropx][dropy]), 0);
12233 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12240 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12243 InitField_WithBug1(dropx, dropy, FALSE);
12245 InitField(dropx, dropy, FALSE);
12246 if (CAN_MOVE(Feld[dropx][dropy]))
12247 InitMovDir(dropx, dropy);
12251 new_element = Feld[dropx][dropy]; /* element might have changed */
12253 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12254 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12257 int move_stepsize = element_info[new_element].move_stepsize;
12259 int move_direction, nextx, nexty;
12261 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12262 MovDir[dropx][dropy] = drop_direction;
12264 move_direction = MovDir[dropx][dropy];
12265 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12266 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12269 Changed[dropx][dropy] = 0; /* allow another change */
12270 CheckCollision[dropx][dropy] = 2;
12273 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12276 WasJustMoving[dropx][dropy] = 3;
12279 InitMovingField(dropx, dropy, move_direction);
12280 ContinueMoving(dropx, dropy);
12285 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12288 Changed[dropx][dropy] = 0; /* allow another change */
12291 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12293 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12294 CE_HITTING_SOMETHING, move_direction);
12302 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12307 player->drop_delay = 8 + 8 + 8;
12311 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12316 player->is_dropping = TRUE;
12322 /* ------------------------------------------------------------------------- */
12323 /* game sound playing functions */
12324 /* ------------------------------------------------------------------------- */
12326 static int *loop_sound_frame = NULL;
12327 static int *loop_sound_volume = NULL;
12329 void InitPlayLevelSound()
12331 int num_sounds = getSoundListSize();
12333 checked_free(loop_sound_frame);
12334 checked_free(loop_sound_volume);
12336 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12337 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12340 static void PlayLevelSound(int x, int y, int nr)
12342 int sx = SCREENX(x), sy = SCREENY(y);
12343 int volume, stereo_position;
12344 int max_distance = 8;
12345 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12347 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12348 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12351 if (!IN_LEV_FIELD(x, y) ||
12352 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12353 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12356 volume = SOUND_MAX_VOLUME;
12358 if (!IN_SCR_FIELD(sx, sy))
12360 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12361 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12363 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12366 stereo_position = (SOUND_MAX_LEFT +
12367 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12368 (SCR_FIELDX + 2 * max_distance));
12370 if (IS_LOOP_SOUND(nr))
12372 /* This assures that quieter loop sounds do not overwrite louder ones,
12373 while restarting sound volume comparison with each new game frame. */
12375 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12378 loop_sound_volume[nr] = volume;
12379 loop_sound_frame[nr] = FrameCounter;
12382 PlaySoundExt(nr, volume, stereo_position, type);
12385 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12387 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12388 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12389 y < LEVELY(BY1) ? LEVELY(BY1) :
12390 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12394 static void PlayLevelSoundAction(int x, int y, int action)
12396 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12399 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12401 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12403 if (sound_effect != SND_UNDEFINED)
12404 PlayLevelSound(x, y, sound_effect);
12407 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12410 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12412 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12413 PlayLevelSound(x, y, sound_effect);
12416 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12418 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12420 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12421 PlayLevelSound(x, y, sound_effect);
12424 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12426 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12428 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12429 StopSound(sound_effect);
12432 static void PlayLevelMusic()
12434 if (levelset.music[level_nr] != MUS_UNDEFINED)
12435 PlayMusic(levelset.music[level_nr]); /* from config file */
12437 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12440 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12442 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12445 if (sample == SAMPLE_bug)
12446 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12452 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12456 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12460 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12464 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12468 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12472 PlayLevelSoundElementAction(x, y, EL_BUG, ACTION_MOVING);
12476 PlayLevelSoundElementAction(x, y, EL_SPACESHIP, ACTION_MOVING);
12479 case SAMPLE_android:
12480 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12483 case SAMPLE_spring:
12484 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12488 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12492 PlayLevelSoundElementAction(x, y, EL_YAMYAM, ACTION_WAITING);
12496 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12499 case SAMPLE_collect:
12500 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12503 case SAMPLE_diamond:
12504 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12507 case SAMPLE_squash:
12508 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12511 case SAMPLE_wonderfall:
12512 PlayLevelSoundElementAction(x, y, EL_MAGIC_WALL, ACTION_FILLING);
12516 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12520 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12524 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12528 PlayLevelSound(x, y, SND_ACID_SPLASHING);
12532 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12536 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12539 case SAMPLE_wonder:
12540 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12544 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12548 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12551 case SAMPLE_dynamite:
12552 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12556 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12560 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12564 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
12568 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12572 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12576 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12580 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12585 void RaiseScore(int value)
12587 local_player->score += value;
12589 DrawGameValue_Score(local_player->score);
12592 void RaiseScoreElement(int element)
12597 case EL_BD_DIAMOND:
12598 case EL_EMERALD_YELLOW:
12599 case EL_EMERALD_RED:
12600 case EL_EMERALD_PURPLE:
12601 case EL_SP_INFOTRON:
12602 RaiseScore(level.score[SC_EMERALD]);
12605 RaiseScore(level.score[SC_DIAMOND]);
12608 RaiseScore(level.score[SC_CRYSTAL]);
12611 RaiseScore(level.score[SC_PEARL]);
12614 case EL_BD_BUTTERFLY:
12615 case EL_SP_ELECTRON:
12616 RaiseScore(level.score[SC_BUG]);
12619 case EL_BD_FIREFLY:
12620 case EL_SP_SNIKSNAK:
12621 RaiseScore(level.score[SC_SPACESHIP]);
12624 case EL_DARK_YAMYAM:
12625 RaiseScore(level.score[SC_YAMYAM]);
12628 RaiseScore(level.score[SC_ROBOT]);
12631 RaiseScore(level.score[SC_PACMAN]);
12634 RaiseScore(level.score[SC_NUT]);
12637 case EL_SP_DISK_RED:
12638 case EL_DYNABOMB_INCREASE_NUMBER:
12639 case EL_DYNABOMB_INCREASE_SIZE:
12640 case EL_DYNABOMB_INCREASE_POWER:
12641 RaiseScore(level.score[SC_DYNAMITE]);
12643 case EL_SHIELD_NORMAL:
12644 case EL_SHIELD_DEADLY:
12645 RaiseScore(level.score[SC_SHIELD]);
12647 case EL_EXTRA_TIME:
12648 RaiseScore(level.score[SC_TIME_BONUS]);
12662 RaiseScore(level.score[SC_KEY]);
12665 RaiseScore(element_info[element].collect_score);
12670 void RequestQuitGame(boolean ask_if_really_quit)
12672 if (AllPlayersGone ||
12673 !ask_if_really_quit ||
12674 level_editor_test_game ||
12675 Request("Do you really want to quit the game ?",
12676 REQ_ASK | REQ_STAY_CLOSED))
12678 #if defined(NETWORK_AVALIABLE)
12679 if (options.network)
12680 SendToServer_StopPlaying();
12684 game_status = GAME_MODE_MAIN;
12692 if (tape.playing && tape.deactivate_display)
12693 TapeDeactivateDisplayOff(TRUE);
12696 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12699 if (tape.playing && tape.deactivate_display)
12700 TapeDeactivateDisplayOn();
12707 /* ---------- new game button stuff ---------------------------------------- */
12709 /* graphic position values for game buttons */
12710 #define GAME_BUTTON_XSIZE 30
12711 #define GAME_BUTTON_YSIZE 30
12712 #define GAME_BUTTON_XPOS 5
12713 #define GAME_BUTTON_YPOS 215
12714 #define SOUND_BUTTON_XPOS 5
12715 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12717 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12718 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12719 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12720 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12721 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12722 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12729 } gamebutton_info[NUM_GAME_BUTTONS] =
12732 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12737 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12738 GAME_CTRL_ID_PAUSE,
12742 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12747 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12748 SOUND_CTRL_ID_MUSIC,
12749 "background music on/off"
12752 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12753 SOUND_CTRL_ID_LOOPS,
12754 "sound loops on/off"
12757 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12758 SOUND_CTRL_ID_SIMPLE,
12759 "normal sounds on/off"
12763 void CreateGameButtons()
12767 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12769 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12770 struct GadgetInfo *gi;
12773 unsigned long event_mask;
12774 int gd_xoffset, gd_yoffset;
12775 int gd_x1, gd_x2, gd_y1, gd_y2;
12778 gd_xoffset = gamebutton_info[i].x;
12779 gd_yoffset = gamebutton_info[i].y;
12780 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12781 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12783 if (id == GAME_CTRL_ID_STOP ||
12784 id == GAME_CTRL_ID_PAUSE ||
12785 id == GAME_CTRL_ID_PLAY)
12787 button_type = GD_TYPE_NORMAL_BUTTON;
12789 event_mask = GD_EVENT_RELEASED;
12790 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12791 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12795 button_type = GD_TYPE_CHECK_BUTTON;
12797 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12798 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12799 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12800 event_mask = GD_EVENT_PRESSED;
12801 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12802 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12805 gi = CreateGadget(GDI_CUSTOM_ID, id,
12806 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12807 GDI_X, DX + gd_xoffset,
12808 GDI_Y, DY + gd_yoffset,
12809 GDI_WIDTH, GAME_BUTTON_XSIZE,
12810 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12811 GDI_TYPE, button_type,
12812 GDI_STATE, GD_BUTTON_UNPRESSED,
12813 GDI_CHECKED, checked,
12814 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12815 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12816 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12817 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12818 GDI_EVENT_MASK, event_mask,
12819 GDI_CALLBACK_ACTION, HandleGameButtons,
12823 Error(ERR_EXIT, "cannot create gadget");
12825 game_gadget[id] = gi;
12829 void FreeGameButtons()
12833 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12834 FreeGadget(game_gadget[i]);
12837 static void MapGameButtons()
12841 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12842 MapGadget(game_gadget[i]);
12845 void UnmapGameButtons()
12849 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12850 UnmapGadget(game_gadget[i]);
12853 static void HandleGameButtons(struct GadgetInfo *gi)
12855 int id = gi->custom_id;
12857 if (game_status != GAME_MODE_PLAYING)
12862 case GAME_CTRL_ID_STOP:
12863 RequestQuitGame(TRUE);
12866 case GAME_CTRL_ID_PAUSE:
12867 if (options.network)
12869 #if defined(NETWORK_AVALIABLE)
12871 SendToServer_ContinuePlaying();
12873 SendToServer_PausePlaying();
12877 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12880 case GAME_CTRL_ID_PLAY:
12883 #if defined(NETWORK_AVALIABLE)
12884 if (options.network)
12885 SendToServer_ContinuePlaying();
12889 tape.pausing = FALSE;
12890 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12895 case SOUND_CTRL_ID_MUSIC:
12896 if (setup.sound_music)
12898 setup.sound_music = FALSE;
12901 else if (audio.music_available)
12903 setup.sound = setup.sound_music = TRUE;
12905 SetAudioMode(setup.sound);
12911 case SOUND_CTRL_ID_LOOPS:
12912 if (setup.sound_loops)
12913 setup.sound_loops = FALSE;
12914 else if (audio.loops_available)
12916 setup.sound = setup.sound_loops = TRUE;
12917 SetAudioMode(setup.sound);
12921 case SOUND_CTRL_ID_SIMPLE:
12922 if (setup.sound_simple)
12923 setup.sound_simple = FALSE;
12924 else if (audio.sound_available)
12926 setup.sound = setup.sound_simple = TRUE;
12927 SetAudioMode(setup.sound);