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(struct PlayerInfo *player)
1069 for (i = 0; i < MAX_KEYS; i++)
1071 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1072 el2edimg(EL_KEY_1 + i));
1075 inline void DrawGameValue_Score(int value)
1077 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1080 inline void DrawGameValue_Time(int value)
1083 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1085 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1088 inline void DrawGameValue_Level(int value)
1091 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1094 /* misuse area for displaying emeralds to draw bigger level number */
1095 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1096 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1098 /* now copy it to the area for displaying level number */
1099 BlitBitmap(drawto, drawto,
1100 DX_EMERALDS, DY_EMERALDS + 1,
1101 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1102 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1103 DX_LEVEL - 1, DY_LEVEL + 1);
1105 /* restore the area for displaying emeralds */
1106 DrawGameValue_Emeralds(local_player->gems_still_needed);
1108 /* yes, this is all really ugly :-) */
1112 void DrawGameDoorValues()
1116 DrawGameValue_Level(level_nr);
1118 for (i = 0; i < MAX_PLAYERS; i++)
1119 DrawGameValue_Keys(&stored_player[i]);
1121 DrawGameValue_Emeralds(local_player->gems_still_needed);
1122 DrawGameValue_Dynamite(local_player->inventory_size);
1123 DrawGameValue_Score(local_player->score);
1124 DrawGameValue_Time(TimeLeft);
1127 void DrawGameDoorValues_EM(int emeralds, int dynamite, int score, int time)
1129 DrawGameValue_Emeralds(emeralds);
1130 DrawGameValue_Dynamite(dynamite);
1131 DrawGameValue_Score(score);
1132 DrawGameValue_Time(time);
1135 static void resolve_group_element(int group_element, int recursion_depth)
1137 static int group_nr;
1138 static struct ElementGroupInfo *group;
1139 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1142 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1144 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1145 group_element - EL_GROUP_START + 1);
1147 /* replace element which caused too deep recursion by question mark */
1148 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1153 if (recursion_depth == 0) /* initialization */
1155 group = element_info[group_element].group;
1156 group_nr = group_element - EL_GROUP_START;
1158 group->num_elements_resolved = 0;
1159 group->choice_pos = 0;
1162 for (i = 0; i < actual_group->num_elements; i++)
1164 int element = actual_group->element[i];
1166 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1169 if (IS_GROUP_ELEMENT(element))
1170 resolve_group_element(element, recursion_depth + 1);
1173 group->element_resolved[group->num_elements_resolved++] = element;
1174 element_info[element].in_group[group_nr] = TRUE;
1179 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1181 printf("::: group %d: %d resolved elements\n",
1182 group_element - EL_GROUP_START, group->num_elements_resolved);
1183 for (i = 0; i < group->num_elements_resolved; i++)
1184 printf("::: - %d ['%s']\n", group->element_resolved[i],
1185 element_info[group->element_resolved[i]].token_name);
1192 =============================================================================
1194 -----------------------------------------------------------------------------
1195 initialize game engine due to level / tape version number
1196 =============================================================================
1199 static void InitGameEngine()
1203 /* set game engine from tape file when re-playing, else from level file */
1204 game.engine_version = (tape.playing ? tape.engine_version :
1205 level.game_version);
1207 /* ---------------------------------------------------------------------- */
1208 /* set flags for bugs and changes according to active game engine version */
1209 /* ---------------------------------------------------------------------- */
1213 Before 3.1.0, custom elements that "change when pushing" changed directly
1214 after the player started pushing them (until then handled in "DigField()").
1215 Since 3.1.0, these custom elements are not changed until the "pushing"
1216 move of the element is finished (now handled in "ContinueMoving()").
1218 Affected levels/tapes:
1219 The first condition is generally needed for all levels/tapes before version
1220 3.1.0, which might use the old behaviour before it was changed; known tapes
1221 that are affected are some tapes from the level set "Walpurgis Gardens" by
1223 The second condition is an exception from the above case and is needed for
1224 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1225 above (including some development versions of 3.1.0), but before it was
1226 known that this change would break tapes like the above and was fixed in
1227 3.1.1, so that the changed behaviour was active although the engine version
1228 while recording maybe was before 3.1.0. There is at least one tape that is
1229 affected by this exception, which is the tape for the one-level set "Bug
1230 Machine" by Juergen Bonhagen.
1233 game.use_bug_change_when_pushing =
1234 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1236 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1237 tape.game_version < VERSION_IDENT(3,1,1,0)));
1239 /* ---------------------------------------------------------------------- */
1241 /* dynamically adjust element properties according to game engine version */
1242 InitElementPropertiesEngine(game.engine_version);
1245 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1246 printf(" tape version == %06d [%s] [file: %06d]\n",
1247 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1249 printf(" => game.engine_version == %06d\n", game.engine_version);
1252 /* ---------- recursively resolve group elements ------------------------- */
1254 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1255 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1256 element_info[i].in_group[j] = FALSE;
1258 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1259 resolve_group_element(EL_GROUP_START + i, 0);
1261 /* ---------- initialize player's initial move delay --------------------- */
1263 #if USE_NEW_MOVE_DELAY
1264 /* dynamically adjust player properties according to level information */
1265 game.initial_move_delay_value =
1266 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1268 /* dynamically adjust player properties according to game engine version */
1269 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1270 game.initial_move_delay_value : 0);
1272 /* dynamically adjust player properties according to game engine version */
1273 game.initial_move_delay =
1274 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1275 INITIAL_MOVE_DELAY_OFF);
1277 /* dynamically adjust player properties according to level information */
1278 game.initial_move_delay_value =
1279 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1282 /* ---------- initialize player's initial push delay --------------------- */
1284 /* dynamically adjust player properties according to game engine version */
1285 game.initial_push_delay_value =
1286 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1288 /* ---------- initialize changing elements ------------------------------- */
1290 /* initialize changing elements information */
1291 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1293 struct ElementInfo *ei = &element_info[i];
1295 /* this pointer might have been changed in the level editor */
1296 ei->change = &ei->change_page[0];
1298 if (!IS_CUSTOM_ELEMENT(i))
1300 ei->change->target_element = EL_EMPTY_SPACE;
1301 ei->change->delay_fixed = 0;
1302 ei->change->delay_random = 0;
1303 ei->change->delay_frames = 1;
1306 ei->change_events = CE_BITMASK_DEFAULT;
1307 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1309 ei->event_page_nr[j] = 0;
1310 ei->event_page[j] = &ei->change_page[0];
1314 /* add changing elements from pre-defined list */
1315 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1317 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1318 struct ElementInfo *ei = &element_info[ch_delay->element];
1320 ei->change->target_element = ch_delay->target_element;
1321 ei->change->delay_fixed = ch_delay->change_delay;
1323 ei->change->pre_change_function = ch_delay->pre_change_function;
1324 ei->change->change_function = ch_delay->change_function;
1325 ei->change->post_change_function = ch_delay->post_change_function;
1327 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1330 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1335 /* add change events from custom element configuration */
1336 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1338 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1340 for (j = 0; j < ei->num_change_pages; j++)
1342 if (!ei->change_page[j].can_change)
1345 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1347 /* only add event page for the first page found with this event */
1348 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1349 !(ei->change_events & CH_EVENT_BIT(k)))
1351 ei->change_events |= CH_EVENT_BIT(k);
1352 ei->event_page_nr[k] = j;
1353 ei->event_page[k] = &ei->change_page[j];
1361 /* add change events from custom element configuration */
1362 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1364 int element = EL_CUSTOM_START + i;
1366 /* only add custom elements that change after fixed/random frame delay */
1367 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1368 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1372 /* ---------- initialize run-time trigger player and element ------------- */
1374 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1376 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1378 for (j = 0; j < ei->num_change_pages; j++)
1380 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1381 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1385 /* ---------- initialize trigger events ---------------------------------- */
1387 /* initialize trigger events information */
1388 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1389 trigger_events[i] = EP_BITMASK_DEFAULT;
1392 /* add trigger events from element change event properties */
1393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 struct ElementInfo *ei = &element_info[i];
1397 for (j = 0; j < ei->num_change_pages; j++)
1399 if (!ei->change_page[j].can_change)
1402 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1404 int trigger_element = ei->change_page[j].trigger_element;
1406 if (IS_GROUP_ELEMENT(trigger_element))
1408 struct ElementGroupInfo *group = element_info[trigger_element].group;
1410 for (k = 0; k < group->num_elements_resolved; k++)
1411 trigger_events[group->element_resolved[k]]
1412 |= ei->change_page[j].events;
1415 trigger_events[trigger_element] |= ei->change_page[j].events;
1420 /* add trigger events from element change event properties */
1421 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1422 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1423 trigger_events[element_info[i].change->trigger_element] |=
1424 element_info[i].change->events;
1427 /* ---------- initialize push delay -------------------------------------- */
1429 /* initialize push delay values to default */
1430 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1432 if (!IS_CUSTOM_ELEMENT(i))
1434 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1435 element_info[i].push_delay_random = game.default_push_delay_random;
1439 /* set push delay value for certain elements from pre-defined list */
1440 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1442 int e = push_delay_list[i].element;
1444 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1445 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1448 /* set push delay value for Supaplex elements for newer engine versions */
1449 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1453 if (IS_SP_ELEMENT(i))
1455 #if USE_NEW_MOVE_STYLE
1456 /* set SP push delay to just enough to push under a falling zonk */
1457 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1459 element_info[i].push_delay_fixed = delay;
1460 element_info[i].push_delay_random = 0;
1462 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1463 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1469 /* ---------- initialize move stepsize ----------------------------------- */
1471 /* initialize move stepsize values to default */
1472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1473 if (!IS_CUSTOM_ELEMENT(i))
1474 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1476 /* set move stepsize value for certain elements from pre-defined list */
1477 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1479 int e = move_stepsize_list[i].element;
1481 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1485 /* ---------- initialize move dig/leave ---------------------------------- */
1487 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1489 element_info[i].can_leave_element = FALSE;
1490 element_info[i].can_leave_element_last = FALSE;
1494 /* ---------- initialize gem count --------------------------------------- */
1496 /* initialize gem count values for each element */
1497 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1498 if (!IS_CUSTOM_ELEMENT(i))
1499 element_info[i].collect_count = 0;
1501 /* add gem count values for all elements from pre-defined list */
1502 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1503 element_info[collect_count_list[i].element].collect_count =
1504 collect_count_list[i].count;
1506 /* ---------- initialize access direction -------------------------------- */
1508 /* initialize access direction values to default (access from every side) */
1509 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1510 if (!IS_CUSTOM_ELEMENT(i))
1511 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1513 /* set access direction value for certain elements from pre-defined list */
1514 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1515 element_info[access_direction_list[i].element].access_direction =
1516 access_direction_list[i].direction;
1521 =============================================================================
1523 -----------------------------------------------------------------------------
1524 initialize and start new game
1525 =============================================================================
1530 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1531 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1532 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1539 #if USE_NEW_AMOEBA_CODE
1540 printf("Using new amoeba code.\n");
1542 printf("Using old amoeba code.\n");
1547 /* don't play tapes over network */
1548 network_playing = (options.network && !tape.playing);
1550 for (i = 0; i < MAX_PLAYERS; i++)
1552 struct PlayerInfo *player = &stored_player[i];
1554 player->index_nr = i;
1555 player->index_bit = (1 << i);
1556 player->element_nr = EL_PLAYER_1 + i;
1558 player->present = FALSE;
1559 player->active = FALSE;
1562 player->effective_action = 0;
1563 player->programmed_action = 0;
1566 player->gems_still_needed = level.gems_needed;
1567 player->sokobanfields_still_needed = 0;
1568 player->lights_still_needed = 0;
1569 player->friends_still_needed = 0;
1571 for (j = 0; j < MAX_KEYS; j++)
1572 player->key[j] = FALSE;
1574 player->dynabomb_count = 0;
1575 player->dynabomb_size = 1;
1576 player->dynabombs_left = 0;
1577 player->dynabomb_xl = FALSE;
1579 player->MovDir = MV_NO_MOVING;
1582 player->GfxDir = MV_NO_MOVING;
1583 player->GfxAction = ACTION_DEFAULT;
1585 player->StepFrame = 0;
1587 player->use_murphy_graphic = FALSE;
1589 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1590 player->block_delay = -1; /* initialized in InitPlayerField() */
1592 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1594 player->actual_frame_counter = 0;
1596 player->step_counter = 0;
1598 player->last_move_dir = MV_NO_MOVING;
1600 player->is_waiting = FALSE;
1601 player->is_moving = FALSE;
1602 player->is_auto_moving = FALSE;
1603 player->is_digging = FALSE;
1604 player->is_snapping = FALSE;
1605 player->is_collecting = FALSE;
1606 player->is_pushing = FALSE;
1607 player->is_switching = FALSE;
1608 player->is_dropping = FALSE;
1610 player->is_bored = FALSE;
1611 player->is_sleeping = FALSE;
1613 player->frame_counter_bored = -1;
1614 player->frame_counter_sleeping = -1;
1616 player->anim_delay_counter = 0;
1617 player->post_delay_counter = 0;
1619 player->action_waiting = ACTION_DEFAULT;
1620 player->last_action_waiting = ACTION_DEFAULT;
1621 player->special_action_bored = ACTION_DEFAULT;
1622 player->special_action_sleeping = ACTION_DEFAULT;
1624 player->num_special_action_bored = 0;
1625 player->num_special_action_sleeping = 0;
1627 /* determine number of special actions for bored and sleeping animation */
1628 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1630 boolean found = FALSE;
1632 for (k = 0; k < NUM_DIRECTIONS; k++)
1633 if (el_act_dir2img(player->element_nr, j, k) !=
1634 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1638 player->num_special_action_bored++;
1642 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1644 boolean found = FALSE;
1646 for (k = 0; k < NUM_DIRECTIONS; k++)
1647 if (el_act_dir2img(player->element_nr, j, k) !=
1648 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1652 player->num_special_action_sleeping++;
1657 player->switch_x = -1;
1658 player->switch_y = -1;
1660 player->show_envelope = 0;
1662 player->move_delay = game.initial_move_delay;
1663 player->move_delay_value = game.initial_move_delay_value;
1665 player->move_delay_reset_counter = 0;
1667 #if USE_NEW_PUSH_DELAY
1668 player->push_delay = -1; /* initialized when pushing starts */
1669 player->push_delay_value = game.initial_push_delay_value;
1671 player->push_delay = 0;
1672 player->push_delay_value = game.initial_push_delay_value;
1675 player->drop_delay = 0;
1677 player->last_jx = player->last_jy = 0;
1678 player->jx = player->jy = 0;
1680 player->shield_normal_time_left = 0;
1681 player->shield_deadly_time_left = 0;
1683 player->inventory_infinite_element = EL_UNDEFINED;
1684 player->inventory_size = 0;
1686 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1687 SnapField(player, 0, 0);
1689 player->LevelSolved = FALSE;
1690 player->GameOver = FALSE;
1693 network_player_action_received = FALSE;
1695 #if defined(NETWORK_AVALIABLE)
1696 /* initial null action */
1697 if (network_playing)
1698 SendToServer_MovePlayer(MV_NO_MOVING);
1706 TimeLeft = level.time;
1709 ScreenMovDir = MV_NO_MOVING;
1713 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1715 AllPlayersGone = FALSE;
1717 game.yamyam_content_nr = 0;
1718 game.magic_wall_active = FALSE;
1719 game.magic_wall_time_left = 0;
1720 game.light_time_left = 0;
1721 game.timegate_time_left = 0;
1722 game.switchgate_pos = 0;
1723 game.balloon_dir = MV_NO_MOVING;
1724 game.gravity = level.initial_gravity;
1725 game.explosions_delayed = TRUE;
1727 game.envelope_active = FALSE;
1729 for (i = 0; i < NUM_BELTS; i++)
1731 game.belt_dir[i] = MV_NO_MOVING;
1732 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1735 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1736 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1738 for (x = 0; x < lev_fieldx; x++)
1740 for (y = 0; y < lev_fieldy; y++)
1742 Feld[x][y] = level.field[x][y];
1743 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1744 ChangeDelay[x][y] = 0;
1745 ChangePage[x][y] = -1;
1746 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1748 WasJustMoving[x][y] = 0;
1749 WasJustFalling[x][y] = 0;
1750 CheckCollision[x][y] = 0;
1752 Pushed[x][y] = FALSE;
1754 Changed[x][y] = CE_BITMASK_DEFAULT;
1755 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1757 ExplodePhase[x][y] = 0;
1758 ExplodeDelay[x][y] = 0;
1759 ExplodeField[x][y] = EX_TYPE_NONE;
1761 RunnerVisit[x][y] = 0;
1762 PlayerVisit[x][y] = 0;
1765 GfxRandom[x][y] = INIT_GFX_RANDOM();
1766 GfxElement[x][y] = EL_UNDEFINED;
1767 GfxAction[x][y] = ACTION_DEFAULT;
1768 GfxDir[x][y] = MV_NO_MOVING;
1772 for (y = 0; y < lev_fieldy; y++)
1774 for (x = 0; x < lev_fieldx; x++)
1776 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1778 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1780 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1783 InitField(x, y, TRUE);
1789 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1790 emulate_sb ? EMU_SOKOBAN :
1791 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1793 /* initialize explosion and ignition delay */
1794 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1796 if (!IS_CUSTOM_ELEMENT(i))
1799 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1800 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1801 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1802 int last_phase = (num_phase + 1) * delay;
1803 int half_phase = (num_phase / 2) * delay;
1805 element_info[i].explosion_delay = last_phase - 1;
1806 element_info[i].ignition_delay = half_phase;
1809 if (i == EL_BLACK_ORB)
1810 element_info[i].ignition_delay = 0;
1812 if (i == EL_BLACK_ORB)
1813 element_info[i].ignition_delay = 1;
1818 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1819 element_info[i].explosion_delay = 1;
1821 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1822 element_info[i].ignition_delay = 1;
1826 /* correct non-moving belts to start moving left */
1827 for (i = 0; i < NUM_BELTS; i++)
1828 if (game.belt_dir[i] == MV_NO_MOVING)
1829 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1831 /* check if any connected player was not found in playfield */
1832 for (i = 0; i < MAX_PLAYERS; i++)
1834 struct PlayerInfo *player = &stored_player[i];
1836 if (player->connected && !player->present)
1838 for (j = 0; j < MAX_PLAYERS; j++)
1840 struct PlayerInfo *some_player = &stored_player[j];
1841 int jx = some_player->jx, jy = some_player->jy;
1843 /* assign first free player found that is present in the playfield */
1844 if (some_player->present && !some_player->connected)
1846 player->present = TRUE;
1847 player->active = TRUE;
1849 some_player->present = FALSE;
1850 some_player->active = FALSE;
1853 player->element_nr = some_player->element_nr;
1856 #if USE_NEW_BLOCK_STYLE
1857 player->block_last_field = some_player->block_last_field;
1858 player->block_delay = some_player->block_delay;
1861 StorePlayer[jx][jy] = player->element_nr;
1862 player->jx = player->last_jx = jx;
1863 player->jy = player->last_jy = jy;
1873 /* when playing a tape, eliminate all players which do not participate */
1875 for (i = 0; i < MAX_PLAYERS; i++)
1877 if (stored_player[i].active && !tape.player_participates[i])
1879 struct PlayerInfo *player = &stored_player[i];
1880 int jx = player->jx, jy = player->jy;
1882 player->active = FALSE;
1883 StorePlayer[jx][jy] = 0;
1884 Feld[jx][jy] = EL_EMPTY;
1888 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1890 /* when in single player mode, eliminate all but the first active player */
1892 for (i = 0; i < MAX_PLAYERS; i++)
1894 if (stored_player[i].active)
1896 for (j = i + 1; j < MAX_PLAYERS; j++)
1898 if (stored_player[j].active)
1900 struct PlayerInfo *player = &stored_player[j];
1901 int jx = player->jx, jy = player->jy;
1903 player->active = FALSE;
1904 player->present = FALSE;
1906 StorePlayer[jx][jy] = 0;
1907 Feld[jx][jy] = EL_EMPTY;
1914 /* when recording the game, store which players take part in the game */
1917 for (i = 0; i < MAX_PLAYERS; i++)
1918 if (stored_player[i].active)
1919 tape.player_participates[i] = TRUE;
1924 for (i = 0; i < MAX_PLAYERS; i++)
1926 struct PlayerInfo *player = &stored_player[i];
1928 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1933 if (local_player == player)
1934 printf("Player %d is local player.\n", i+1);
1938 if (BorderElement == EL_EMPTY)
1941 SBX_Right = lev_fieldx - SCR_FIELDX;
1943 SBY_Lower = lev_fieldy - SCR_FIELDY;
1948 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1950 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1953 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1954 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1956 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1957 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1959 /* if local player not found, look for custom element that might create
1960 the player (make some assumptions about the right custom element) */
1961 if (!local_player->present)
1963 int start_x = 0, start_y = 0;
1964 int found_rating = 0;
1965 int found_element = EL_UNDEFINED;
1967 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1969 int element = Feld[x][y];
1974 if (!IS_CUSTOM_ELEMENT(element))
1977 if (CAN_CHANGE(element))
1979 for (i = 0; i < element_info[element].num_change_pages; i++)
1981 content = element_info[element].change_page[i].target_element;
1982 is_player = ELEM_IS_PLAYER(content);
1984 if (is_player && (found_rating < 3 || element < found_element))
1990 found_element = element;
1995 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1997 content = element_info[element].content[xx][yy];
1998 is_player = ELEM_IS_PLAYER(content);
2000 if (is_player && (found_rating < 2 || element < found_element))
2002 start_x = x + xx - 1;
2003 start_y = y + yy - 1;
2006 found_element = element;
2009 if (!CAN_CHANGE(element))
2012 for (i = 0; i < element_info[element].num_change_pages; i++)
2014 content= element_info[element].change_page[i].target_content[xx][yy];
2015 is_player = ELEM_IS_PLAYER(content);
2017 if (is_player && (found_rating < 1 || element < found_element))
2019 start_x = x + xx - 1;
2020 start_y = y + yy - 1;
2023 found_element = element;
2029 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2030 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2033 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2034 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2040 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2041 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2042 local_player->jx - MIDPOSX);
2044 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2045 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2046 local_player->jy - MIDPOSY);
2048 scroll_x = SBX_Left;
2049 scroll_y = SBY_Upper;
2050 if (local_player->jx >= SBX_Left + MIDPOSX)
2051 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2052 local_player->jx - MIDPOSX :
2054 if (local_player->jy >= SBY_Upper + MIDPOSY)
2055 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2056 local_player->jy - MIDPOSY :
2061 CloseDoor(DOOR_CLOSE_1);
2065 if (em_main_init_game(level_nr) != 0)
2067 game_status = GAME_MODE_MAIN;
2078 /* after drawing the level, correct some elements */
2079 if (game.timegate_time_left == 0)
2080 CloseAllOpenTimegates();
2082 if (setup.soft_scrolling)
2083 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2085 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2090 /* copy default game door content to main double buffer */
2091 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2092 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2094 DrawGameDoorValues();
2098 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2099 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2100 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2104 /* copy actual game door content to door double buffer for OpenDoor() */
2105 BlitBitmap(drawto, bitmap_db_door,
2106 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2108 OpenDoor(DOOR_OPEN_ALL);
2110 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2112 if (setup.sound_music)
2115 KeyboardAutoRepeatOffUnlessAutoplay();
2119 for (i = 0; i < MAX_PLAYERS; i++)
2120 printf("Player %d %sactive.\n",
2121 i + 1, (stored_player[i].active ? "" : "not "));
2125 printf("::: starting game [%d]\n", FrameCounter);
2129 void InitMovDir(int x, int y)
2131 int i, element = Feld[x][y];
2132 static int xy[4][2] =
2139 static int direction[3][4] =
2141 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2142 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2143 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2152 Feld[x][y] = EL_BUG;
2153 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2156 case EL_SPACESHIP_RIGHT:
2157 case EL_SPACESHIP_UP:
2158 case EL_SPACESHIP_LEFT:
2159 case EL_SPACESHIP_DOWN:
2160 Feld[x][y] = EL_SPACESHIP;
2161 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2164 case EL_BD_BUTTERFLY_RIGHT:
2165 case EL_BD_BUTTERFLY_UP:
2166 case EL_BD_BUTTERFLY_LEFT:
2167 case EL_BD_BUTTERFLY_DOWN:
2168 Feld[x][y] = EL_BD_BUTTERFLY;
2169 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2172 case EL_BD_FIREFLY_RIGHT:
2173 case EL_BD_FIREFLY_UP:
2174 case EL_BD_FIREFLY_LEFT:
2175 case EL_BD_FIREFLY_DOWN:
2176 Feld[x][y] = EL_BD_FIREFLY;
2177 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2180 case EL_PACMAN_RIGHT:
2182 case EL_PACMAN_LEFT:
2183 case EL_PACMAN_DOWN:
2184 Feld[x][y] = EL_PACMAN;
2185 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2188 case EL_SP_SNIKSNAK:
2189 MovDir[x][y] = MV_UP;
2192 case EL_SP_ELECTRON:
2193 MovDir[x][y] = MV_LEFT;
2200 Feld[x][y] = EL_MOLE;
2201 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2205 if (IS_CUSTOM_ELEMENT(element))
2207 struct ElementInfo *ei = &element_info[element];
2208 int move_direction_initial = ei->move_direction_initial;
2209 int move_pattern = ei->move_pattern;
2211 if (move_direction_initial == MV_START_PREVIOUS)
2213 if (MovDir[x][y] != MV_NO_MOVING)
2216 move_direction_initial = MV_START_AUTOMATIC;
2219 if (move_direction_initial == MV_START_RANDOM)
2220 MovDir[x][y] = 1 << RND(4);
2221 else if (move_direction_initial & MV_ANY_DIRECTION)
2222 MovDir[x][y] = move_direction_initial;
2223 else if (move_pattern == MV_ALL_DIRECTIONS ||
2224 move_pattern == MV_TURNING_LEFT ||
2225 move_pattern == MV_TURNING_RIGHT ||
2226 move_pattern == MV_TURNING_LEFT_RIGHT ||
2227 move_pattern == MV_TURNING_RIGHT_LEFT ||
2228 move_pattern == MV_TURNING_RANDOM)
2229 MovDir[x][y] = 1 << RND(4);
2230 else if (move_pattern == MV_HORIZONTAL)
2231 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2232 else if (move_pattern == MV_VERTICAL)
2233 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2234 else if (move_pattern & MV_ANY_DIRECTION)
2235 MovDir[x][y] = element_info[element].move_pattern;
2236 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2237 move_pattern == MV_ALONG_RIGHT_SIDE)
2240 /* use random direction as default start direction */
2241 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2242 MovDir[x][y] = 1 << RND(4);
2245 for (i = 0; i < NUM_DIRECTIONS; i++)
2247 int x1 = x + xy[i][0];
2248 int y1 = y + xy[i][1];
2250 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2252 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2253 MovDir[x][y] = direction[0][i];
2255 MovDir[x][y] = direction[1][i];
2264 MovDir[x][y] = 1 << RND(4);
2266 if (element != EL_BUG &&
2267 element != EL_SPACESHIP &&
2268 element != EL_BD_BUTTERFLY &&
2269 element != EL_BD_FIREFLY)
2272 for (i = 0; i < NUM_DIRECTIONS; i++)
2274 int x1 = x + xy[i][0];
2275 int y1 = y + xy[i][1];
2277 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2279 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2281 MovDir[x][y] = direction[0][i];
2284 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2285 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2287 MovDir[x][y] = direction[1][i];
2296 GfxDir[x][y] = MovDir[x][y];
2299 void InitAmoebaNr(int x, int y)
2302 int group_nr = AmoebeNachbarNr(x, y);
2306 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2308 if (AmoebaCnt[i] == 0)
2316 AmoebaNr[x][y] = group_nr;
2317 AmoebaCnt[group_nr]++;
2318 AmoebaCnt2[group_nr]++;
2324 boolean raise_level = FALSE;
2326 if (local_player->MovPos)
2330 if (tape.auto_play) /* tape might already be stopped here */
2331 tape.auto_play_level_solved = TRUE;
2333 if (tape.playing && tape.auto_play)
2334 tape.auto_play_level_solved = TRUE;
2337 local_player->LevelSolved = FALSE;
2339 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2343 if (!tape.playing && setup.sound_loops)
2344 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2345 SND_CTRL_PLAY_LOOP);
2347 while (TimeLeft > 0)
2349 if (!tape.playing && !setup.sound_loops)
2350 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2351 if (TimeLeft > 0 && !(TimeLeft % 10))
2352 RaiseScore(level.score[SC_TIME_BONUS]);
2353 if (TimeLeft > 100 && !(TimeLeft % 10))
2358 DrawGameValue_Time(TimeLeft);
2366 if (!tape.playing && setup.sound_loops)
2367 StopSound(SND_GAME_LEVELTIME_BONUS);
2369 else if (level.time == 0) /* level without time limit */
2371 if (!tape.playing && setup.sound_loops)
2372 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2373 SND_CTRL_PLAY_LOOP);
2375 while (TimePlayed < 999)
2377 if (!tape.playing && !setup.sound_loops)
2378 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2379 if (TimePlayed < 999 && !(TimePlayed % 10))
2380 RaiseScore(level.score[SC_TIME_BONUS]);
2381 if (TimePlayed < 900 && !(TimePlayed % 10))
2386 DrawGameValue_Time(TimePlayed);
2394 if (!tape.playing && setup.sound_loops)
2395 StopSound(SND_GAME_LEVELTIME_BONUS);
2398 /* close exit door after last player */
2399 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2400 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2402 int element = Feld[ExitX][ExitY];
2404 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2405 EL_SP_EXIT_CLOSING);
2407 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2410 /* Hero disappears */
2411 DrawLevelField(ExitX, ExitY);
2417 CloseDoor(DOOR_CLOSE_1);
2422 SaveTape(tape.level_nr); /* Ask to save tape */
2425 if (level_nr == leveldir_current->handicap_level)
2427 leveldir_current->handicap_level++;
2428 SaveLevelSetup_SeriesInfo();
2431 if (level_editor_test_game)
2432 local_player->score = -1; /* no highscore when playing from editor */
2433 else if (level_nr < leveldir_current->last_level)
2434 raise_level = TRUE; /* advance to next level */
2436 if ((hi_pos = NewHiScore()) >= 0)
2438 game_status = GAME_MODE_SCORES;
2439 DrawHallOfFame(hi_pos);
2448 game_status = GAME_MODE_MAIN;
2465 LoadScore(level_nr);
2467 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2468 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2471 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2473 if (local_player->score > highscore[k].Score)
2475 /* player has made it to the hall of fame */
2477 if (k < MAX_SCORE_ENTRIES - 1)
2479 int m = MAX_SCORE_ENTRIES - 1;
2482 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2483 if (!strcmp(setup.player_name, highscore[l].Name))
2485 if (m == k) /* player's new highscore overwrites his old one */
2489 for (l = m; l > k; l--)
2491 strcpy(highscore[l].Name, highscore[l - 1].Name);
2492 highscore[l].Score = highscore[l - 1].Score;
2499 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2500 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2501 highscore[k].Score = local_player->score;
2507 else if (!strncmp(setup.player_name, highscore[k].Name,
2508 MAX_PLAYER_NAME_LEN))
2509 break; /* player already there with a higher score */
2515 SaveScore(level_nr);
2520 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2522 if (player->GfxAction != action || player->GfxDir != dir)
2525 printf("Player frame reset! (%d => %d, %d => %d)\n",
2526 player->GfxAction, action, player->GfxDir, dir);
2529 player->GfxAction = action;
2530 player->GfxDir = dir;
2532 player->StepFrame = 0;
2536 static void ResetRandomAnimationValue(int x, int y)
2538 GfxRandom[x][y] = INIT_GFX_RANDOM();
2541 static void ResetGfxAnimation(int x, int y)
2544 GfxAction[x][y] = ACTION_DEFAULT;
2545 GfxDir[x][y] = MovDir[x][y];
2548 void InitMovingField(int x, int y, int direction)
2550 int element = Feld[x][y];
2551 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2552 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2556 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2557 ResetGfxAnimation(x, y);
2559 MovDir[newx][newy] = MovDir[x][y] = direction;
2560 GfxDir[x][y] = direction;
2562 if (Feld[newx][newy] == EL_EMPTY)
2563 Feld[newx][newy] = EL_BLOCKED;
2565 if (direction == MV_DOWN && CAN_FALL(element))
2566 GfxAction[x][y] = ACTION_FALLING;
2568 GfxAction[x][y] = ACTION_MOVING;
2570 GfxFrame[newx][newy] = GfxFrame[x][y];
2571 GfxRandom[newx][newy] = GfxRandom[x][y];
2572 GfxAction[newx][newy] = GfxAction[x][y];
2573 GfxDir[newx][newy] = GfxDir[x][y];
2576 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2578 int direction = MovDir[x][y];
2579 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2580 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2586 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2588 int oldx = x, oldy = y;
2589 int direction = MovDir[x][y];
2591 if (direction == MV_LEFT)
2593 else if (direction == MV_RIGHT)
2595 else if (direction == MV_UP)
2597 else if (direction == MV_DOWN)
2600 *comes_from_x = oldx;
2601 *comes_from_y = oldy;
2604 int MovingOrBlocked2Element(int x, int y)
2606 int element = Feld[x][y];
2608 if (element == EL_BLOCKED)
2612 Blocked2Moving(x, y, &oldx, &oldy);
2613 return Feld[oldx][oldy];
2619 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2621 /* like MovingOrBlocked2Element(), but if element is moving
2622 and (x,y) is the field the moving element is just leaving,
2623 return EL_BLOCKED instead of the element value */
2624 int element = Feld[x][y];
2626 if (IS_MOVING(x, y))
2628 if (element == EL_BLOCKED)
2632 Blocked2Moving(x, y, &oldx, &oldy);
2633 return Feld[oldx][oldy];
2642 static void RemoveField(int x, int y)
2644 Feld[x][y] = EL_EMPTY;
2651 ChangeDelay[x][y] = 0;
2652 ChangePage[x][y] = -1;
2653 Pushed[x][y] = FALSE;
2656 ExplodeField[x][y] = EX_TYPE_NONE;
2659 GfxElement[x][y] = EL_UNDEFINED;
2660 GfxAction[x][y] = ACTION_DEFAULT;
2661 GfxDir[x][y] = MV_NO_MOVING;
2664 void RemoveMovingField(int x, int y)
2666 int oldx = x, oldy = y, newx = x, newy = y;
2667 int element = Feld[x][y];
2668 int next_element = EL_UNDEFINED;
2670 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2673 if (IS_MOVING(x, y))
2675 Moving2Blocked(x, y, &newx, &newy);
2677 if (Feld[newx][newy] != EL_BLOCKED)
2680 if (Feld[newx][newy] != EL_BLOCKED)
2682 /* element is moving, but target field is not free (blocked), but
2683 already occupied by something different (example: acid pool);
2684 in this case, only remove the moving field, but not the target */
2686 RemoveField(oldx, oldy);
2688 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2690 DrawLevelField(oldx, oldy);
2696 else if (element == EL_BLOCKED)
2698 Blocked2Moving(x, y, &oldx, &oldy);
2699 if (!IS_MOVING(oldx, oldy))
2703 if (element == EL_BLOCKED &&
2704 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2705 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2706 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2707 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2708 next_element = get_next_element(Feld[oldx][oldy]);
2710 RemoveField(oldx, oldy);
2711 RemoveField(newx, newy);
2713 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2715 if (next_element != EL_UNDEFINED)
2716 Feld[oldx][oldy] = next_element;
2718 DrawLevelField(oldx, oldy);
2719 DrawLevelField(newx, newy);
2722 void DrawDynamite(int x, int y)
2724 int sx = SCREENX(x), sy = SCREENY(y);
2725 int graphic = el2img(Feld[x][y]);
2728 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2731 if (IS_WALKABLE_INSIDE(Back[x][y]))
2735 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2736 else if (Store[x][y])
2737 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2739 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2742 if (Back[x][y] || Store[x][y])
2743 DrawGraphicThruMask(sx, sy, graphic, frame);
2745 DrawGraphic(sx, sy, graphic, frame);
2747 if (game.emulation == EMU_SUPAPLEX)
2748 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2749 else if (Store[x][y])
2750 DrawGraphicThruMask(sx, sy, graphic, frame);
2752 DrawGraphic(sx, sy, graphic, frame);
2756 void CheckDynamite(int x, int y)
2758 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2762 if (MovDelay[x][y] != 0)
2765 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2772 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2774 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2775 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2776 StopSound(SND_DYNAMITE_ACTIVE);
2778 StopSound(SND_DYNABOMB_ACTIVE);
2784 void DrawRelocatePlayer(struct PlayerInfo *player)
2786 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2787 boolean no_delay = (tape.warp_forward);
2788 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2789 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2790 int jx = player->jx;
2791 int jy = player->jy;
2793 if (level.instant_relocation)
2796 int offset = (setup.scroll_delay ? 3 : 0);
2798 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2800 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2801 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2802 local_player->jx - MIDPOSX);
2804 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2805 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2806 local_player->jy - MIDPOSY);
2810 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2811 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2812 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2814 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2815 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2816 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2818 /* don't scroll over playfield boundaries */
2819 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2820 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2822 /* don't scroll over playfield boundaries */
2823 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2824 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2827 scroll_x += (local_player->jx - old_jx);
2828 scroll_y += (local_player->jy - old_jy);
2830 /* don't scroll over playfield boundaries */
2831 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2832 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2834 /* don't scroll over playfield boundaries */
2835 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2836 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2839 RedrawPlayfield(TRUE, 0,0,0,0);
2845 int offset = (setup.scroll_delay ? 3 : 0);
2847 int scroll_xx = -999, scroll_yy = -999;
2849 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2851 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2854 int fx = FX, fy = FY;
2856 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2857 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2858 local_player->jx - MIDPOSX);
2860 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2861 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2862 local_player->jy - MIDPOSY);
2864 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2865 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2868 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2871 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2878 fx += dx * TILEX / 2;
2879 fy += dy * TILEY / 2;
2881 ScrollLevel(dx, dy);
2884 /* scroll in two steps of half tile size to make things smoother */
2885 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2887 Delay(wait_delay_value);
2889 /* scroll second step to align at full tile size */
2891 Delay(wait_delay_value);
2894 int scroll_xx = -999, scroll_yy = -999;
2896 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2898 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2901 int fx = FX, fy = FY;
2903 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2904 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2905 local_player->jx - MIDPOSX);
2907 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2908 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2909 local_player->jy - MIDPOSY);
2911 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2912 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2915 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2918 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2925 fx += dx * TILEX / 2;
2926 fy += dy * TILEY / 2;
2928 ScrollLevel(dx, dy);
2931 /* scroll in two steps of half tile size to make things smoother */
2932 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2934 Delay(wait_delay_value);
2936 /* scroll second step to align at full tile size */
2938 Delay(wait_delay_value);
2944 Delay(wait_delay_value);
2948 void RelocatePlayer(int jx, int jy, int el_player_raw)
2951 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2953 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2955 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2956 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2957 boolean no_delay = (tape.warp_forward);
2958 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2959 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2960 int old_jx = player->jx;
2961 int old_jy = player->jy;
2962 int old_element = Feld[old_jx][old_jy];
2963 int element = Feld[jx][jy];
2964 boolean player_relocated = (old_jx != jx || old_jy != jy);
2966 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2967 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2969 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2970 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2971 int leave_side_horiz = move_dir_horiz;
2972 int leave_side_vert = move_dir_vert;
2974 static int trigger_sides[4][2] =
2976 /* enter side leave side */
2977 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2978 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2979 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2980 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2982 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2983 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2984 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2985 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2987 int enter_side = enter_side_horiz | enter_side_vert;
2988 int leave_side = leave_side_horiz | leave_side_vert;
2990 if (player->GameOver) /* do not reanimate dead player */
2993 if (!player_relocated) /* no need to relocate the player */
2996 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2998 RemoveField(jx, jy); /* temporarily remove newly placed player */
2999 DrawLevelField(jx, jy);
3002 if (player->present)
3004 while (player->MovPos)
3006 ScrollPlayer(player, SCROLL_GO_ON);
3007 ScrollScreen(NULL, SCROLL_GO_ON);
3009 #if USE_NEW_MOVE_DELAY
3010 AdvanceFrameAndPlayerCounters(player->index_nr);
3018 Delay(wait_delay_value);
3021 DrawPlayer(player); /* needed here only to cleanup last field */
3022 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3024 player->is_moving = FALSE;
3028 if (IS_CUSTOM_ELEMENT(old_element))
3029 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3031 player->index_bit, leave_side);
3033 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3035 player->index_bit, leave_side);
3038 Feld[jx][jy] = el_player;
3039 InitPlayerField(jx, jy, el_player, TRUE);
3041 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3043 Feld[jx][jy] = element;
3044 InitField(jx, jy, FALSE);
3048 if (player == local_player) /* only visually relocate local player */
3049 DrawRelocatePlayer(player);
3053 TestIfHeroTouchesBadThing(jx, jy);
3054 TestIfPlayerTouchesCustomElement(jx, jy);
3058 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3063 /* needed to allow change of walkable custom element by entering player */
3064 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3065 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3067 /* needed to allow change of walkable custom element by entering player */
3068 Changed[jx][jy] = 0; /* allow another change */
3073 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3074 enter_side == MV_LEFT ? "left" :
3075 enter_side == MV_RIGHT ? "right" :
3076 enter_side == MV_UP ? "top" :
3077 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3081 if (IS_CUSTOM_ELEMENT(element))
3082 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3083 player->index_bit, enter_side);
3085 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3086 CE_OTHER_GETS_ENTERED,
3087 player->index_bit, enter_side);
3091 void Explode(int ex, int ey, int phase, int mode)
3098 /* !!! eliminate this variable !!! */
3099 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3104 int last_phase = num_phase * delay;
3105 int half_phase = (num_phase / 2) * delay;
3106 int first_phase_after_start = EX_PHASE_START + 1;
3110 if (game.explosions_delayed)
3112 ExplodeField[ex][ey] = mode;
3116 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3118 int center_element = Feld[ex][ey];
3121 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3125 /* --- This is only really needed (and now handled) in "Impact()". --- */
3126 /* do not explode moving elements that left the explode field in time */
3127 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3128 center_element == EL_EMPTY &&
3129 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3133 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3134 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3136 /* remove things displayed in background while burning dynamite */
3137 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3140 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3142 /* put moving element to center field (and let it explode there) */
3143 center_element = MovingOrBlocked2Element(ex, ey);
3144 RemoveMovingField(ex, ey);
3145 Feld[ex][ey] = center_element;
3151 last_phase = element_info[center_element].explosion_delay + 1;
3153 last_phase = element_info[center_element].explosion_delay;
3157 printf("::: %d -> %d\n", center_element, last_phase);
3161 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3163 int xx = x - ex + 1;
3164 int yy = y - ey + 1;
3169 if (!IN_LEV_FIELD(x, y) ||
3170 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3171 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3174 if (!IN_LEV_FIELD(x, y) ||
3175 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3179 if (!IN_LEV_FIELD(x, y) ||
3180 ((mode != EX_TYPE_NORMAL ||
3181 center_element == EL_AMOEBA_TO_DIAMOND) &&
3182 (x != ex || y != ey)))
3186 element = Feld[x][y];
3188 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3190 element = MovingOrBlocked2Element(x, y);
3192 if (!IS_EXPLOSION_PROOF(element))
3193 RemoveMovingField(x, y);
3199 if (IS_EXPLOSION_PROOF(element))
3202 /* indestructible elements can only explode in center (but not flames) */
3204 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3205 mode == EX_TYPE_BORDER)) ||
3206 element == EL_FLAMES)
3209 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3210 element == EL_FLAMES)
3216 if ((IS_INDESTRUCTIBLE(element) &&
3217 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3218 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3219 element == EL_FLAMES)
3224 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3225 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3226 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3228 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3231 if (IS_ACTIVE_BOMB(element))
3233 /* re-activate things under the bomb like gate or penguin */
3235 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3238 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3243 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3244 element_info[Feld[x][y]].token_name,
3245 Store[x][y], Store2[x][y]);
3252 /* save walkable background elements while explosion on same tile */
3254 if (IS_INDESTRUCTIBLE(element))
3255 Back[x][y] = element;
3259 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3260 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3261 Back[x][y] = element;
3263 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3264 (x != ex || y != ey))
3265 Back[x][y] = element;
3268 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3269 Back[x][y] = element;
3273 /* ignite explodable elements reached by other explosion */
3274 if (element == EL_EXPLOSION)
3275 element = Store2[x][y];
3278 if (AmoebaNr[x][y] &&
3279 (element == EL_AMOEBA_FULL ||
3280 element == EL_BD_AMOEBA ||
3281 element == EL_AMOEBA_GROWING))
3283 AmoebaCnt[AmoebaNr[x][y]]--;
3284 AmoebaCnt2[AmoebaNr[x][y]]--;
3290 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3292 switch(StorePlayer[ex][ey])
3295 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3298 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3301 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3305 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3310 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3311 Store[x][y] = EL_EMPTY;
3313 if (game.emulation == EMU_SUPAPLEX)
3314 Store[x][y] = EL_EMPTY;
3317 else if (center_element == EL_MOLE)
3318 Store[x][y] = EL_EMERALD_RED;
3319 else if (center_element == EL_PENGUIN)
3320 Store[x][y] = EL_EMERALD_PURPLE;
3321 else if (center_element == EL_BUG)
3322 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3323 else if (center_element == EL_BD_BUTTERFLY)
3324 Store[x][y] = EL_BD_DIAMOND;
3325 else if (center_element == EL_SP_ELECTRON)
3326 Store[x][y] = EL_SP_INFOTRON;
3327 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3328 Store[x][y] = level.amoeba_content;
3329 else if (center_element == EL_YAMYAM)
3330 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3331 else if (IS_CUSTOM_ELEMENT(center_element) &&
3332 element_info[center_element].content[xx][yy] != EL_EMPTY)
3333 Store[x][y] = element_info[center_element].content[xx][yy];
3334 else if (element == EL_WALL_EMERALD)
3335 Store[x][y] = EL_EMERALD;
3336 else if (element == EL_WALL_DIAMOND)
3337 Store[x][y] = EL_DIAMOND;
3338 else if (element == EL_WALL_BD_DIAMOND)
3339 Store[x][y] = EL_BD_DIAMOND;
3340 else if (element == EL_WALL_EMERALD_YELLOW)
3341 Store[x][y] = EL_EMERALD_YELLOW;
3342 else if (element == EL_WALL_EMERALD_RED)
3343 Store[x][y] = EL_EMERALD_RED;
3344 else if (element == EL_WALL_EMERALD_PURPLE)
3345 Store[x][y] = EL_EMERALD_PURPLE;
3346 else if (element == EL_WALL_PEARL)
3347 Store[x][y] = EL_PEARL;
3348 else if (element == EL_WALL_CRYSTAL)
3349 Store[x][y] = EL_CRYSTAL;
3350 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3351 Store[x][y] = element_info[element].content[1][1];
3353 Store[x][y] = EL_EMPTY;
3355 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3356 center_element == EL_AMOEBA_TO_DIAMOND)
3357 Store2[x][y] = element;
3360 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3361 element_info[Store2[x][y]].token_name);
3365 if (AmoebaNr[x][y] &&
3366 (element == EL_AMOEBA_FULL ||
3367 element == EL_BD_AMOEBA ||
3368 element == EL_AMOEBA_GROWING))
3370 AmoebaCnt[AmoebaNr[x][y]]--;
3371 AmoebaCnt2[AmoebaNr[x][y]]--;
3377 MovDir[x][y] = MovPos[x][y] = 0;
3378 GfxDir[x][y] = MovDir[x][y];
3383 Feld[x][y] = EL_EXPLOSION;
3385 GfxElement[x][y] = center_element;
3387 GfxElement[x][y] = EL_UNDEFINED;
3390 ExplodePhase[x][y] = 1;
3392 ExplodeDelay[x][y] = last_phase;
3397 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3399 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3406 if (center_element == EL_YAMYAM)
3407 game.yamyam_content_nr =
3408 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3411 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3412 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3426 GfxFrame[x][y] = 0; /* restart explosion animation */
3430 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3434 last_phase = ExplodeDelay[x][y];
3437 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3441 /* activate this even in non-DEBUG version until cause for crash in
3442 getGraphicAnimationFrame() (see below) is found and eliminated */
3446 if (GfxElement[x][y] == EL_UNDEFINED)
3449 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3450 printf("Explode(): This should never happen!\n");
3453 GfxElement[x][y] = EL_EMPTY;
3459 border_element = Store2[x][y];
3461 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3462 border_element = StorePlayer[x][y];
3464 if (IS_PLAYER(x, y))
3465 border_element = StorePlayer[x][y];
3469 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3470 element_info[border_element].token_name, Store2[x][y]);
3474 printf("::: phase == %d\n", phase);
3477 if (phase == element_info[border_element].ignition_delay ||
3478 phase == last_phase)
3480 boolean border_explosion = FALSE;
3484 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3485 !PLAYER_EXPLOSION_PROTECTED(x, y))
3487 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3490 if (IS_PLAYER(x, y))
3493 KillHeroUnlessExplosionProtected(x, y);
3494 border_explosion = TRUE;
3497 if (phase == last_phase)
3498 printf("::: IS_PLAYER\n");
3501 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3504 printf("::: %d,%d: %d %s\n", x, y, border_element,
3505 element_info[border_element].token_name);
3508 Feld[x][y] = Store2[x][y];
3511 border_explosion = TRUE;
3514 if (phase == last_phase)
3515 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3518 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3520 AmoebeUmwandeln(x, y);
3522 border_explosion = TRUE;
3525 if (phase == last_phase)
3526 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3527 element_info[border_element].explosion_delay,
3528 element_info[border_element].ignition_delay,
3534 /* if an element just explodes due to another explosion (chain-reaction),
3535 do not immediately end the new explosion when it was the last frame of
3536 the explosion (as it would be done in the following "if"-statement!) */
3537 if (border_explosion && phase == last_phase)
3544 if (phase == first_phase_after_start)
3546 int element = Store2[x][y];
3548 if (element == EL_BLACK_ORB)
3550 Feld[x][y] = Store2[x][y];
3555 else if (phase == half_phase)
3557 int element = Store2[x][y];
3559 if (IS_PLAYER(x, y))
3560 KillHeroUnlessExplosionProtected(x, y);
3561 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3563 Feld[x][y] = Store2[x][y];
3567 else if (element == EL_AMOEBA_TO_DIAMOND)
3568 AmoebeUmwandeln(x, y);
3572 if (phase == last_phase)
3577 printf("::: done: phase == %d\n", phase);
3581 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3584 element = Feld[x][y] = Store[x][y];
3585 Store[x][y] = Store2[x][y] = 0;
3586 GfxElement[x][y] = EL_UNDEFINED;
3588 /* player can escape from explosions and might therefore be still alive */
3589 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3590 element <= EL_PLAYER_IS_EXPLODING_4)
3591 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3593 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3594 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3595 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3598 /* restore probably existing indestructible background element */
3599 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3600 element = Feld[x][y] = Back[x][y];
3603 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3604 GfxDir[x][y] = MV_NO_MOVING;
3605 ChangeDelay[x][y] = 0;
3606 ChangePage[x][y] = -1;
3609 InitField_WithBug2(x, y, FALSE);
3611 InitField(x, y, FALSE);
3613 /* !!! not needed !!! */
3615 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3616 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3619 if (CAN_MOVE(element))
3624 DrawLevelField(x, y);
3626 TestIfElementTouchesCustomElement(x, y);
3628 if (GFX_CRUMBLED(element))
3629 DrawLevelFieldCrumbledSandNeighbours(x, y);
3631 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3632 StorePlayer[x][y] = 0;
3634 if (ELEM_IS_PLAYER(element))
3635 RelocatePlayer(x, y, element);
3638 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3640 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3644 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3646 int stored = Store[x][y];
3647 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3648 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3652 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3654 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3658 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3662 printf("::: %d / %d [%d - %d]\n",
3663 GfxFrame[x][y], phase - delay, phase, delay);
3667 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3668 element_info[GfxElement[x][y]].token_name,
3673 DrawLevelFieldCrumbledSand(x, y);
3675 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3677 DrawLevelElement(x, y, Back[x][y]);
3678 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3680 else if (IS_WALKABLE_UNDER(Back[x][y]))
3682 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3683 DrawLevelElementThruMask(x, y, Back[x][y]);
3685 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3686 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3690 void DynaExplode(int ex, int ey)
3693 int dynabomb_element = Feld[ex][ey];
3694 int dynabomb_size = 1;
3695 boolean dynabomb_xl = FALSE;
3696 struct PlayerInfo *player;
3697 static int xy[4][2] =
3705 if (IS_ACTIVE_BOMB(dynabomb_element))
3707 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3708 dynabomb_size = player->dynabomb_size;
3709 dynabomb_xl = player->dynabomb_xl;
3710 player->dynabombs_left++;
3713 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3715 for (i = 0; i < NUM_DIRECTIONS; i++)
3717 for (j = 1; j <= dynabomb_size; j++)
3719 int x = ex + j * xy[i][0];
3720 int y = ey + j * xy[i][1];
3723 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3726 element = Feld[x][y];
3728 /* do not restart explosions of fields with active bombs */
3729 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3732 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3736 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3737 !IS_DIGGABLE(element) && !dynabomb_xl)
3740 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3741 !CAN_GROW_INTO(element) && !dynabomb_xl)
3745 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3746 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3747 element != EL_SAND && !dynabomb_xl)
3754 void Bang(int x, int y)
3757 int element = MovingOrBlocked2Element(x, y);
3759 int element = Feld[x][y];
3763 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3765 if (IS_PLAYER(x, y))
3768 struct PlayerInfo *player = PLAYERINFO(x, y);
3770 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3771 player->element_nr);
3776 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3778 if (game.emulation == EMU_SUPAPLEX)
3779 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3781 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3786 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3794 case EL_BD_BUTTERFLY:
3797 case EL_DARK_YAMYAM:
3801 RaiseScoreElement(element);
3802 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3804 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3805 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3806 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3807 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3808 case EL_DYNABOMB_INCREASE_NUMBER:
3809 case EL_DYNABOMB_INCREASE_SIZE:
3810 case EL_DYNABOMB_INCREASE_POWER:
3815 case EL_LAMP_ACTIVE:
3817 case EL_AMOEBA_TO_DIAMOND:
3819 if (IS_PLAYER(x, y))
3820 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3822 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3826 if (element_info[element].explosion_type == EXPLODES_CROSS)
3828 if (CAN_EXPLODE_CROSS(element))
3831 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3836 else if (element_info[element].explosion_type == EXPLODES_1X1)
3838 else if (CAN_EXPLODE_1X1(element))
3840 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3842 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3846 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3849 void SplashAcid(int x, int y)
3852 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3853 (!IN_LEV_FIELD(x - 1, y - 2) ||
3854 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3855 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3857 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3858 (!IN_LEV_FIELD(x + 1, y - 2) ||
3859 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3860 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3862 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3864 /* input: position of element entering acid (obsolete) */
3866 int element = Feld[x][y];
3868 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3871 if (element != EL_ACID_SPLASH_LEFT &&
3872 element != EL_ACID_SPLASH_RIGHT)
3874 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3876 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3877 (!IN_LEV_FIELD(x - 1, y - 1) ||
3878 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3879 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3881 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3882 (!IN_LEV_FIELD(x + 1, y - 1) ||
3883 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3884 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3889 static void InitBeltMovement()
3891 static int belt_base_element[4] =
3893 EL_CONVEYOR_BELT_1_LEFT,
3894 EL_CONVEYOR_BELT_2_LEFT,
3895 EL_CONVEYOR_BELT_3_LEFT,
3896 EL_CONVEYOR_BELT_4_LEFT
3898 static int belt_base_active_element[4] =
3900 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3901 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3902 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3903 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3908 /* set frame order for belt animation graphic according to belt direction */
3909 for (i = 0; i < NUM_BELTS; i++)
3913 for (j = 0; j < NUM_BELT_PARTS; j++)
3915 int element = belt_base_active_element[belt_nr] + j;
3916 int graphic = el2img(element);
3918 if (game.belt_dir[i] == MV_LEFT)
3919 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3921 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3925 for (y = 0; y < lev_fieldy; y++)
3927 for (x = 0; x < lev_fieldx; x++)
3929 int element = Feld[x][y];
3931 for (i = 0; i < NUM_BELTS; i++)
3933 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3935 int e_belt_nr = getBeltNrFromBeltElement(element);
3938 if (e_belt_nr == belt_nr)
3940 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3942 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3950 static void ToggleBeltSwitch(int x, int y)
3952 static int belt_base_element[4] =
3954 EL_CONVEYOR_BELT_1_LEFT,
3955 EL_CONVEYOR_BELT_2_LEFT,
3956 EL_CONVEYOR_BELT_3_LEFT,
3957 EL_CONVEYOR_BELT_4_LEFT
3959 static int belt_base_active_element[4] =
3961 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3962 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3963 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3964 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3966 static int belt_base_switch_element[4] =
3968 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3969 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3970 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3971 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3973 static int belt_move_dir[4] =
3981 int element = Feld[x][y];
3982 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3983 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3984 int belt_dir = belt_move_dir[belt_dir_nr];
3987 if (!IS_BELT_SWITCH(element))
3990 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3991 game.belt_dir[belt_nr] = belt_dir;
3993 if (belt_dir_nr == 3)
3996 /* set frame order for belt animation graphic according to belt direction */
3997 for (i = 0; i < NUM_BELT_PARTS; i++)
3999 int element = belt_base_active_element[belt_nr] + i;
4000 int graphic = el2img(element);
4002 if (belt_dir == MV_LEFT)
4003 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4005 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4008 for (yy = 0; yy < lev_fieldy; yy++)
4010 for (xx = 0; xx < lev_fieldx; xx++)
4012 int element = Feld[xx][yy];
4014 if (IS_BELT_SWITCH(element))
4016 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4018 if (e_belt_nr == belt_nr)
4020 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4021 DrawLevelField(xx, yy);
4024 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4026 int e_belt_nr = getBeltNrFromBeltElement(element);
4028 if (e_belt_nr == belt_nr)
4030 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4032 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4033 DrawLevelField(xx, yy);
4036 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4038 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4040 if (e_belt_nr == belt_nr)
4042 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4044 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4045 DrawLevelField(xx, yy);
4052 static void ToggleSwitchgateSwitch(int x, int y)
4056 game.switchgate_pos = !game.switchgate_pos;
4058 for (yy = 0; yy < lev_fieldy; yy++)
4060 for (xx = 0; xx < lev_fieldx; xx++)
4062 int element = Feld[xx][yy];
4064 if (element == EL_SWITCHGATE_SWITCH_UP ||
4065 element == EL_SWITCHGATE_SWITCH_DOWN)
4067 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4068 DrawLevelField(xx, yy);
4070 else if (element == EL_SWITCHGATE_OPEN ||
4071 element == EL_SWITCHGATE_OPENING)
4073 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4075 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4077 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4080 else if (element == EL_SWITCHGATE_CLOSED ||
4081 element == EL_SWITCHGATE_CLOSING)
4083 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4085 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4087 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4094 static int getInvisibleActiveFromInvisibleElement(int element)
4096 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4097 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4098 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4102 static int getInvisibleFromInvisibleActiveElement(int element)
4104 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4105 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4106 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4110 static void RedrawAllLightSwitchesAndInvisibleElements()
4114 for (y = 0; y < lev_fieldy; y++)
4116 for (x = 0; x < lev_fieldx; x++)
4118 int element = Feld[x][y];
4120 if (element == EL_LIGHT_SWITCH &&
4121 game.light_time_left > 0)
4123 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4124 DrawLevelField(x, y);
4126 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4127 game.light_time_left == 0)
4129 Feld[x][y] = EL_LIGHT_SWITCH;
4130 DrawLevelField(x, y);
4132 else if (element == EL_INVISIBLE_STEELWALL ||
4133 element == EL_INVISIBLE_WALL ||
4134 element == EL_INVISIBLE_SAND)
4136 if (game.light_time_left > 0)
4137 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4139 DrawLevelField(x, y);
4141 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4142 element == EL_INVISIBLE_WALL_ACTIVE ||
4143 element == EL_INVISIBLE_SAND_ACTIVE)
4145 if (game.light_time_left == 0)
4146 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4148 DrawLevelField(x, y);
4154 static void ToggleLightSwitch(int x, int y)
4156 int element = Feld[x][y];
4158 game.light_time_left =
4159 (element == EL_LIGHT_SWITCH ?
4160 level.time_light * FRAMES_PER_SECOND : 0);
4162 RedrawAllLightSwitchesAndInvisibleElements();
4165 static void ActivateTimegateSwitch(int x, int y)
4169 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4171 for (yy = 0; yy < lev_fieldy; yy++)
4173 for (xx = 0; xx < lev_fieldx; xx++)
4175 int element = Feld[xx][yy];
4177 if (element == EL_TIMEGATE_CLOSED ||
4178 element == EL_TIMEGATE_CLOSING)
4180 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4181 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4185 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4187 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4188 DrawLevelField(xx, yy);
4195 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4198 inline static int getElementMoveStepsize(int x, int y)
4200 int element = Feld[x][y];
4201 int direction = MovDir[x][y];
4202 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4203 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4204 int horiz_move = (dx != 0);
4205 int sign = (horiz_move ? dx : dy);
4206 int step = sign * element_info[element].move_stepsize;
4208 /* special values for move stepsize for spring and things on conveyor belt */
4212 if (element == EL_SPRING)
4213 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4214 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4215 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4216 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4218 if (CAN_FALL(element) &&
4219 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4220 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4221 else if (element == EL_SPRING)
4222 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4229 void Impact(int x, int y)
4231 boolean lastline = (y == lev_fieldy-1);
4232 boolean object_hit = FALSE;
4233 boolean impact = (lastline || object_hit);
4234 int element = Feld[x][y];
4235 int smashed = EL_STEELWALL;
4237 if (!lastline) /* check if element below was hit */
4239 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4242 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4243 MovDir[x][y + 1] != MV_DOWN ||
4244 MovPos[x][y + 1] <= TILEY / 2));
4247 object_hit = !IS_FREE(x, y + 1);
4250 /* do not smash moving elements that left the smashed field in time */
4251 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4252 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4256 smashed = MovingOrBlocked2Element(x, y + 1);
4258 impact = (lastline || object_hit);
4261 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4263 SplashAcid(x, y + 1);
4267 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4268 /* only reset graphic animation if graphic really changes after impact */
4270 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4272 ResetGfxAnimation(x, y);
4273 DrawLevelField(x, y);
4276 if (impact && CAN_EXPLODE_IMPACT(element))
4281 else if (impact && element == EL_PEARL)
4283 ResetGfxAnimation(x, y);
4285 Feld[x][y] = EL_PEARL_BREAKING;
4286 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4289 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4291 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4296 if (impact && element == EL_AMOEBA_DROP)
4298 if (object_hit && IS_PLAYER(x, y + 1))
4299 KillHeroUnlessEnemyProtected(x, y + 1);
4300 else if (object_hit && smashed == EL_PENGUIN)
4304 Feld[x][y] = EL_AMOEBA_GROWING;
4305 Store[x][y] = EL_AMOEBA_WET;
4307 ResetRandomAnimationValue(x, y);
4312 if (object_hit) /* check which object was hit */
4314 if (CAN_PASS_MAGIC_WALL(element) &&
4315 (smashed == EL_MAGIC_WALL ||
4316 smashed == EL_BD_MAGIC_WALL))
4319 int activated_magic_wall =
4320 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4321 EL_BD_MAGIC_WALL_ACTIVE);
4323 /* activate magic wall / mill */
4324 for (yy = 0; yy < lev_fieldy; yy++)
4325 for (xx = 0; xx < lev_fieldx; xx++)
4326 if (Feld[xx][yy] == smashed)
4327 Feld[xx][yy] = activated_magic_wall;
4329 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4330 game.magic_wall_active = TRUE;
4332 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4333 SND_MAGIC_WALL_ACTIVATING :
4334 SND_BD_MAGIC_WALL_ACTIVATING));
4337 if (IS_PLAYER(x, y + 1))
4339 if (CAN_SMASH_PLAYER(element))
4341 KillHeroUnlessEnemyProtected(x, y + 1);
4345 else if (smashed == EL_PENGUIN)
4347 if (CAN_SMASH_PLAYER(element))
4353 else if (element == EL_BD_DIAMOND)
4355 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4361 else if (((element == EL_SP_INFOTRON ||
4362 element == EL_SP_ZONK) &&
4363 (smashed == EL_SP_SNIKSNAK ||
4364 smashed == EL_SP_ELECTRON ||
4365 smashed == EL_SP_DISK_ORANGE)) ||
4366 (element == EL_SP_INFOTRON &&
4367 smashed == EL_SP_DISK_YELLOW))
4373 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4379 else if (CAN_SMASH_EVERYTHING(element))
4381 if (IS_CLASSIC_ENEMY(smashed) ||
4382 CAN_EXPLODE_SMASHED(smashed))
4387 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4389 if (smashed == EL_LAMP ||
4390 smashed == EL_LAMP_ACTIVE)
4395 else if (smashed == EL_NUT)
4397 Feld[x][y + 1] = EL_NUT_BREAKING;
4398 PlayLevelSound(x, y, SND_NUT_BREAKING);
4399 RaiseScoreElement(EL_NUT);
4402 else if (smashed == EL_PEARL)
4404 ResetGfxAnimation(x, y);
4406 Feld[x][y + 1] = EL_PEARL_BREAKING;
4407 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4410 else if (smashed == EL_DIAMOND)
4412 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4413 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4416 else if (IS_BELT_SWITCH(smashed))
4418 ToggleBeltSwitch(x, y + 1);
4420 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4421 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4423 ToggleSwitchgateSwitch(x, y + 1);
4425 else if (smashed == EL_LIGHT_SWITCH ||
4426 smashed == EL_LIGHT_SWITCH_ACTIVE)
4428 ToggleLightSwitch(x, y + 1);
4433 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4436 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4439 /* !!! TEST ONLY !!! */
4440 CheckElementChangeBySide(x, y + 1, smashed, element,
4441 CE_SWITCHED, CH_SIDE_TOP);
4442 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4443 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4445 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4446 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4447 CheckElementChangeBySide(x, y + 1, smashed, element,
4448 CE_SWITCHED, CH_SIDE_TOP);
4454 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4459 /* play sound of magic wall / mill */
4461 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4462 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4464 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4465 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4466 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4467 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4472 /* play sound of object that hits the ground */
4473 if (lastline || object_hit)
4474 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4477 inline static void TurnRoundExt(int x, int y)
4489 { 0, 0 }, { 0, 0 }, { 0, 0 },
4494 int left, right, back;
4498 { MV_DOWN, MV_UP, MV_RIGHT },
4499 { MV_UP, MV_DOWN, MV_LEFT },
4501 { MV_LEFT, MV_RIGHT, MV_DOWN },
4505 { MV_RIGHT, MV_LEFT, MV_UP }
4508 int element = Feld[x][y];
4509 int move_pattern = element_info[element].move_pattern;
4511 int old_move_dir = MovDir[x][y];
4512 int left_dir = turn[old_move_dir].left;
4513 int right_dir = turn[old_move_dir].right;
4514 int back_dir = turn[old_move_dir].back;
4516 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4517 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4518 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4519 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4521 int left_x = x + left_dx, left_y = y + left_dy;
4522 int right_x = x + right_dx, right_y = y + right_dy;
4523 int move_x = x + move_dx, move_y = y + move_dy;
4527 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4529 TestIfBadThingTouchesOtherBadThing(x, y);
4531 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4532 MovDir[x][y] = right_dir;
4533 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4534 MovDir[x][y] = left_dir;
4536 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4538 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4542 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4543 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4545 TestIfBadThingTouchesOtherBadThing(x, y);
4547 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4548 MovDir[x][y] = left_dir;
4549 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4550 MovDir[x][y] = right_dir;
4552 if ((element == EL_SPACESHIP ||
4553 element == EL_SP_SNIKSNAK ||
4554 element == EL_SP_ELECTRON)
4555 && MovDir[x][y] != old_move_dir)
4557 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4561 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4563 TestIfBadThingTouchesOtherBadThing(x, y);
4565 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4566 MovDir[x][y] = left_dir;
4567 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4568 MovDir[x][y] = right_dir;
4570 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4572 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4575 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4577 TestIfBadThingTouchesOtherBadThing(x, y);
4579 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4580 MovDir[x][y] = left_dir;
4581 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4582 MovDir[x][y] = right_dir;
4584 if (MovDir[x][y] != old_move_dir)
4588 else if (element == EL_YAMYAM)
4590 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4591 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4593 if (can_turn_left && can_turn_right)
4594 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4595 else if (can_turn_left)
4596 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4597 else if (can_turn_right)
4598 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4600 MovDir[x][y] = back_dir;
4602 MovDelay[x][y] = 16 + 16 * RND(3);
4604 else if (element == EL_DARK_YAMYAM)
4606 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4608 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4611 if (can_turn_left && can_turn_right)
4612 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4613 else if (can_turn_left)
4614 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4615 else if (can_turn_right)
4616 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4618 MovDir[x][y] = back_dir;
4620 MovDelay[x][y] = 16 + 16 * RND(3);
4622 else if (element == EL_PACMAN)
4624 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4625 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4627 if (can_turn_left && can_turn_right)
4628 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4629 else if (can_turn_left)
4630 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4631 else if (can_turn_right)
4632 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4634 MovDir[x][y] = back_dir;
4636 MovDelay[x][y] = 6 + RND(40);
4638 else if (element == EL_PIG)
4640 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4641 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4642 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4643 boolean should_turn_left, should_turn_right, should_move_on;
4645 int rnd = RND(rnd_value);
4647 should_turn_left = (can_turn_left &&
4649 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4650 y + back_dy + left_dy)));
4651 should_turn_right = (can_turn_right &&
4653 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4654 y + back_dy + right_dy)));
4655 should_move_on = (can_move_on &&
4658 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4659 y + move_dy + left_dy) ||
4660 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4661 y + move_dy + right_dy)));
4663 if (should_turn_left || should_turn_right || should_move_on)
4665 if (should_turn_left && should_turn_right && should_move_on)
4666 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4667 rnd < 2 * rnd_value / 3 ? right_dir :
4669 else if (should_turn_left && should_turn_right)
4670 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4671 else if (should_turn_left && should_move_on)
4672 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4673 else if (should_turn_right && should_move_on)
4674 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4675 else if (should_turn_left)
4676 MovDir[x][y] = left_dir;
4677 else if (should_turn_right)
4678 MovDir[x][y] = right_dir;
4679 else if (should_move_on)
4680 MovDir[x][y] = old_move_dir;
4682 else if (can_move_on && rnd > rnd_value / 8)
4683 MovDir[x][y] = old_move_dir;
4684 else if (can_turn_left && can_turn_right)
4685 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4686 else if (can_turn_left && rnd > rnd_value / 8)
4687 MovDir[x][y] = left_dir;
4688 else if (can_turn_right && rnd > rnd_value/8)
4689 MovDir[x][y] = right_dir;
4691 MovDir[x][y] = back_dir;
4693 xx = x + move_xy[MovDir[x][y]].x;
4694 yy = y + move_xy[MovDir[x][y]].y;
4697 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4698 if (!IN_LEV_FIELD(xx, yy) ||
4699 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4700 MovDir[x][y] = old_move_dir;
4702 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4703 MovDir[x][y] = old_move_dir;
4708 else if (element == EL_DRAGON)
4710 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4711 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4712 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4714 int rnd = RND(rnd_value);
4717 if (FrameCounter < 1 && x == 0 && y == 29)
4718 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4721 if (can_move_on && rnd > rnd_value / 8)
4722 MovDir[x][y] = old_move_dir;
4723 else if (can_turn_left && can_turn_right)
4724 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4725 else if (can_turn_left && rnd > rnd_value / 8)
4726 MovDir[x][y] = left_dir;
4727 else if (can_turn_right && rnd > rnd_value / 8)
4728 MovDir[x][y] = right_dir;
4730 MovDir[x][y] = back_dir;
4732 xx = x + move_xy[MovDir[x][y]].x;
4733 yy = y + move_xy[MovDir[x][y]].y;
4736 if (FrameCounter < 1 && x == 0 && y == 29)
4737 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4738 xx, yy, Feld[xx][yy],
4743 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4744 MovDir[x][y] = old_move_dir;
4746 if (!IS_FREE(xx, yy))
4747 MovDir[x][y] = old_move_dir;
4751 if (FrameCounter < 1 && x == 0 && y == 29)
4752 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4757 else if (element == EL_MOLE)
4759 boolean can_move_on =
4760 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4761 IS_AMOEBOID(Feld[move_x][move_y]) ||
4762 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4765 boolean can_turn_left =
4766 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4767 IS_AMOEBOID(Feld[left_x][left_y])));
4769 boolean can_turn_right =
4770 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4771 IS_AMOEBOID(Feld[right_x][right_y])));
4773 if (can_turn_left && can_turn_right)
4774 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4775 else if (can_turn_left)
4776 MovDir[x][y] = left_dir;
4778 MovDir[x][y] = right_dir;
4781 if (MovDir[x][y] != old_move_dir)
4784 else if (element == EL_BALLOON)
4786 MovDir[x][y] = game.balloon_dir;
4789 else if (element == EL_SPRING)
4792 if (MovDir[x][y] & MV_HORIZONTAL &&
4793 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4794 MovDir[x][y] = MV_NO_MOVING;
4796 if (MovDir[x][y] & MV_HORIZONTAL &&
4797 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4798 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4799 MovDir[x][y] = MV_NO_MOVING;
4804 else if (element == EL_ROBOT ||
4805 element == EL_SATELLITE ||
4806 element == EL_PENGUIN)
4808 int attr_x = -1, attr_y = -1;
4819 for (i = 0; i < MAX_PLAYERS; i++)
4821 struct PlayerInfo *player = &stored_player[i];
4822 int jx = player->jx, jy = player->jy;
4824 if (!player->active)
4828 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4837 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4838 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4839 game.engine_version < VERSION_IDENT(3,1,0,0)))
4841 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4848 if (element == EL_PENGUIN)
4851 static int xy[4][2] =
4859 for (i = 0; i < NUM_DIRECTIONS; i++)
4861 int ex = x + xy[i][0];
4862 int ey = y + xy[i][1];
4864 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4873 MovDir[x][y] = MV_NO_MOVING;
4875 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4876 else if (attr_x > x)
4877 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4879 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4880 else if (attr_y > y)
4881 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4883 if (element == EL_ROBOT)
4887 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4888 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4889 Moving2Blocked(x, y, &newx, &newy);
4891 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4892 MovDelay[x][y] = 8 + 8 * !RND(3);
4894 MovDelay[x][y] = 16;
4896 else if (element == EL_PENGUIN)
4902 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4904 boolean first_horiz = RND(2);
4905 int new_move_dir = MovDir[x][y];
4908 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4909 Moving2Blocked(x, y, &newx, &newy);
4911 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4915 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4916 Moving2Blocked(x, y, &newx, &newy);
4918 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4921 MovDir[x][y] = old_move_dir;
4925 else /* (element == EL_SATELLITE) */
4931 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4933 boolean first_horiz = RND(2);
4934 int new_move_dir = MovDir[x][y];
4937 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4938 Moving2Blocked(x, y, &newx, &newy);
4940 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4944 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4945 Moving2Blocked(x, y, &newx, &newy);
4947 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4950 MovDir[x][y] = old_move_dir;
4955 else if (move_pattern == MV_TURNING_LEFT ||
4956 move_pattern == MV_TURNING_RIGHT ||
4957 move_pattern == MV_TURNING_LEFT_RIGHT ||
4958 move_pattern == MV_TURNING_RIGHT_LEFT ||
4959 move_pattern == MV_TURNING_RANDOM ||
4960 move_pattern == MV_ALL_DIRECTIONS)
4962 boolean can_turn_left =
4963 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4964 boolean can_turn_right =
4965 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4967 if (move_pattern == MV_TURNING_LEFT)
4968 MovDir[x][y] = left_dir;
4969 else if (move_pattern == MV_TURNING_RIGHT)
4970 MovDir[x][y] = right_dir;
4971 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4972 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4973 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4974 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4975 else if (move_pattern == MV_TURNING_RANDOM)
4976 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4977 can_turn_right && !can_turn_left ? right_dir :
4978 RND(2) ? left_dir : right_dir);
4979 else if (can_turn_left && can_turn_right)
4980 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4981 else if (can_turn_left)
4982 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4983 else if (can_turn_right)
4984 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4986 MovDir[x][y] = back_dir;
4988 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4990 else if (move_pattern == MV_HORIZONTAL ||
4991 move_pattern == MV_VERTICAL)
4993 if (move_pattern & old_move_dir)
4994 MovDir[x][y] = back_dir;
4995 else if (move_pattern == MV_HORIZONTAL)
4996 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4997 else if (move_pattern == MV_VERTICAL)
4998 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5000 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5002 else if (move_pattern & MV_ANY_DIRECTION)
5004 MovDir[x][y] = move_pattern;
5005 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5007 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5009 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5010 MovDir[x][y] = left_dir;
5011 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5012 MovDir[x][y] = right_dir;
5014 if (MovDir[x][y] != old_move_dir)
5015 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5017 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5019 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5020 MovDir[x][y] = right_dir;
5021 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5022 MovDir[x][y] = left_dir;
5024 if (MovDir[x][y] != old_move_dir)
5025 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5027 else if (move_pattern == MV_TOWARDS_PLAYER ||
5028 move_pattern == MV_AWAY_FROM_PLAYER)
5030 int attr_x = -1, attr_y = -1;
5032 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5043 for (i = 0; i < MAX_PLAYERS; i++)
5045 struct PlayerInfo *player = &stored_player[i];
5046 int jx = player->jx, jy = player->jy;
5048 if (!player->active)
5052 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5060 MovDir[x][y] = MV_NO_MOVING;
5062 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5063 else if (attr_x > x)
5064 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5066 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5067 else if (attr_y > y)
5068 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5070 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5072 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5074 boolean first_horiz = RND(2);
5075 int new_move_dir = MovDir[x][y];
5078 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5079 Moving2Blocked(x, y, &newx, &newy);
5081 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5085 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5086 Moving2Blocked(x, y, &newx, &newy);
5088 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5091 MovDir[x][y] = old_move_dir;
5094 else if (move_pattern == MV_WHEN_PUSHED ||
5095 move_pattern == MV_WHEN_DROPPED)
5097 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5098 MovDir[x][y] = MV_NO_MOVING;
5102 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5104 static int test_xy[7][2] =
5114 static int test_dir[7] =
5124 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5125 int move_preference = -1000000; /* start with very low preference */
5126 int new_move_dir = MV_NO_MOVING;
5127 int start_test = RND(4);
5130 for (i = 0; i < NUM_DIRECTIONS; i++)
5132 int move_dir = test_dir[start_test + i];
5133 int move_dir_preference;
5135 xx = x + test_xy[start_test + i][0];
5136 yy = y + test_xy[start_test + i][1];
5138 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5139 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5141 new_move_dir = move_dir;
5146 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5149 move_dir_preference = -1 * RunnerVisit[xx][yy];
5150 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5151 move_dir_preference = PlayerVisit[xx][yy];
5153 if (move_dir_preference > move_preference)
5155 /* prefer field that has not been visited for the longest time */
5156 move_preference = move_dir_preference;
5157 new_move_dir = move_dir;
5159 else if (move_dir_preference == move_preference &&
5160 move_dir == old_move_dir)
5162 /* prefer last direction when all directions are preferred equally */
5163 move_preference = move_dir_preference;
5164 new_move_dir = move_dir;
5168 MovDir[x][y] = new_move_dir;
5169 if (old_move_dir != new_move_dir)
5172 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5180 static void TurnRound(int x, int y)
5182 int direction = MovDir[x][y];
5185 GfxDir[x][y] = MovDir[x][y];
5191 GfxDir[x][y] = MovDir[x][y];
5194 if (direction != MovDir[x][y])
5199 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5202 GfxAction[x][y] = ACTION_WAITING;
5206 static boolean JustBeingPushed(int x, int y)
5210 for (i = 0; i < MAX_PLAYERS; i++)
5212 struct PlayerInfo *player = &stored_player[i];
5214 if (player->active && player->is_pushing && player->MovPos)
5216 int next_jx = player->jx + (player->jx - player->last_jx);
5217 int next_jy = player->jy + (player->jy - player->last_jy);
5219 if (x == next_jx && y == next_jy)
5227 void StartMoving(int x, int y)
5230 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5232 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5233 int element = Feld[x][y];
5239 if (MovDelay[x][y] == 0)
5240 GfxAction[x][y] = ACTION_DEFAULT;
5242 /* !!! this should be handled more generic (not only for mole) !!! */
5243 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5244 GfxAction[x][y] = ACTION_DEFAULT;
5247 if (CAN_FALL(element) && y < lev_fieldy - 1)
5249 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5250 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5251 if (JustBeingPushed(x, y))
5254 if (element == EL_QUICKSAND_FULL)
5256 if (IS_FREE(x, y + 1))
5258 InitMovingField(x, y, MV_DOWN);
5259 started_moving = TRUE;
5261 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5262 Store[x][y] = EL_ROCK;
5264 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5266 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5269 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5271 if (!MovDelay[x][y])
5272 MovDelay[x][y] = TILEY + 1;
5281 Feld[x][y] = EL_QUICKSAND_EMPTY;
5282 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5283 Store[x][y + 1] = Store[x][y];
5286 PlayLevelSoundAction(x, y, ACTION_FILLING);
5288 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5292 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5293 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5295 InitMovingField(x, y, MV_DOWN);
5296 started_moving = TRUE;
5298 Feld[x][y] = EL_QUICKSAND_FILLING;
5299 Store[x][y] = element;
5301 PlayLevelSoundAction(x, y, ACTION_FILLING);
5303 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5306 else if (element == EL_MAGIC_WALL_FULL)
5308 if (IS_FREE(x, y + 1))
5310 InitMovingField(x, y, MV_DOWN);
5311 started_moving = TRUE;
5313 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5314 Store[x][y] = EL_CHANGED(Store[x][y]);
5316 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5318 if (!MovDelay[x][y])
5319 MovDelay[x][y] = TILEY/4 + 1;
5328 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5329 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5330 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5334 else if (element == EL_BD_MAGIC_WALL_FULL)
5336 if (IS_FREE(x, y + 1))
5338 InitMovingField(x, y, MV_DOWN);
5339 started_moving = TRUE;
5341 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5342 Store[x][y] = EL_CHANGED2(Store[x][y]);
5344 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5346 if (!MovDelay[x][y])
5347 MovDelay[x][y] = TILEY/4 + 1;
5356 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5357 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5358 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5362 else if (CAN_PASS_MAGIC_WALL(element) &&
5363 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5364 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5366 InitMovingField(x, y, MV_DOWN);
5367 started_moving = TRUE;
5370 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5371 EL_BD_MAGIC_WALL_FILLING);
5372 Store[x][y] = element;
5375 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5377 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5380 SplashAcid(x, y + 1);
5382 InitMovingField(x, y, MV_DOWN);
5383 started_moving = TRUE;
5385 Store[x][y] = EL_ACID;
5387 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5388 GfxAction[x][y + 1] = ACTION_ACTIVE;
5392 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5393 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5395 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5396 CAN_SMASH(element) && WasJustFalling[x][y] &&
5397 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5399 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5400 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5401 (Feld[x][y + 1] == EL_BLOCKED)))
5405 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5406 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5407 WasJustMoving[x][y] && !Pushed[x][y + 1])
5409 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5410 WasJustMoving[x][y])
5415 /* this is needed for a special case not covered by calling "Impact()"
5416 from "ContinueMoving()": if an element moves to a tile directly below
5417 another element which was just falling on that tile (which was empty
5418 in the previous frame), the falling element above would just stop
5419 instead of smashing the element below (in previous version, the above
5420 element was just checked for "moving" instead of "falling", resulting
5421 in incorrect smashes caused by horizontal movement of the above
5422 element; also, the case of the player being the element to smash was
5423 simply not covered here... :-/ ) */
5426 WasJustMoving[x][y] = 0;
5427 WasJustFalling[x][y] = 0;
5430 CheckCollision[x][y] = 0;
5433 if (IS_PLAYER(x, y + 1))
5434 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5439 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5441 if (MovDir[x][y] == MV_NO_MOVING)
5443 InitMovingField(x, y, MV_DOWN);
5444 started_moving = TRUE;
5447 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5449 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5450 MovDir[x][y] = MV_DOWN;
5452 InitMovingField(x, y, MV_DOWN);
5453 started_moving = TRUE;
5455 else if (element == EL_AMOEBA_DROP)
5457 Feld[x][y] = EL_AMOEBA_GROWING;
5458 Store[x][y] = EL_AMOEBA_WET;
5460 /* Store[x][y + 1] must be zero, because:
5461 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5464 #if OLD_GAME_BEHAVIOUR
5465 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5467 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5468 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5469 element != EL_DX_SUPABOMB)
5472 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5473 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5474 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5475 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5478 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5479 (IS_FREE(x - 1, y + 1) ||
5480 Feld[x - 1][y + 1] == EL_ACID));
5481 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5482 (IS_FREE(x + 1, y + 1) ||
5483 Feld[x + 1][y + 1] == EL_ACID));
5484 boolean can_fall_any = (can_fall_left || can_fall_right);
5485 boolean can_fall_both = (can_fall_left && can_fall_right);
5487 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5489 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5491 if (slippery_type == SLIPPERY_ONLY_LEFT)
5492 can_fall_right = FALSE;
5493 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5494 can_fall_left = FALSE;
5495 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5496 can_fall_right = FALSE;
5497 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5498 can_fall_left = FALSE;
5500 can_fall_any = (can_fall_left || can_fall_right);
5501 can_fall_both = (can_fall_left && can_fall_right);
5504 #if USE_NEW_SP_SLIPPERY
5505 /* !!! better use the same properties as for custom elements here !!! */
5506 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5507 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5509 can_fall_right = FALSE; /* slip down on left side */
5510 can_fall_both = FALSE;
5517 if (game.emulation == EMU_BOULDERDASH ||
5518 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5519 can_fall_right = FALSE; /* slip down on left side */
5521 can_fall_left = !(can_fall_right = RND(2));
5523 can_fall_both = FALSE;
5530 if (can_fall_both &&
5531 (game.emulation != EMU_BOULDERDASH &&
5532 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5533 can_fall_left = !(can_fall_right = RND(2));
5536 /* if not determined otherwise, prefer left side for slipping down */
5537 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5538 started_moving = TRUE;
5542 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5544 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5547 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5548 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5549 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5550 int belt_dir = game.belt_dir[belt_nr];
5552 if ((belt_dir == MV_LEFT && left_is_free) ||
5553 (belt_dir == MV_RIGHT && right_is_free))
5556 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5559 InitMovingField(x, y, belt_dir);
5560 started_moving = TRUE;
5563 Pushed[x][y] = TRUE;
5564 Pushed[nextx][y] = TRUE;
5567 GfxAction[x][y] = ACTION_DEFAULT;
5571 MovDir[x][y] = 0; /* if element was moving, stop it */
5576 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5578 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5580 if (CAN_MOVE(element) && !started_moving)
5583 int move_pattern = element_info[element].move_pattern;
5588 if (MovDir[x][y] == MV_NO_MOVING)
5590 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5591 x, y, element, element_info[element].token_name);
5592 printf("StartMoving(): This should never happen!\n");
5597 Moving2Blocked(x, y, &newx, &newy);
5600 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5603 if ((element == EL_SATELLITE ||
5604 element == EL_BALLOON ||
5605 element == EL_SPRING)
5606 && JustBeingPushed(x, y))
5613 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5614 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5616 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5617 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5618 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5622 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5623 element, element_info[element].token_name,
5624 WasJustMoving[x][y],
5625 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5626 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5627 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5628 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5632 WasJustMoving[x][y] = 0;
5635 CheckCollision[x][y] = 0;
5637 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5640 if (Feld[x][y] != element) /* element has changed */
5642 element = Feld[x][y];
5643 move_pattern = element_info[element].move_pattern;
5645 if (!CAN_MOVE(element))
5649 if (Feld[x][y] != element) /* element has changed */
5657 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5658 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5660 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5662 Moving2Blocked(x, y, &newx, &newy);
5663 if (Feld[newx][newy] == EL_BLOCKED)
5664 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5670 if (FrameCounter < 1 && x == 0 && y == 29)
5671 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5674 if (!MovDelay[x][y]) /* start new movement phase */
5676 /* all objects that can change their move direction after each step
5677 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5679 if (element != EL_YAMYAM &&
5680 element != EL_DARK_YAMYAM &&
5681 element != EL_PACMAN &&
5682 !(move_pattern & MV_ANY_DIRECTION) &&
5683 move_pattern != MV_TURNING_LEFT &&
5684 move_pattern != MV_TURNING_RIGHT &&
5685 move_pattern != MV_TURNING_LEFT_RIGHT &&
5686 move_pattern != MV_TURNING_RIGHT_LEFT &&
5687 move_pattern != MV_TURNING_RANDOM)
5692 if (FrameCounter < 1 && x == 0 && y == 29)
5693 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5696 if (MovDelay[x][y] && (element == EL_BUG ||
5697 element == EL_SPACESHIP ||
5698 element == EL_SP_SNIKSNAK ||
5699 element == EL_SP_ELECTRON ||
5700 element == EL_MOLE))
5701 DrawLevelField(x, y);
5705 if (MovDelay[x][y]) /* wait some time before next movement */
5710 if (element == EL_YAMYAM)
5713 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5714 DrawLevelElementAnimation(x, y, element);
5718 if (MovDelay[x][y]) /* element still has to wait some time */
5721 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5722 ResetGfxAnimation(x, y);
5726 if (GfxAction[x][y] != ACTION_WAITING)
5727 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5729 GfxAction[x][y] = ACTION_WAITING;
5733 if (element == EL_ROBOT ||
5735 element == EL_PACMAN ||
5737 element == EL_YAMYAM ||
5738 element == EL_DARK_YAMYAM)
5741 DrawLevelElementAnimation(x, y, element);
5743 DrawLevelElementAnimationIfNeeded(x, y, element);
5745 PlayLevelSoundAction(x, y, ACTION_WAITING);
5747 else if (element == EL_SP_ELECTRON)
5748 DrawLevelElementAnimationIfNeeded(x, y, element);
5749 else if (element == EL_DRAGON)
5752 int dir = MovDir[x][y];
5753 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5754 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5755 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5756 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5757 dir == MV_UP ? IMG_FLAMES_1_UP :
5758 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5759 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5762 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5765 GfxAction[x][y] = ACTION_ATTACKING;
5767 if (IS_PLAYER(x, y))
5768 DrawPlayerField(x, y);
5770 DrawLevelField(x, y);
5772 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5774 for (i = 1; i <= 3; i++)
5776 int xx = x + i * dx;
5777 int yy = y + i * dy;
5778 int sx = SCREENX(xx);
5779 int sy = SCREENY(yy);
5780 int flame_graphic = graphic + (i - 1);
5782 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5787 int flamed = MovingOrBlocked2Element(xx, yy);
5791 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5793 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5794 RemoveMovingField(xx, yy);
5796 RemoveField(xx, yy);
5798 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5801 RemoveMovingField(xx, yy);
5805 if (ChangeDelay[xx][yy])
5806 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5807 Feld[xx][yy] == EL_BLOCKED));
5811 ChangeDelay[xx][yy] = 0;
5813 Feld[xx][yy] = EL_FLAMES;
5814 if (IN_SCR_FIELD(sx, sy))
5816 DrawLevelFieldCrumbledSand(xx, yy);
5817 DrawGraphic(sx, sy, flame_graphic, frame);
5822 if (Feld[xx][yy] == EL_FLAMES)
5823 Feld[xx][yy] = EL_EMPTY;
5824 DrawLevelField(xx, yy);
5829 if (MovDelay[x][y]) /* element still has to wait some time */
5831 PlayLevelSoundAction(x, y, ACTION_WAITING);
5837 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5838 for all other elements GfxAction will be set by InitMovingField() */
5839 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5840 GfxAction[x][y] = ACTION_MOVING;
5844 /* now make next step */
5846 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5848 if (DONT_COLLIDE_WITH(element) &&
5849 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5850 !PLAYER_ENEMY_PROTECTED(newx, newy))
5853 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5857 /* player killed by element which is deadly when colliding with */
5859 KillHero(PLAYERINFO(newx, newy));
5866 else if (CAN_MOVE_INTO_ACID(element) &&
5867 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5868 (MovDir[x][y] == MV_DOWN ||
5869 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5871 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5872 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5876 else if ((element == EL_PENGUIN ||
5877 element == EL_ROBOT ||
5878 element == EL_SATELLITE ||
5879 element == EL_BALLOON ||
5880 IS_CUSTOM_ELEMENT(element)) &&
5881 IN_LEV_FIELD(newx, newy) &&
5882 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5885 SplashAcid(newx, newy);
5886 Store[x][y] = EL_ACID;
5888 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5890 if (Feld[newx][newy] == EL_EXIT_OPEN)
5894 DrawLevelField(x, y);
5896 Feld[x][y] = EL_EMPTY;
5897 DrawLevelField(x, y);
5900 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5901 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5902 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5904 local_player->friends_still_needed--;
5905 if (!local_player->friends_still_needed &&
5906 !local_player->GameOver && AllPlayersGone)
5907 local_player->LevelSolved = local_player->GameOver = TRUE;
5911 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5913 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5914 DrawLevelField(newx, newy);
5916 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5918 else if (!IS_FREE(newx, newy))
5920 GfxAction[x][y] = ACTION_WAITING;
5922 if (IS_PLAYER(x, y))
5923 DrawPlayerField(x, y);
5925 DrawLevelField(x, y);
5930 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5932 if (IS_FOOD_PIG(Feld[newx][newy]))
5934 if (IS_MOVING(newx, newy))
5935 RemoveMovingField(newx, newy);
5938 Feld[newx][newy] = EL_EMPTY;
5939 DrawLevelField(newx, newy);
5942 PlayLevelSound(x, y, SND_PIG_DIGGING);
5944 else if (!IS_FREE(newx, newy))
5946 if (IS_PLAYER(x, y))
5947 DrawPlayerField(x, y);
5949 DrawLevelField(x, y);
5958 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5961 else if (IS_CUSTOM_ELEMENT(element) &&
5962 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5966 !IS_FREE(newx, newy)
5971 int new_element = Feld[newx][newy];
5974 printf("::: '%s' digs '%s' [%d]\n",
5975 element_info[element].token_name,
5976 element_info[Feld[newx][newy]].token_name,
5977 StorePlayer[newx][newy]);
5980 if (!IS_FREE(newx, newy))
5982 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5983 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5986 /* no element can dig solid indestructible elements */
5987 if (IS_INDESTRUCTIBLE(new_element) &&
5988 !IS_DIGGABLE(new_element) &&
5989 !IS_COLLECTIBLE(new_element))
5992 if (AmoebaNr[newx][newy] &&
5993 (new_element == EL_AMOEBA_FULL ||
5994 new_element == EL_BD_AMOEBA ||
5995 new_element == EL_AMOEBA_GROWING))
5997 AmoebaCnt[AmoebaNr[newx][newy]]--;
5998 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6001 if (IS_MOVING(newx, newy))
6002 RemoveMovingField(newx, newy);
6005 RemoveField(newx, newy);
6006 DrawLevelField(newx, newy);
6009 /* if digged element was about to explode, prevent the explosion */
6010 ExplodeField[newx][newy] = EX_TYPE_NONE;
6012 PlayLevelSoundAction(x, y, action);
6017 Store[newx][newy] = EL_EMPTY;
6018 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6019 Store[newx][newy] = element_info[element].move_leave_element;
6021 Store[newx][newy] = EL_EMPTY;
6022 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6023 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6024 Store[newx][newy] = element_info[element].move_leave_element;
6027 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6028 element_info[element].can_leave_element = TRUE;
6031 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6033 RunnerVisit[x][y] = FrameCounter;
6034 PlayerVisit[x][y] /= 8; /* expire player visit path */
6040 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6042 if (!IS_FREE(newx, newy))
6044 if (IS_PLAYER(x, y))
6045 DrawPlayerField(x, y);
6047 DrawLevelField(x, y);
6053 boolean wanna_flame = !RND(10);
6054 int dx = newx - x, dy = newy - y;
6055 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6056 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6057 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6058 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6059 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6060 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6063 IS_CLASSIC_ENEMY(element1) ||
6064 IS_CLASSIC_ENEMY(element2)) &&
6065 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6066 element1 != EL_FLAMES && element2 != EL_FLAMES)
6069 ResetGfxAnimation(x, y);
6070 GfxAction[x][y] = ACTION_ATTACKING;
6073 if (IS_PLAYER(x, y))
6074 DrawPlayerField(x, y);
6076 DrawLevelField(x, y);
6078 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6080 MovDelay[x][y] = 50;
6084 RemoveField(newx, newy);
6086 Feld[newx][newy] = EL_FLAMES;
6087 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6090 RemoveField(newx1, newy1);
6092 Feld[newx1][newy1] = EL_FLAMES;
6094 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6097 RemoveField(newx2, newy2);
6099 Feld[newx2][newy2] = EL_FLAMES;
6106 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6107 Feld[newx][newy] == EL_DIAMOND)
6109 if (IS_MOVING(newx, newy))
6110 RemoveMovingField(newx, newy);
6113 Feld[newx][newy] = EL_EMPTY;
6114 DrawLevelField(newx, newy);
6117 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6119 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6120 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6122 if (AmoebaNr[newx][newy])
6124 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6125 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6126 Feld[newx][newy] == EL_BD_AMOEBA)
6127 AmoebaCnt[AmoebaNr[newx][newy]]--;
6132 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6134 if (IS_MOVING(newx, newy))
6137 RemoveMovingField(newx, newy);
6141 Feld[newx][newy] = EL_EMPTY;
6142 DrawLevelField(newx, newy);
6145 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6147 else if ((element == EL_PACMAN || element == EL_MOLE)
6148 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6150 if (AmoebaNr[newx][newy])
6152 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6153 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6154 Feld[newx][newy] == EL_BD_AMOEBA)
6155 AmoebaCnt[AmoebaNr[newx][newy]]--;
6158 if (element == EL_MOLE)
6160 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6161 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6163 ResetGfxAnimation(x, y);
6164 GfxAction[x][y] = ACTION_DIGGING;
6165 DrawLevelField(x, y);
6167 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6169 return; /* wait for shrinking amoeba */
6171 else /* element == EL_PACMAN */
6173 Feld[newx][newy] = EL_EMPTY;
6174 DrawLevelField(newx, newy);
6175 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6178 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6179 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6180 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6182 /* wait for shrinking amoeba to completely disappear */
6185 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6187 /* object was running against a wall */
6192 if (move_pattern & MV_ANY_DIRECTION &&
6193 move_pattern == MovDir[x][y])
6195 int blocking_element =
6196 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6199 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6200 element_info[element].token_name,
6201 element_info[blocking_element].token_name,
6205 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6208 element = Feld[x][y]; /* element might have changed */
6213 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6214 DrawLevelElementAnimation(x, y, element);
6216 if (element == EL_BUG ||
6217 element == EL_SPACESHIP ||
6218 element == EL_SP_SNIKSNAK)
6219 DrawLevelField(x, y);
6220 else if (element == EL_MOLE)
6221 DrawLevelField(x, y);
6222 else if (element == EL_BD_BUTTERFLY ||
6223 element == EL_BD_FIREFLY)
6224 DrawLevelElementAnimationIfNeeded(x, y, element);
6225 else if (element == EL_SATELLITE)
6226 DrawLevelElementAnimationIfNeeded(x, y, element);
6227 else if (element == EL_SP_ELECTRON)
6228 DrawLevelElementAnimationIfNeeded(x, y, element);
6231 if (DONT_TOUCH(element))
6232 TestIfBadThingTouchesHero(x, y);
6235 PlayLevelSoundAction(x, y, ACTION_WAITING);
6241 InitMovingField(x, y, MovDir[x][y]);
6243 PlayLevelSoundAction(x, y, ACTION_MOVING);
6247 ContinueMoving(x, y);
6250 void ContinueMoving(int x, int y)
6252 int element = Feld[x][y];
6253 int stored = Store[x][y];
6254 struct ElementInfo *ei = &element_info[element];
6255 int direction = MovDir[x][y];
6256 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6257 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6258 int newx = x + dx, newy = y + dy;
6260 int nextx = newx + dx, nexty = newy + dy;
6263 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6264 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6266 boolean pushed_by_player = Pushed[x][y];
6269 MovPos[x][y] += getElementMoveStepsize(x, y);
6272 if (pushed_by_player && IS_PLAYER(x, y))
6274 /* special case: moving object pushed by player */
6275 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6278 if (pushed_by_player) /* special case: moving object pushed by player */
6279 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6282 if (ABS(MovPos[x][y]) < TILEX)
6284 DrawLevelField(x, y);
6286 return; /* element is still moving */
6289 /* element reached destination field */
6291 Feld[x][y] = EL_EMPTY;
6292 Feld[newx][newy] = element;
6293 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6296 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6298 element = Feld[newx][newy] = EL_ACID;
6301 else if (element == EL_MOLE)
6303 Feld[x][y] = EL_SAND;
6305 DrawLevelFieldCrumbledSandNeighbours(x, y);
6307 else if (element == EL_QUICKSAND_FILLING)
6309 element = Feld[newx][newy] = get_next_element(element);
6310 Store[newx][newy] = Store[x][y];
6312 else if (element == EL_QUICKSAND_EMPTYING)
6314 Feld[x][y] = get_next_element(element);
6315 element = Feld[newx][newy] = Store[x][y];
6317 else if (element == EL_MAGIC_WALL_FILLING)
6319 element = Feld[newx][newy] = get_next_element(element);
6320 if (!game.magic_wall_active)
6321 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6322 Store[newx][newy] = Store[x][y];
6324 else if (element == EL_MAGIC_WALL_EMPTYING)
6326 Feld[x][y] = get_next_element(element);
6327 if (!game.magic_wall_active)
6328 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6329 element = Feld[newx][newy] = Store[x][y];
6331 else if (element == EL_BD_MAGIC_WALL_FILLING)
6333 element = Feld[newx][newy] = get_next_element(element);
6334 if (!game.magic_wall_active)
6335 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6336 Store[newx][newy] = Store[x][y];
6338 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6340 Feld[x][y] = get_next_element(element);
6341 if (!game.magic_wall_active)
6342 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6343 element = Feld[newx][newy] = Store[x][y];
6345 else if (element == EL_AMOEBA_DROPPING)
6347 Feld[x][y] = get_next_element(element);
6348 element = Feld[newx][newy] = Store[x][y];
6350 else if (element == EL_SOKOBAN_OBJECT)
6353 Feld[x][y] = Back[x][y];
6355 if (Back[newx][newy])
6356 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6358 Back[x][y] = Back[newx][newy] = 0;
6361 else if (Store[x][y] == EL_ACID)
6363 element = Feld[newx][newy] = EL_ACID;
6367 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6368 ei->move_leave_element != EL_EMPTY &&
6369 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6370 Store[x][y] != EL_EMPTY))
6372 /* some elements can leave other elements behind after moving */
6374 Feld[x][y] = ei->move_leave_element;
6375 InitField(x, y, FALSE);
6377 if (GFX_CRUMBLED(Feld[x][y]))
6378 DrawLevelFieldCrumbledSandNeighbours(x, y);
6382 Store[x][y] = EL_EMPTY;
6383 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6384 MovDelay[newx][newy] = 0;
6386 if (CAN_CHANGE(element))
6388 /* copy element change control values to new field */
6389 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6390 ChangePage[newx][newy] = ChangePage[x][y];
6391 Changed[newx][newy] = Changed[x][y];
6392 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6395 ChangeDelay[x][y] = 0;
6396 ChangePage[x][y] = -1;
6397 Changed[x][y] = CE_BITMASK_DEFAULT;
6398 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6400 /* copy animation control values to new field */
6401 GfxFrame[newx][newy] = GfxFrame[x][y];
6402 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6403 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6404 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6406 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6408 ResetGfxAnimation(x, y); /* reset animation values for old field */
6411 /* some elements can leave other elements behind after moving */
6413 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6414 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6415 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6417 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6418 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6422 int move_leave_element = ei->move_leave_element;
6424 Feld[x][y] = move_leave_element;
6425 InitField(x, y, FALSE);
6427 if (GFX_CRUMBLED(Feld[x][y]))
6428 DrawLevelFieldCrumbledSandNeighbours(x, y);
6430 if (ELEM_IS_PLAYER(move_leave_element))
6431 RelocatePlayer(x, y, move_leave_element);
6436 /* some elements can leave other elements behind after moving */
6437 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6438 ei->move_leave_element != EL_EMPTY &&
6439 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6440 ei->can_leave_element_last))
6442 Feld[x][y] = ei->move_leave_element;
6443 InitField(x, y, FALSE);
6445 if (GFX_CRUMBLED(Feld[x][y]))
6446 DrawLevelFieldCrumbledSandNeighbours(x, y);
6449 ei->can_leave_element_last = ei->can_leave_element;
6450 ei->can_leave_element = FALSE;
6454 /* 2.1.1 (does not work correctly for spring) */
6455 if (!CAN_MOVE(element))
6456 MovDir[newx][newy] = 0;
6460 /* (does not work for falling objects that slide horizontally) */
6461 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6462 MovDir[newx][newy] = 0;
6465 if (!CAN_MOVE(element) ||
6466 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6467 MovDir[newx][newy] = 0;
6471 if (!CAN_MOVE(element) ||
6472 (CAN_FALL(element) && direction == MV_DOWN))
6473 GfxDir[x][y] = MovDir[newx][newy] = 0;
6475 if (!CAN_MOVE(element) ||
6476 (CAN_FALL(element) && direction == MV_DOWN &&
6477 (element == EL_SPRING ||
6478 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6479 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6480 GfxDir[x][y] = MovDir[newx][newy] = 0;
6486 DrawLevelField(x, y);
6487 DrawLevelField(newx, newy);
6489 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6491 /* prevent pushed element from moving on in pushed direction */
6492 if (pushed_by_player && CAN_MOVE(element) &&
6493 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6494 !(element_info[element].move_pattern & direction))
6495 TurnRound(newx, newy);
6498 /* prevent elements on conveyor belt from moving on in last direction */
6499 if (pushed_by_conveyor && CAN_FALL(element) &&
6500 direction & MV_HORIZONTAL)
6503 if (CAN_MOVE(element))
6504 InitMovDir(newx, newy);
6506 MovDir[newx][newy] = 0;
6508 MovDir[newx][newy] = 0;
6513 if (!pushed_by_player)
6515 int nextx = newx + dx, nexty = newy + dy;
6516 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6518 WasJustMoving[newx][newy] = 3;
6520 if (CAN_FALL(element) && direction == MV_DOWN)
6521 WasJustFalling[newx][newy] = 3;
6523 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6524 CheckCollision[newx][newy] = 2;
6527 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6529 TestIfBadThingTouchesHero(newx, newy);
6530 TestIfBadThingTouchesFriend(newx, newy);
6532 if (!IS_CUSTOM_ELEMENT(element))
6533 TestIfBadThingTouchesOtherBadThing(newx, newy);
6535 else if (element == EL_PENGUIN)
6536 TestIfFriendTouchesBadThing(newx, newy);
6538 #if USE_NEW_MOVE_STYLE
6540 if (CAN_FALL(element) && direction == MV_DOWN &&
6541 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6542 IS_PLAYER(x, newy + 1))
6543 printf("::: we would now kill the player [%d]\n", FrameCounter);
6546 /* give the player one last chance (one more frame) to move away */
6547 if (CAN_FALL(element) && direction == MV_DOWN &&
6548 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6549 (!IS_PLAYER(x, newy + 1) ||
6550 game.engine_version < VERSION_IDENT(3,1,1,0)))
6553 if (CAN_FALL(element) && direction == MV_DOWN &&
6554 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6562 if (pushed_by_player && !game.use_bug_change_when_pushing)
6564 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6567 if (pushed_by_player)
6572 int dig_side = MV_DIR_OPPOSITE(direction);
6574 static int trigger_sides[4] =
6576 CH_SIDE_RIGHT, /* moving left */
6577 CH_SIDE_LEFT, /* moving right */
6578 CH_SIDE_BOTTOM, /* moving up */
6579 CH_SIDE_TOP, /* moving down */
6581 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6583 struct PlayerInfo *player = PLAYERINFO(x, y);
6585 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6586 player->index_bit, dig_side);
6587 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6588 player->index_bit, dig_side);
6593 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6597 if (ChangePage[newx][newy] != -1) /* delayed change */
6598 ChangeElement(newx, newy, ChangePage[newx][newy]);
6603 TestIfElementHitsCustomElement(newx, newy, direction);
6607 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6609 int hitting_element = Feld[newx][newy];
6611 /* !!! fix side (direction) orientation here and elsewhere !!! */
6612 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6616 if (IN_LEV_FIELD(nextx, nexty))
6618 int opposite_direction = MV_DIR_OPPOSITE(direction);
6619 int hitting_side = direction;
6620 int touched_side = opposite_direction;
6621 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6622 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6623 MovDir[nextx][nexty] != direction ||
6624 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6630 CheckElementChangeBySide(nextx, nexty, touched_element,
6631 CE_HIT_BY_SOMETHING, opposite_direction);
6633 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6634 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6636 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6638 struct ElementChangeInfo *change =
6639 &element_info[hitting_element].change_page[i];
6641 if (change->can_change &&
6642 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6643 change->trigger_side & touched_side &&
6644 change->trigger_element == touched_element)
6646 CheckElementChangeByPage(newx, newy, hitting_element,
6647 touched_element, CE_OTHER_IS_HITTING,i);
6653 if (IS_CUSTOM_ELEMENT(touched_element) &&
6654 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6656 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6658 struct ElementChangeInfo *change =
6659 &element_info[touched_element].change_page[i];
6661 if (change->can_change &&
6662 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6663 change->trigger_side & hitting_side &&
6664 change->trigger_element == hitting_element)
6666 CheckElementChangeByPage(nextx, nexty, touched_element,
6667 hitting_element, CE_OTHER_GETS_HIT, i);
6678 TestIfPlayerTouchesCustomElement(newx, newy);
6679 TestIfElementTouchesCustomElement(newx, newy);
6682 int AmoebeNachbarNr(int ax, int ay)
6685 int element = Feld[ax][ay];
6687 static int xy[4][2] =
6695 for (i = 0; i < NUM_DIRECTIONS; i++)
6697 int x = ax + xy[i][0];
6698 int y = ay + xy[i][1];
6700 if (!IN_LEV_FIELD(x, y))
6703 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6704 group_nr = AmoebaNr[x][y];
6710 void AmoebenVereinigen(int ax, int ay)
6712 int i, x, y, xx, yy;
6713 int new_group_nr = AmoebaNr[ax][ay];
6714 static int xy[4][2] =
6722 if (new_group_nr == 0)
6725 for (i = 0; i < NUM_DIRECTIONS; i++)
6730 if (!IN_LEV_FIELD(x, y))
6733 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6734 Feld[x][y] == EL_BD_AMOEBA ||
6735 Feld[x][y] == EL_AMOEBA_DEAD) &&
6736 AmoebaNr[x][y] != new_group_nr)
6738 int old_group_nr = AmoebaNr[x][y];
6740 if (old_group_nr == 0)
6743 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6744 AmoebaCnt[old_group_nr] = 0;
6745 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6746 AmoebaCnt2[old_group_nr] = 0;
6748 for (yy = 0; yy < lev_fieldy; yy++)
6750 for (xx = 0; xx < lev_fieldx; xx++)
6752 if (AmoebaNr[xx][yy] == old_group_nr)
6753 AmoebaNr[xx][yy] = new_group_nr;
6760 void AmoebeUmwandeln(int ax, int ay)
6764 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6766 int group_nr = AmoebaNr[ax][ay];
6771 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6772 printf("AmoebeUmwandeln(): This should never happen!\n");
6777 for (y = 0; y < lev_fieldy; y++)
6779 for (x = 0; x < lev_fieldx; x++)
6781 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6784 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6788 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6789 SND_AMOEBA_TURNING_TO_GEM :
6790 SND_AMOEBA_TURNING_TO_ROCK));
6795 static int xy[4][2] =
6803 for (i = 0; i < NUM_DIRECTIONS; i++)
6808 if (!IN_LEV_FIELD(x, y))
6811 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6813 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6814 SND_AMOEBA_TURNING_TO_GEM :
6815 SND_AMOEBA_TURNING_TO_ROCK));
6822 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6825 int group_nr = AmoebaNr[ax][ay];
6826 boolean done = FALSE;
6831 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6832 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6837 for (y = 0; y < lev_fieldy; y++)
6839 for (x = 0; x < lev_fieldx; x++)
6841 if (AmoebaNr[x][y] == group_nr &&
6842 (Feld[x][y] == EL_AMOEBA_DEAD ||
6843 Feld[x][y] == EL_BD_AMOEBA ||
6844 Feld[x][y] == EL_AMOEBA_GROWING))
6847 Feld[x][y] = new_element;
6848 InitField(x, y, FALSE);
6849 DrawLevelField(x, y);
6856 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6857 SND_BD_AMOEBA_TURNING_TO_ROCK :
6858 SND_BD_AMOEBA_TURNING_TO_GEM));
6861 void AmoebeWaechst(int x, int y)
6863 static unsigned long sound_delay = 0;
6864 static unsigned long sound_delay_value = 0;
6866 if (!MovDelay[x][y]) /* start new growing cycle */
6870 if (DelayReached(&sound_delay, sound_delay_value))
6873 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6875 if (Store[x][y] == EL_BD_AMOEBA)
6876 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6878 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6880 sound_delay_value = 30;
6884 if (MovDelay[x][y]) /* wait some time before growing bigger */
6887 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6889 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6890 6 - MovDelay[x][y]);
6892 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6895 if (!MovDelay[x][y])
6897 Feld[x][y] = Store[x][y];
6899 DrawLevelField(x, y);
6904 void AmoebaDisappearing(int x, int y)
6906 static unsigned long sound_delay = 0;
6907 static unsigned long sound_delay_value = 0;
6909 if (!MovDelay[x][y]) /* start new shrinking cycle */
6913 if (DelayReached(&sound_delay, sound_delay_value))
6914 sound_delay_value = 30;
6917 if (MovDelay[x][y]) /* wait some time before shrinking */
6920 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6922 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6923 6 - MovDelay[x][y]);
6925 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6928 if (!MovDelay[x][y])
6930 Feld[x][y] = EL_EMPTY;
6931 DrawLevelField(x, y);
6933 /* don't let mole enter this field in this cycle;
6934 (give priority to objects falling to this field from above) */
6940 void AmoebeAbleger(int ax, int ay)
6943 int element = Feld[ax][ay];
6944 int graphic = el2img(element);
6945 int newax = ax, neway = ay;
6946 static int xy[4][2] =
6954 if (!level.amoeba_speed)
6956 Feld[ax][ay] = EL_AMOEBA_DEAD;
6957 DrawLevelField(ax, ay);
6961 if (IS_ANIMATED(graphic))
6962 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6964 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6965 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6967 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6970 if (MovDelay[ax][ay])
6974 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6977 int x = ax + xy[start][0];
6978 int y = ay + xy[start][1];
6980 if (!IN_LEV_FIELD(x, y))
6984 if (IS_FREE(x, y) ||
6985 CAN_GROW_INTO(Feld[x][y]) ||
6986 Feld[x][y] == EL_QUICKSAND_EMPTY)
6992 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6993 if (IS_FREE(x, y) ||
6994 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7001 if (newax == ax && neway == ay)
7004 else /* normal or "filled" (BD style) amoeba */
7007 boolean waiting_for_player = FALSE;
7009 for (i = 0; i < NUM_DIRECTIONS; i++)
7011 int j = (start + i) % 4;
7012 int x = ax + xy[j][0];
7013 int y = ay + xy[j][1];
7015 if (!IN_LEV_FIELD(x, y))
7019 if (IS_FREE(x, y) ||
7020 CAN_GROW_INTO(Feld[x][y]) ||
7021 Feld[x][y] == EL_QUICKSAND_EMPTY)
7028 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7029 if (IS_FREE(x, y) ||
7030 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7037 else if (IS_PLAYER(x, y))
7038 waiting_for_player = TRUE;
7041 if (newax == ax && neway == ay) /* amoeba cannot grow */
7044 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7046 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7049 Feld[ax][ay] = EL_AMOEBA_DEAD;
7050 DrawLevelField(ax, ay);
7051 AmoebaCnt[AmoebaNr[ax][ay]]--;
7053 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7055 if (element == EL_AMOEBA_FULL)
7056 AmoebeUmwandeln(ax, ay);
7057 else if (element == EL_BD_AMOEBA)
7058 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7063 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7065 /* amoeba gets larger by growing in some direction */
7067 int new_group_nr = AmoebaNr[ax][ay];
7070 if (new_group_nr == 0)
7072 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7073 printf("AmoebeAbleger(): This should never happen!\n");
7078 AmoebaNr[newax][neway] = new_group_nr;
7079 AmoebaCnt[new_group_nr]++;
7080 AmoebaCnt2[new_group_nr]++;
7082 /* if amoeba touches other amoeba(s) after growing, unify them */
7083 AmoebenVereinigen(newax, neway);
7085 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7087 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7093 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7094 (neway == lev_fieldy - 1 && newax != ax))
7096 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7097 Store[newax][neway] = element;
7099 else if (neway == ay)
7101 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7103 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7105 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7110 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7111 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7112 Store[ax][ay] = EL_AMOEBA_DROP;
7113 ContinueMoving(ax, ay);
7117 DrawLevelField(newax, neway);
7120 void Life(int ax, int ay)
7123 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7125 int element = Feld[ax][ay];
7126 int graphic = el2img(element);
7127 boolean changed = FALSE;
7129 if (IS_ANIMATED(graphic))
7130 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7135 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7136 MovDelay[ax][ay] = life_time;
7138 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7141 if (MovDelay[ax][ay])
7145 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7147 int xx = ax+x1, yy = ay+y1;
7150 if (!IN_LEV_FIELD(xx, yy))
7153 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7155 int x = xx+x2, y = yy+y2;
7157 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7160 if (((Feld[x][y] == element ||
7161 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7163 (IS_FREE(x, y) && Stop[x][y]))
7167 if (xx == ax && yy == ay) /* field in the middle */
7169 if (nachbarn < life[0] || nachbarn > life[1])
7171 Feld[xx][yy] = EL_EMPTY;
7173 DrawLevelField(xx, yy);
7174 Stop[xx][yy] = TRUE;
7179 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7180 { /* free border field */
7181 if (nachbarn >= life[2] && nachbarn <= life[3])
7183 Feld[xx][yy] = element;
7184 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7186 DrawLevelField(xx, yy);
7187 Stop[xx][yy] = TRUE;
7192 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7193 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7194 { /* free border field */
7195 if (nachbarn >= life[2] && nachbarn <= life[3])
7197 Feld[xx][yy] = element;
7198 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7200 DrawLevelField(xx, yy);
7201 Stop[xx][yy] = TRUE;
7209 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7210 SND_GAME_OF_LIFE_GROWING);
7213 static void InitRobotWheel(int x, int y)
7215 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7218 static void RunRobotWheel(int x, int y)
7220 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7223 static void StopRobotWheel(int x, int y)
7225 if (ZX == x && ZY == y)
7229 static void InitTimegateWheel(int x, int y)
7232 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7234 /* another brainless, "type style" bug ... :-( */
7235 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7239 static void RunTimegateWheel(int x, int y)
7241 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7244 void CheckExit(int x, int y)
7246 if (local_player->gems_still_needed > 0 ||
7247 local_player->sokobanfields_still_needed > 0 ||
7248 local_player->lights_still_needed > 0)
7250 int element = Feld[x][y];
7251 int graphic = el2img(element);
7253 if (IS_ANIMATED(graphic))
7254 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7259 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7262 Feld[x][y] = EL_EXIT_OPENING;
7264 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7267 void CheckExitSP(int x, int y)
7269 if (local_player->gems_still_needed > 0)
7271 int element = Feld[x][y];
7272 int graphic = el2img(element);
7274 if (IS_ANIMATED(graphic))
7275 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7280 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7283 Feld[x][y] = EL_SP_EXIT_OPENING;
7285 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7288 static void CloseAllOpenTimegates()
7292 for (y = 0; y < lev_fieldy; y++)
7294 for (x = 0; x < lev_fieldx; x++)
7296 int element = Feld[x][y];
7298 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7300 Feld[x][y] = EL_TIMEGATE_CLOSING;
7302 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7304 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7311 void EdelsteinFunkeln(int x, int y)
7313 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7316 if (Feld[x][y] == EL_BD_DIAMOND)
7319 if (MovDelay[x][y] == 0) /* next animation frame */
7320 MovDelay[x][y] = 11 * !SimpleRND(500);
7322 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7326 if (setup.direct_draw && MovDelay[x][y])
7327 SetDrawtoField(DRAW_BUFFERED);
7329 DrawLevelElementAnimation(x, y, Feld[x][y]);
7331 if (MovDelay[x][y] != 0)
7333 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7334 10 - MovDelay[x][y]);
7336 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7338 if (setup.direct_draw)
7342 dest_x = FX + SCREENX(x) * TILEX;
7343 dest_y = FY + SCREENY(y) * TILEY;
7345 BlitBitmap(drawto_field, window,
7346 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7347 SetDrawtoField(DRAW_DIRECT);
7353 void MauerWaechst(int x, int y)
7357 if (!MovDelay[x][y]) /* next animation frame */
7358 MovDelay[x][y] = 3 * delay;
7360 if (MovDelay[x][y]) /* wait some time before next frame */
7364 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7366 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7367 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7369 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7372 if (!MovDelay[x][y])
7374 if (MovDir[x][y] == MV_LEFT)
7376 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7377 DrawLevelField(x - 1, y);
7379 else if (MovDir[x][y] == MV_RIGHT)
7381 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7382 DrawLevelField(x + 1, y);
7384 else if (MovDir[x][y] == MV_UP)
7386 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7387 DrawLevelField(x, y - 1);
7391 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7392 DrawLevelField(x, y + 1);
7395 Feld[x][y] = Store[x][y];
7397 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7398 DrawLevelField(x, y);
7403 void MauerAbleger(int ax, int ay)
7405 int element = Feld[ax][ay];
7406 int graphic = el2img(element);
7407 boolean oben_frei = FALSE, unten_frei = FALSE;
7408 boolean links_frei = FALSE, rechts_frei = FALSE;
7409 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7410 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7411 boolean new_wall = FALSE;
7413 if (IS_ANIMATED(graphic))
7414 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7416 if (!MovDelay[ax][ay]) /* start building new wall */
7417 MovDelay[ax][ay] = 6;
7419 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7422 if (MovDelay[ax][ay])
7426 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7428 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7430 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7432 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7435 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7436 element == EL_EXPANDABLE_WALL_ANY)
7440 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7441 Store[ax][ay-1] = element;
7442 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7443 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7444 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7445 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7450 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7451 Store[ax][ay+1] = element;
7452 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7453 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7454 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7455 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7460 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7461 element == EL_EXPANDABLE_WALL_ANY ||
7462 element == EL_EXPANDABLE_WALL)
7466 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7467 Store[ax-1][ay] = element;
7468 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7469 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7470 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7471 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7477 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7478 Store[ax+1][ay] = element;
7479 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7480 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7481 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7482 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7487 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7488 DrawLevelField(ax, ay);
7490 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7492 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7493 unten_massiv = TRUE;
7494 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7495 links_massiv = TRUE;
7496 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7497 rechts_massiv = TRUE;
7499 if (((oben_massiv && unten_massiv) ||
7500 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7501 element == EL_EXPANDABLE_WALL) &&
7502 ((links_massiv && rechts_massiv) ||
7503 element == EL_EXPANDABLE_WALL_VERTICAL))
7504 Feld[ax][ay] = EL_WALL;
7508 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7510 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7514 void CheckForDragon(int x, int y)
7517 boolean dragon_found = FALSE;
7518 static int xy[4][2] =
7526 for (i = 0; i < NUM_DIRECTIONS; i++)
7528 for (j = 0; j < 4; j++)
7530 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7532 if (IN_LEV_FIELD(xx, yy) &&
7533 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7535 if (Feld[xx][yy] == EL_DRAGON)
7536 dragon_found = TRUE;
7545 for (i = 0; i < NUM_DIRECTIONS; i++)
7547 for (j = 0; j < 3; j++)
7549 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7551 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7553 Feld[xx][yy] = EL_EMPTY;
7554 DrawLevelField(xx, yy);
7563 static void InitBuggyBase(int x, int y)
7565 int element = Feld[x][y];
7566 int activating_delay = FRAMES_PER_SECOND / 4;
7569 (element == EL_SP_BUGGY_BASE ?
7570 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7571 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7573 element == EL_SP_BUGGY_BASE_ACTIVE ?
7574 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7577 static void WarnBuggyBase(int x, int y)
7580 static int xy[4][2] =
7588 for (i = 0; i < NUM_DIRECTIONS; i++)
7590 int xx = x + xy[i][0], yy = y + xy[i][1];
7592 if (IS_PLAYER(xx, yy))
7594 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7601 static void InitTrap(int x, int y)
7603 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7606 static void ActivateTrap(int x, int y)
7608 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7611 static void ChangeActiveTrap(int x, int y)
7613 int graphic = IMG_TRAP_ACTIVE;
7615 /* if new animation frame was drawn, correct crumbled sand border */
7616 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7617 DrawLevelFieldCrumbledSand(x, y);
7620 static void ChangeElementNowExt(int x, int y, int target_element)
7622 int previous_move_direction = MovDir[x][y];
7624 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7625 IS_WALKABLE(Feld[x][y]));
7627 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7628 IS_WALKABLE(Feld[x][y]) &&
7632 /* check if element under player changes from accessible to unaccessible
7633 (needed for special case of dropping element which then changes) */
7634 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7635 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7638 printf("::: BOOOM! [%d, '%s']\n", target_element,
7639 element_info[target_element].token_name);
7651 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7652 RemoveMovingField(x, y);
7656 Feld[x][y] = target_element;
7659 Feld[x][y] = target_element;
7662 ResetGfxAnimation(x, y);
7663 ResetRandomAnimationValue(x, y);
7665 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7666 MovDir[x][y] = previous_move_direction;
7669 InitField_WithBug1(x, y, FALSE);
7671 InitField(x, y, FALSE);
7672 if (CAN_MOVE(Feld[x][y]))
7676 DrawLevelField(x, y);
7678 if (GFX_CRUMBLED(Feld[x][y]))
7679 DrawLevelFieldCrumbledSandNeighbours(x, y);
7683 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7687 TestIfBadThingTouchesHero(x, y);
7688 TestIfPlayerTouchesCustomElement(x, y);
7689 TestIfElementTouchesCustomElement(x, y);
7692 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7693 if (ELEM_IS_PLAYER(target_element))
7694 RelocatePlayer(x, y, target_element);
7697 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7701 TestIfBadThingTouchesHero(x, y);
7702 TestIfPlayerTouchesCustomElement(x, y);
7703 TestIfElementTouchesCustomElement(x, y);
7707 static boolean ChangeElementNow(int x, int y, int element, int page)
7709 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7711 int old_element = Feld[x][y];
7713 /* always use default change event to prevent running into a loop */
7714 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7715 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7717 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7719 /* reset actual trigger element and player */
7720 change->actual_trigger_element = EL_EMPTY;
7721 change->actual_trigger_player = EL_PLAYER_1;
7724 /* do not change already changed elements with same change event */
7726 if (Changed[x][y] & ChangeEvent[x][y])
7733 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7736 /* !!! indirect change before direct change !!! */
7737 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7740 if (change->explode)
7747 if (change->use_target_content)
7749 boolean complete_replace = TRUE;
7750 boolean can_replace[3][3];
7753 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7756 boolean is_walkable;
7757 boolean is_diggable;
7758 boolean is_collectible;
7759 boolean is_removable;
7760 boolean is_destructible;
7761 int ex = x + xx - 1;
7762 int ey = y + yy - 1;
7763 int content_element = change->target_content[xx][yy];
7766 can_replace[xx][yy] = TRUE;
7768 if (ex == x && ey == y) /* do not check changing element itself */
7771 if (content_element == EL_EMPTY_SPACE)
7773 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7778 if (!IN_LEV_FIELD(ex, ey))
7780 can_replace[xx][yy] = FALSE;
7781 complete_replace = FALSE;
7787 if (Changed[ex][ey]) /* do not change already changed elements */
7789 can_replace[xx][yy] = FALSE;
7790 complete_replace = FALSE;
7798 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7799 e = MovingOrBlocked2Element(ex, ey);
7804 is_empty = (IS_FREE(ex, ey) ||
7805 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7806 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7807 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7811 is_empty = (IS_FREE(ex, ey) ||
7812 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7814 is_empty = (IS_FREE(ex, ey) ||
7815 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7820 is_walkable = (is_empty || IS_WALKABLE(e));
7821 is_diggable = (is_empty || IS_DIGGABLE(e));
7822 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7823 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7824 is_removable = (is_diggable || is_collectible);
7826 can_replace[xx][yy] =
7827 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7828 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7829 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7830 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7831 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7832 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7833 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7835 if (!can_replace[xx][yy])
7836 complete_replace = FALSE;
7838 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7839 IS_WALKABLE(content_element)));
7841 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7843 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7846 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7847 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7848 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7850 can_replace[xx][yy] = FALSE;
7851 complete_replace = FALSE;
7856 if (!change->only_if_complete || complete_replace)
7858 boolean something_has_changed = FALSE;
7860 if (change->only_if_complete && change->use_random_replace &&
7861 RND(100) < change->random_percentage)
7864 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7866 int ex = x + xx - 1;
7867 int ey = y + yy - 1;
7868 int content_element;
7870 if (can_replace[xx][yy] && (!change->use_random_replace ||
7871 RND(100) < change->random_percentage))
7873 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7874 RemoveMovingField(ex, ey);
7876 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7878 content_element = change->target_content[xx][yy];
7879 target_element = GET_TARGET_ELEMENT(content_element, change);
7881 ChangeElementNowExt(ex, ey, target_element);
7883 something_has_changed = TRUE;
7885 /* for symmetry reasons, freeze newly created border elements */
7886 if (ex != x || ey != y)
7887 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7891 if (something_has_changed)
7892 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7897 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7899 ChangeElementNowExt(x, y, target_element);
7901 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7905 /* this uses direct change before indirect change */
7906 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7912 static void ChangeElement(int x, int y, int page)
7914 int element = MovingOrBlocked2Element(x, y);
7915 struct ElementInfo *ei = &element_info[element];
7916 struct ElementChangeInfo *change = &ei->change_page[page];
7919 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7922 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7923 x, y, element, element_info[element].token_name);
7924 printf("ChangeElement(): This should never happen!\n");
7929 /* this can happen with classic bombs on walkable, changing elements */
7930 if (!CAN_CHANGE(element))
7933 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7934 ChangeDelay[x][y] = 0;
7940 if (ChangeDelay[x][y] == 0) /* initialize element change */
7942 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7943 RND(change->delay_random * change->delay_frames)) + 1;
7945 ResetGfxAnimation(x, y);
7946 ResetRandomAnimationValue(x, y);
7948 if (change->pre_change_function)
7949 change->pre_change_function(x, y);
7952 ChangeDelay[x][y]--;
7954 if (ChangeDelay[x][y] != 0) /* continue element change */
7956 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7958 if (IS_ANIMATED(graphic))
7959 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7961 if (change->change_function)
7962 change->change_function(x, y);
7964 else /* finish element change */
7966 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7968 page = ChangePage[x][y];
7969 ChangePage[x][y] = -1;
7971 change = &ei->change_page[page];
7975 if (IS_MOVING(x, y) && !change->explode)
7977 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7980 ChangeDelay[x][y] = 1; /* try change after next move step */
7981 ChangePage[x][y] = page; /* remember page to use for change */
7986 if (ChangeElementNow(x, y, element, page))
7988 if (change->post_change_function)
7989 change->post_change_function(x, y);
7994 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7995 int trigger_element,
8002 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8004 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8007 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8009 int element = EL_CUSTOM_START + i;
8011 boolean change_element = FALSE;
8014 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8017 for (j = 0; j < element_info[element].num_change_pages; j++)
8019 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8021 if (change->can_change &&
8022 change->events & CH_EVENT_BIT(trigger_event) &&
8023 change->trigger_side & trigger_side &&
8024 change->trigger_player & trigger_player &&
8025 change->trigger_page & trigger_page_bits &&
8026 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8029 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8030 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8031 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8034 change_element = TRUE;
8037 change->actual_trigger_element = trigger_element;
8038 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8044 if (!change_element)
8047 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8050 if (x == lx && y == ly) /* do not change trigger element itself */
8054 if (Feld[x][y] == element)
8056 ChangeDelay[x][y] = 1;
8057 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8058 ChangeElement(x, y, page);
8066 static boolean CheckElementChangeExt(int x, int y,
8068 int trigger_element,
8074 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8077 if (Feld[x][y] == EL_BLOCKED)
8079 Blocked2Moving(x, y, &x, &y);
8080 element = Feld[x][y];
8084 if (Feld[x][y] != element) /* check if element has already changed */
8087 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8088 Feld[x][y], element_info[Feld[x][y]].token_name,
8089 element, element_info[element].token_name,
8098 if (trigger_page < 0)
8100 boolean change_element = FALSE;
8103 for (i = 0; i < element_info[element].num_change_pages; i++)
8105 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8107 if (change->can_change &&
8108 change->events & CH_EVENT_BIT(trigger_event) &&
8109 change->trigger_side & trigger_side &&
8110 change->trigger_player & trigger_player)
8112 change_element = TRUE;
8115 change->actual_trigger_element = trigger_element;
8116 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8122 if (!change_element)
8127 struct ElementInfo *ei = &element_info[element];
8128 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8130 change->actual_trigger_element = trigger_element;
8131 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8136 /* !!! this check misses pages with same event, but different side !!! */
8138 if (trigger_page < 0)
8139 trigger_page = element_info[element].event_page_nr[trigger_event];
8141 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8145 ChangeDelay[x][y] = 1;
8146 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8147 ChangeElement(x, y, trigger_page);
8152 static void PlayPlayerSound(struct PlayerInfo *player)
8154 int jx = player->jx, jy = player->jy;
8155 int element = player->element_nr;
8156 int last_action = player->last_action_waiting;
8157 int action = player->action_waiting;
8159 if (player->is_waiting)
8161 if (action != last_action)
8162 PlayLevelSoundElementAction(jx, jy, element, action);
8164 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8168 if (action != last_action)
8169 StopSound(element_info[element].sound[last_action]);
8171 if (last_action == ACTION_SLEEPING)
8172 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8176 static void PlayAllPlayersSound()
8180 for (i = 0; i < MAX_PLAYERS; i++)
8181 if (stored_player[i].active)
8182 PlayPlayerSound(&stored_player[i]);
8185 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8187 boolean last_waiting = player->is_waiting;
8188 int move_dir = player->MovDir;
8190 player->last_action_waiting = player->action_waiting;
8194 if (!last_waiting) /* not waiting -> waiting */
8196 player->is_waiting = TRUE;
8198 player->frame_counter_bored =
8200 game.player_boring_delay_fixed +
8201 SimpleRND(game.player_boring_delay_random);
8202 player->frame_counter_sleeping =
8204 game.player_sleeping_delay_fixed +
8205 SimpleRND(game.player_sleeping_delay_random);
8207 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8210 if (game.player_sleeping_delay_fixed +
8211 game.player_sleeping_delay_random > 0 &&
8212 player->anim_delay_counter == 0 &&
8213 player->post_delay_counter == 0 &&
8214 FrameCounter >= player->frame_counter_sleeping)
8215 player->is_sleeping = TRUE;
8216 else if (game.player_boring_delay_fixed +
8217 game.player_boring_delay_random > 0 &&
8218 FrameCounter >= player->frame_counter_bored)
8219 player->is_bored = TRUE;
8221 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8222 player->is_bored ? ACTION_BORING :
8225 if (player->is_sleeping)
8227 if (player->num_special_action_sleeping > 0)
8229 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8231 int last_special_action = player->special_action_sleeping;
8232 int num_special_action = player->num_special_action_sleeping;
8233 int special_action =
8234 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8235 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8236 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8237 last_special_action + 1 : ACTION_SLEEPING);
8238 int special_graphic =
8239 el_act_dir2img(player->element_nr, special_action, move_dir);
8241 player->anim_delay_counter =
8242 graphic_info[special_graphic].anim_delay_fixed +
8243 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8244 player->post_delay_counter =
8245 graphic_info[special_graphic].post_delay_fixed +
8246 SimpleRND(graphic_info[special_graphic].post_delay_random);
8248 player->special_action_sleeping = special_action;
8251 if (player->anim_delay_counter > 0)
8253 player->action_waiting = player->special_action_sleeping;
8254 player->anim_delay_counter--;
8256 else if (player->post_delay_counter > 0)
8258 player->post_delay_counter--;
8262 else if (player->is_bored)
8264 if (player->num_special_action_bored > 0)
8266 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8268 int special_action =
8269 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8270 int special_graphic =
8271 el_act_dir2img(player->element_nr, special_action, move_dir);
8273 player->anim_delay_counter =
8274 graphic_info[special_graphic].anim_delay_fixed +
8275 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8276 player->post_delay_counter =
8277 graphic_info[special_graphic].post_delay_fixed +
8278 SimpleRND(graphic_info[special_graphic].post_delay_random);
8280 player->special_action_bored = special_action;
8283 if (player->anim_delay_counter > 0)
8285 player->action_waiting = player->special_action_bored;
8286 player->anim_delay_counter--;
8288 else if (player->post_delay_counter > 0)
8290 player->post_delay_counter--;
8295 else if (last_waiting) /* waiting -> not waiting */
8297 player->is_waiting = FALSE;
8298 player->is_bored = FALSE;
8299 player->is_sleeping = FALSE;
8301 player->frame_counter_bored = -1;
8302 player->frame_counter_sleeping = -1;
8304 player->anim_delay_counter = 0;
8305 player->post_delay_counter = 0;
8307 player->action_waiting = ACTION_DEFAULT;
8309 player->special_action_bored = ACTION_DEFAULT;
8310 player->special_action_sleeping = ACTION_DEFAULT;
8315 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8318 static byte stored_player_action[MAX_PLAYERS];
8319 static int num_stored_actions = 0;
8321 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8322 int left = player_action & JOY_LEFT;
8323 int right = player_action & JOY_RIGHT;
8324 int up = player_action & JOY_UP;
8325 int down = player_action & JOY_DOWN;
8326 int button1 = player_action & JOY_BUTTON_1;
8327 int button2 = player_action & JOY_BUTTON_2;
8328 int dx = (left ? -1 : right ? 1 : 0);
8329 int dy = (up ? -1 : down ? 1 : 0);
8332 stored_player_action[player->index_nr] = 0;
8333 num_stored_actions++;
8337 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8340 if (!player->active || tape.pausing)
8344 printf("::: [%d %d %d %d] [%d %d]\n",
8345 left, right, up, down, button1, button2);
8351 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8356 if (player->MovPos == 0)
8357 CheckGravityMovement(player);
8360 snapped = SnapField(player, dx, dy);
8364 dropped = DropElement(player);
8366 moved = MovePlayer(player, dx, dy);
8369 if (tape.single_step && tape.recording && !tape.pausing)
8371 if (button1 || (dropped && !moved))
8373 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8374 SnapField(player, 0, 0); /* stop snapping */
8378 SetPlayerWaiting(player, FALSE);
8381 return player_action;
8383 stored_player_action[player->index_nr] = player_action;
8389 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8392 /* no actions for this player (no input at player's configured device) */
8394 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8395 SnapField(player, 0, 0);
8396 CheckGravityMovementWhenNotMoving(player);
8398 if (player->MovPos == 0)
8399 SetPlayerWaiting(player, TRUE);
8401 if (player->MovPos == 0) /* needed for tape.playing */
8402 player->is_moving = FALSE;
8404 player->is_dropping = FALSE;
8410 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8412 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8414 TapeRecordAction(stored_player_action);
8415 num_stored_actions = 0;
8422 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8424 static byte stored_player_action[MAX_PLAYERS];
8425 static int num_stored_actions = 0;
8426 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8427 int left = player_action & JOY_LEFT;
8428 int right = player_action & JOY_RIGHT;
8429 int up = player_action & JOY_UP;
8430 int down = player_action & JOY_DOWN;
8431 int button1 = player_action & JOY_BUTTON_1;
8432 int button2 = player_action & JOY_BUTTON_2;
8433 int dx = (left ? -1 : right ? 1 : 0);
8434 int dy = (up ? -1 : down ? 1 : 0);
8436 stored_player_action[player->index_nr] = 0;
8437 num_stored_actions++;
8439 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8441 if (!player->active || tape.pausing)
8446 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8449 snapped = SnapField(player, dx, dy);
8453 dropped = DropElement(player);
8455 moved = MovePlayer(player, dx, dy);
8458 if (tape.single_step && tape.recording && !tape.pausing)
8460 if (button1 || (dropped && !moved))
8462 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8463 SnapField(player, 0, 0); /* stop snapping */
8467 stored_player_action[player->index_nr] = player_action;
8471 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8473 /* no actions for this player (no input at player's configured device) */
8475 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8476 SnapField(player, 0, 0);
8477 CheckGravityMovementWhenNotMoving(player);
8479 if (player->MovPos == 0)
8480 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8482 if (player->MovPos == 0) /* needed for tape.playing */
8483 player->is_moving = FALSE;
8486 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8488 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8490 TapeRecordAction(stored_player_action);
8491 num_stored_actions = 0;
8496 void AdvanceFrameAndPlayerCounters(int player_nr)
8500 /* advance frame counters (global frame counter and time frame counter) */
8504 /* advance player counters (counters for move delay, move animation etc.) */
8505 for (i = 0; i < MAX_PLAYERS; i++)
8507 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8509 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8511 if (!advance_player_counters) /* not all players may be affected */
8514 stored_player[i].Frame += move_frames;
8516 if (stored_player[i].MovPos != 0)
8517 stored_player[i].StepFrame += move_frames;
8519 #if USE_NEW_MOVE_DELAY
8520 if (stored_player[i].move_delay > 0)
8521 stored_player[i].move_delay--;
8524 #if USE_NEW_PUSH_DELAY
8525 /* due to bugs in previous versions, counter must count up, not down */
8526 if (stored_player[i].push_delay != -1)
8527 stored_player[i].push_delay++;
8530 if (stored_player[i].drop_delay > 0)
8531 stored_player[i].drop_delay--;
8537 static unsigned long action_delay = 0;
8538 unsigned long action_delay_value;
8539 int magic_wall_x = 0, magic_wall_y = 0;
8540 int i, x, y, element, graphic;
8541 byte *recorded_player_action;
8542 byte summarized_player_action = 0;
8544 byte tape_action[MAX_PLAYERS];
8547 if (game_status != GAME_MODE_PLAYING)
8550 action_delay_value =
8551 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8553 if (tape.playing && tape.warp_forward && !tape.pausing)
8554 action_delay_value = 0;
8556 /* ---------- main game synchronization point ---------- */
8558 WaitUntilDelayReached(&action_delay, action_delay_value);
8560 if (network_playing && !network_player_action_received)
8564 printf("DEBUG: try to get network player actions in time\n");
8568 #if defined(NETWORK_AVALIABLE)
8569 /* last chance to get network player actions without main loop delay */
8573 if (game_status != GAME_MODE_PLAYING)
8576 if (!network_player_action_received)
8580 printf("DEBUG: failed to get network player actions in time\n");
8591 printf("::: getting new tape action [%d]\n", FrameCounter);
8594 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8597 if (recorded_player_action == NULL && tape.pausing)
8602 printf("::: %d\n", stored_player[0].action);
8606 if (recorded_player_action != NULL)
8607 for (i = 0; i < MAX_PLAYERS; i++)
8608 stored_player[i].action = recorded_player_action[i];
8611 for (i = 0; i < MAX_PLAYERS; i++)
8613 summarized_player_action |= stored_player[i].action;
8615 if (!network_playing)
8616 stored_player[i].effective_action = stored_player[i].action;
8619 #if defined(NETWORK_AVALIABLE)
8620 if (network_playing)
8621 SendToServer_MovePlayer(summarized_player_action);
8624 if (!options.network && !setup.team_mode)
8625 local_player->effective_action = summarized_player_action;
8628 if (recorded_player_action != NULL)
8629 for (i = 0; i < MAX_PLAYERS; i++)
8630 stored_player[i].effective_action = recorded_player_action[i];
8634 for (i = 0; i < MAX_PLAYERS; i++)
8636 tape_action[i] = stored_player[i].effective_action;
8638 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8639 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8642 /* only save actions from input devices, but not programmed actions */
8644 TapeRecordAction(tape_action);
8647 for (i = 0; i < MAX_PLAYERS; i++)
8649 int actual_player_action = stored_player[i].effective_action;
8652 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8653 - rnd_equinox_tetrachloride 048
8654 - rnd_equinox_tetrachloride_ii 096
8655 - rnd_emanuel_schmieg 002
8656 - doctor_sloan_ww 001, 020
8658 if (stored_player[i].MovPos == 0)
8659 CheckGravityMovement(&stored_player[i]);
8663 /* overwrite programmed action with tape action */
8664 if (stored_player[i].programmed_action)
8665 actual_player_action = stored_player[i].programmed_action;
8669 if (stored_player[i].programmed_action)
8670 printf("::: %d\n", stored_player[i].programmed_action);
8673 if (recorded_player_action)
8676 if (stored_player[i].programmed_action &&
8677 stored_player[i].programmed_action != recorded_player_action[i])
8678 printf("::: %d: %d <-> %d\n", i,
8679 stored_player[i].programmed_action, recorded_player_action[i]);
8683 actual_player_action = recorded_player_action[i];
8688 /* overwrite tape action with programmed action */
8689 if (stored_player[i].programmed_action)
8690 actual_player_action = stored_player[i].programmed_action;
8695 printf("::: action: %d: %x [%d]\n",
8696 stored_player[i].MovPos, actual_player_action, FrameCounter);
8700 PlayerActions(&stored_player[i], actual_player_action);
8702 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8704 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8705 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8708 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8713 TapeRecordAction(tape_action);
8716 network_player_action_received = FALSE;
8718 ScrollScreen(NULL, SCROLL_GO_ON);
8724 for (i = 0; i < MAX_PLAYERS; i++)
8725 stored_player[i].Frame++;
8729 /* for backwards compatibility, the following code emulates a fixed bug that
8730 occured when pushing elements (causing elements that just made their last
8731 pushing step to already (if possible) make their first falling step in the
8732 same game frame, which is bad); this code is also needed to use the famous
8733 "spring push bug" which is used in older levels and might be wanted to be
8734 used also in newer levels, but in this case the buggy pushing code is only
8735 affecting the "spring" element and no other elements */
8738 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8740 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8743 for (i = 0; i < MAX_PLAYERS; i++)
8745 struct PlayerInfo *player = &stored_player[i];
8750 if (player->active && player->is_pushing && player->is_moving &&
8752 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8753 Feld[x][y] == EL_SPRING))
8755 if (player->active && player->is_pushing && player->is_moving &&
8759 ContinueMoving(x, y);
8761 /* continue moving after pushing (this is actually a bug) */
8762 if (!IS_MOVING(x, y))
8771 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8773 Changed[x][y] = CE_BITMASK_DEFAULT;
8774 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8776 #if USE_NEW_BLOCK_STYLE
8777 /* this must be handled before main playfield loop */
8778 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8781 if (MovDelay[x][y] <= 0)
8787 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8789 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8790 printf("GameActions(): This should never happen!\n");
8792 ChangePage[x][y] = -1;
8797 if (WasJustMoving[x][y] > 0)
8798 WasJustMoving[x][y]--;
8799 if (WasJustFalling[x][y] > 0)
8800 WasJustFalling[x][y]--;
8801 if (CheckCollision[x][y] > 0)
8802 CheckCollision[x][y]--;
8807 /* reset finished pushing action (not done in ContinueMoving() to allow
8808 continous pushing animation for elements with zero push delay) */
8809 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8811 ResetGfxAnimation(x, y);
8812 DrawLevelField(x, y);
8817 if (IS_BLOCKED(x, y))
8821 Blocked2Moving(x, y, &oldx, &oldy);
8822 if (!IS_MOVING(oldx, oldy))
8824 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8825 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8826 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8827 printf("GameActions(): This should never happen!\n");
8833 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8835 element = Feld[x][y];
8837 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8839 graphic = el2img(element);
8845 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8847 element = graphic = 0;
8851 if (graphic_info[graphic].anim_global_sync)
8852 GfxFrame[x][y] = FrameCounter;
8854 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8855 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8856 ResetRandomAnimationValue(x, y);
8858 SetRandomAnimationValue(x, y);
8861 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8864 if (IS_INACTIVE(element))
8866 if (IS_ANIMATED(graphic))
8867 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8873 /* this may take place after moving, so 'element' may have changed */
8875 if (IS_CHANGING(x, y))
8877 if (IS_CHANGING(x, y) &&
8878 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8882 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8883 element_info[element].event_page_nr[CE_DELAY]);
8885 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8888 element = Feld[x][y];
8889 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8893 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8898 element = Feld[x][y];
8899 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8901 if (element == EL_MOLE)
8902 printf("::: %d, %d, %d [%d]\n",
8903 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8907 if (element == EL_YAMYAM)
8908 printf("::: %d, %d, %d\n",
8909 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8913 if (IS_ANIMATED(graphic) &&
8917 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8920 if (element == EL_BUG)
8921 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8925 if (element == EL_MOLE)
8926 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8930 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8931 EdelsteinFunkeln(x, y);
8933 else if ((element == EL_ACID ||
8934 element == EL_EXIT_OPEN ||
8935 element == EL_SP_EXIT_OPEN ||
8936 element == EL_SP_TERMINAL ||
8937 element == EL_SP_TERMINAL_ACTIVE ||
8938 element == EL_EXTRA_TIME ||
8939 element == EL_SHIELD_NORMAL ||
8940 element == EL_SHIELD_DEADLY) &&
8941 IS_ANIMATED(graphic))
8942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8943 else if (IS_MOVING(x, y))
8944 ContinueMoving(x, y);
8945 else if (IS_ACTIVE_BOMB(element))
8946 CheckDynamite(x, y);
8948 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8949 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8951 else if (element == EL_AMOEBA_GROWING)
8952 AmoebeWaechst(x, y);
8953 else if (element == EL_AMOEBA_SHRINKING)
8954 AmoebaDisappearing(x, y);
8956 #if !USE_NEW_AMOEBA_CODE
8957 else if (IS_AMOEBALIVE(element))
8958 AmoebeAbleger(x, y);
8961 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8963 else if (element == EL_EXIT_CLOSED)
8965 else if (element == EL_SP_EXIT_CLOSED)
8967 else if (element == EL_EXPANDABLE_WALL_GROWING)
8969 else if (element == EL_EXPANDABLE_WALL ||
8970 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8971 element == EL_EXPANDABLE_WALL_VERTICAL ||
8972 element == EL_EXPANDABLE_WALL_ANY)
8974 else if (element == EL_FLAMES)
8975 CheckForDragon(x, y);
8977 else if (IS_AUTO_CHANGING(element))
8978 ChangeElement(x, y);
8980 else if (element == EL_EXPLOSION)
8981 ; /* drawing of correct explosion animation is handled separately */
8982 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8983 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8986 /* this may take place after moving, so 'element' may have changed */
8987 if (IS_AUTO_CHANGING(Feld[x][y]))
8988 ChangeElement(x, y);
8991 if (IS_BELT_ACTIVE(element))
8992 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8994 if (game.magic_wall_active)
8996 int jx = local_player->jx, jy = local_player->jy;
8998 /* play the element sound at the position nearest to the player */
8999 if ((element == EL_MAGIC_WALL_FULL ||
9000 element == EL_MAGIC_WALL_ACTIVE ||
9001 element == EL_MAGIC_WALL_EMPTYING ||
9002 element == EL_BD_MAGIC_WALL_FULL ||
9003 element == EL_BD_MAGIC_WALL_ACTIVE ||
9004 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9005 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9013 #if USE_NEW_AMOEBA_CODE
9014 /* new experimental amoeba growth stuff */
9016 if (!(FrameCounter % 8))
9019 static unsigned long random = 1684108901;
9021 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9024 x = (random >> 10) % lev_fieldx;
9025 y = (random >> 20) % lev_fieldy;
9027 x = RND(lev_fieldx);
9028 y = RND(lev_fieldy);
9030 element = Feld[x][y];
9033 if (!IS_PLAYER(x,y) &&
9034 (element == EL_EMPTY ||
9035 CAN_GROW_INTO(element) ||
9036 element == EL_QUICKSAND_EMPTY ||
9037 element == EL_ACID_SPLASH_LEFT ||
9038 element == EL_ACID_SPLASH_RIGHT))
9040 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9041 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9042 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9043 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9044 Feld[x][y] = EL_AMOEBA_DROP;
9047 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9048 if (!IS_PLAYER(x,y) &&
9049 (element == EL_EMPTY ||
9050 element == EL_SAND ||
9051 element == EL_QUICKSAND_EMPTY ||
9052 element == EL_ACID_SPLASH_LEFT ||
9053 element == EL_ACID_SPLASH_RIGHT))
9055 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9056 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9057 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9058 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9059 Feld[x][y] = EL_AMOEBA_DROP;
9063 random = random * 129 + 1;
9069 if (game.explosions_delayed)
9072 game.explosions_delayed = FALSE;
9074 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9076 element = Feld[x][y];
9078 if (ExplodeField[x][y])
9079 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9080 else if (element == EL_EXPLOSION)
9081 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9083 ExplodeField[x][y] = EX_TYPE_NONE;
9086 game.explosions_delayed = TRUE;
9089 if (game.magic_wall_active)
9091 if (!(game.magic_wall_time_left % 4))
9093 int element = Feld[magic_wall_x][magic_wall_y];
9095 if (element == EL_BD_MAGIC_WALL_FULL ||
9096 element == EL_BD_MAGIC_WALL_ACTIVE ||
9097 element == EL_BD_MAGIC_WALL_EMPTYING)
9098 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9100 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9103 if (game.magic_wall_time_left > 0)
9105 game.magic_wall_time_left--;
9106 if (!game.magic_wall_time_left)
9108 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9110 element = Feld[x][y];
9112 if (element == EL_MAGIC_WALL_ACTIVE ||
9113 element == EL_MAGIC_WALL_FULL)
9115 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9116 DrawLevelField(x, y);
9118 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9119 element == EL_BD_MAGIC_WALL_FULL)
9121 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9122 DrawLevelField(x, y);
9126 game.magic_wall_active = FALSE;
9131 if (game.light_time_left > 0)
9133 game.light_time_left--;
9135 if (game.light_time_left == 0)
9136 RedrawAllLightSwitchesAndInvisibleElements();
9139 if (game.timegate_time_left > 0)
9141 game.timegate_time_left--;
9143 if (game.timegate_time_left == 0)
9144 CloseAllOpenTimegates();
9147 for (i = 0; i < MAX_PLAYERS; i++)
9149 struct PlayerInfo *player = &stored_player[i];
9151 if (SHIELD_ON(player))
9153 if (player->shield_deadly_time_left)
9154 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9155 else if (player->shield_normal_time_left)
9156 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9160 if (TimeFrames >= FRAMES_PER_SECOND)
9165 for (i = 0; i < MAX_PLAYERS; i++)
9167 struct PlayerInfo *player = &stored_player[i];
9169 if (SHIELD_ON(player))
9171 player->shield_normal_time_left--;
9173 if (player->shield_deadly_time_left > 0)
9174 player->shield_deadly_time_left--;
9178 if (!level.use_step_counter)
9186 if (TimeLeft <= 10 && setup.time_limit)
9187 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9189 DrawGameValue_Time(TimeLeft);
9191 if (!TimeLeft && setup.time_limit)
9192 for (i = 0; i < MAX_PLAYERS; i++)
9193 KillHero(&stored_player[i]);
9195 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9196 DrawGameValue_Time(TimePlayed);
9199 if (tape.recording || tape.playing)
9200 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9204 PlayAllPlayersSound();
9206 if (options.debug) /* calculate frames per second */
9208 static unsigned long fps_counter = 0;
9209 static int fps_frames = 0;
9210 unsigned long fps_delay_ms = Counter() - fps_counter;
9214 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9216 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9219 fps_counter = Counter();
9222 redraw_mask |= REDRAW_FPS;
9226 if (stored_player[0].jx != stored_player[0].last_jx ||
9227 stored_player[0].jy != stored_player[0].last_jy)
9228 printf("::: %d, %d, %d, %d, %d\n",
9229 stored_player[0].MovDir,
9230 stored_player[0].MovPos,
9231 stored_player[0].GfxPos,
9232 stored_player[0].Frame,
9233 stored_player[0].StepFrame);
9236 #if USE_NEW_MOVE_DELAY
9237 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9242 for (i = 0; i < MAX_PLAYERS; i++)
9245 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9247 stored_player[i].Frame += move_frames;
9249 if (stored_player[i].MovPos != 0)
9250 stored_player[i].StepFrame += move_frames;
9252 #if USE_NEW_MOVE_DELAY
9253 if (stored_player[i].move_delay > 0)
9254 stored_player[i].move_delay--;
9257 if (stored_player[i].drop_delay > 0)
9258 stored_player[i].drop_delay--;
9263 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9265 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9267 local_player->show_envelope = 0;
9271 #if USE_NEW_RANDOMIZE
9272 /* use random number generator in every frame to make it less predictable */
9273 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9278 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9280 int min_x = x, min_y = y, max_x = x, max_y = y;
9283 for (i = 0; i < MAX_PLAYERS; i++)
9285 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9287 if (!stored_player[i].active || &stored_player[i] == player)
9290 min_x = MIN(min_x, jx);
9291 min_y = MIN(min_y, jy);
9292 max_x = MAX(max_x, jx);
9293 max_y = MAX(max_y, jy);
9296 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9299 static boolean AllPlayersInVisibleScreen()
9303 for (i = 0; i < MAX_PLAYERS; i++)
9305 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9307 if (!stored_player[i].active)
9310 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9317 void ScrollLevel(int dx, int dy)
9319 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9322 BlitBitmap(drawto_field, drawto_field,
9323 FX + TILEX * (dx == -1) - softscroll_offset,
9324 FY + TILEY * (dy == -1) - softscroll_offset,
9325 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9326 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9327 FX + TILEX * (dx == 1) - softscroll_offset,
9328 FY + TILEY * (dy == 1) - softscroll_offset);
9332 x = (dx == 1 ? BX1 : BX2);
9333 for (y = BY1; y <= BY2; y++)
9334 DrawScreenField(x, y);
9339 y = (dy == 1 ? BY1 : BY2);
9340 for (x = BX1; x <= BX2; x++)
9341 DrawScreenField(x, y);
9344 redraw_mask |= REDRAW_FIELD;
9348 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9350 int nextx = x + dx, nexty = y + dy;
9351 int element = Feld[x][y];
9354 element != EL_SP_PORT_LEFT &&
9355 element != EL_SP_GRAVITY_PORT_LEFT &&
9356 element != EL_SP_PORT_HORIZONTAL &&
9357 element != EL_SP_PORT_ANY) ||
9359 element != EL_SP_PORT_RIGHT &&
9360 element != EL_SP_GRAVITY_PORT_RIGHT &&
9361 element != EL_SP_PORT_HORIZONTAL &&
9362 element != EL_SP_PORT_ANY) ||
9364 element != EL_SP_PORT_UP &&
9365 element != EL_SP_GRAVITY_PORT_UP &&
9366 element != EL_SP_PORT_VERTICAL &&
9367 element != EL_SP_PORT_ANY) ||
9369 element != EL_SP_PORT_DOWN &&
9370 element != EL_SP_GRAVITY_PORT_DOWN &&
9371 element != EL_SP_PORT_VERTICAL &&
9372 element != EL_SP_PORT_ANY) ||
9373 !IN_LEV_FIELD(nextx, nexty) ||
9374 !IS_FREE(nextx, nexty))
9381 static boolean canFallDown(struct PlayerInfo *player)
9383 int jx = player->jx, jy = player->jy;
9385 return (IN_LEV_FIELD(jx, jy + 1) &&
9386 (IS_FREE(jx, jy + 1) ||
9387 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9388 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9389 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9392 static boolean canPassField(int x, int y, int move_dir)
9394 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9395 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9396 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9399 int element = Feld[x][y];
9401 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9402 !CAN_MOVE(element) &&
9403 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9404 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9405 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9408 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9410 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9411 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9412 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9416 int nextx = newx + dx;
9417 int nexty = newy + dy;
9421 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9422 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9424 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9426 (IS_DIGGABLE(Feld[newx][newy]) ||
9427 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9428 canPassField(newx, newy, move_dir)));
9431 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9432 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9433 (IS_DIGGABLE(Feld[newx][newy]) ||
9434 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9435 canPassField(newx, newy, move_dir)));
9438 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9439 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9440 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9441 canPassField(newx, newy, move_dir)));
9443 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9444 (IS_DIGGABLE(Feld[newx][newy]) ||
9445 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9446 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9447 !CAN_MOVE(Feld[newx][newy]) &&
9448 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9449 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9450 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9456 static void CheckGravityMovement(struct PlayerInfo *player)
9458 if (game.gravity && !player->programmed_action)
9461 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9462 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9464 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9465 int move_dir_vertical = player->action & MV_VERTICAL;
9469 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9471 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9474 int jx = player->jx, jy = player->jy;
9476 boolean player_is_moving_to_valid_field =
9477 (!player_is_snapping &&
9478 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9479 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9483 (player->last_move_dir & MV_HORIZONTAL ?
9484 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9485 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9489 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9490 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9491 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9492 int new_jx = jx + dx, new_jy = jy + dy;
9493 int nextx = new_jx + dx, nexty = new_jy + dy;
9499 boolean player_can_fall_down = canFallDown(player);
9501 boolean player_can_fall_down =
9502 (IN_LEV_FIELD(jx, jy + 1) &&
9503 (IS_FREE(jx, jy + 1) ||
9504 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9508 boolean player_can_fall_down =
9509 (IN_LEV_FIELD(jx, jy + 1) &&
9510 (IS_FREE(jx, jy + 1)));
9514 boolean player_is_moving_to_valid_field =
9517 !player_is_snapping &&
9521 IN_LEV_FIELD(new_jx, new_jy) &&
9522 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9523 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9524 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9525 IN_LEV_FIELD(nextx, nexty) &&
9526 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9528 IN_LEV_FIELD(new_jx, new_jy) &&
9529 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9530 Feld[new_jx][new_jy] == EL_SAND ||
9531 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9532 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9533 /* !!! extend EL_SAND to anything diggable !!! */
9539 boolean player_is_standing_on_valid_field =
9540 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9541 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9545 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9546 player_can_fall_down,
9547 player_is_standing_on_valid_field,
9548 player_is_moving_to_valid_field,
9549 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9550 player->effective_action,
9551 player->can_fall_into_acid);
9554 if (player_can_fall_down &&
9556 !player_is_standing_on_valid_field &&
9558 !player_is_moving_to_valid_field)
9561 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9562 jx, jy, FrameCounter);
9565 player->programmed_action = MV_DOWN;
9570 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9573 return CheckGravityMovement(player);
9576 if (game.gravity && !player->programmed_action)
9578 int jx = player->jx, jy = player->jy;
9579 boolean field_under_player_is_free =
9580 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9581 boolean player_is_standing_on_valid_field =
9582 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9583 (IS_WALKABLE(Feld[jx][jy]) &&
9584 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9586 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9587 player->programmed_action = MV_DOWN;
9593 -----------------------------------------------------------------------------
9594 dx, dy: direction (non-diagonal) to try to move the player to
9595 real_dx, real_dy: direction as read from input device (can be diagonal)
9598 boolean MovePlayerOneStep(struct PlayerInfo *player,
9599 int dx, int dy, int real_dx, int real_dy)
9602 static int trigger_sides[4][2] =
9604 /* enter side leave side */
9605 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9606 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9607 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9608 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9610 int move_direction = (dx == -1 ? MV_LEFT :
9611 dx == +1 ? MV_RIGHT :
9613 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9614 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9615 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9617 int jx = player->jx, jy = player->jy;
9618 int new_jx = jx + dx, new_jy = jy + dy;
9622 if (!player->active || (!dx && !dy))
9623 return MF_NO_ACTION;
9625 player->MovDir = (dx < 0 ? MV_LEFT :
9628 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9630 if (!IN_LEV_FIELD(new_jx, new_jy))
9631 return MF_NO_ACTION;
9633 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9634 return MF_NO_ACTION;
9637 element = MovingOrBlocked2Element(new_jx, new_jy);
9639 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9642 if (DONT_RUN_INTO(element))
9644 if (element == EL_ACID && dx == 0 && dy == 1)
9646 SplashAcid(new_jx, new_jy);
9647 Feld[jx][jy] = EL_PLAYER_1;
9648 InitMovingField(jx, jy, MV_DOWN);
9649 Store[jx][jy] = EL_ACID;
9650 ContinueMoving(jx, jy);
9654 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9659 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9660 if (can_move != MF_MOVING)
9663 /* check if DigField() has caused relocation of the player */
9664 if (player->jx != jx || player->jy != jy)
9665 return MF_NO_ACTION;
9667 StorePlayer[jx][jy] = 0;
9668 player->last_jx = jx;
9669 player->last_jy = jy;
9670 player->jx = new_jx;
9671 player->jy = new_jy;
9672 StorePlayer[new_jx][new_jy] = player->element_nr;
9675 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9677 player->step_counter++;
9680 player->drop_delay = 0;
9683 PlayerVisit[jx][jy] = FrameCounter;
9685 ScrollPlayer(player, SCROLL_INIT);
9688 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9690 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9692 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9695 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9697 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9698 CE_OTHER_GETS_ENTERED, enter_side);
9699 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9700 CE_ENTERED_BY_PLAYER, enter_side);
9707 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9709 int jx = player->jx, jy = player->jy;
9710 int old_jx = jx, old_jy = jy;
9711 int moved = MF_NO_ACTION;
9714 if (!player->active)
9719 if (player->MovPos == 0)
9721 player->is_moving = FALSE;
9722 player->is_digging = FALSE;
9723 player->is_collecting = FALSE;
9724 player->is_snapping = FALSE;
9725 player->is_pushing = FALSE;
9731 if (!player->active || (!dx && !dy))
9736 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9744 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9745 player->move_delay + player->move_delay_value);
9748 #if USE_NEW_MOVE_DELAY
9749 if (player->move_delay > 0)
9751 if (!FrameReached(&player->move_delay, player->move_delay_value))
9755 printf("::: can NOT move\n");
9761 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9762 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9769 printf("::: COULD move now\n");
9772 #if USE_NEW_MOVE_DELAY
9773 player->move_delay = -1; /* set to "uninitialized" value */
9776 /* store if player is automatically moved to next field */
9777 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9779 /* remove the last programmed player action */
9780 player->programmed_action = 0;
9784 /* should only happen if pre-1.2 tape recordings are played */
9785 /* this is only for backward compatibility */
9787 int original_move_delay_value = player->move_delay_value;
9790 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9794 /* scroll remaining steps with finest movement resolution */
9795 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9797 while (player->MovPos)
9799 ScrollPlayer(player, SCROLL_GO_ON);
9800 ScrollScreen(NULL, SCROLL_GO_ON);
9802 #if USE_NEW_MOVE_DELAY
9803 AdvanceFrameAndPlayerCounters(player->index_nr);
9812 player->move_delay_value = original_move_delay_value;
9815 if (player->last_move_dir & MV_HORIZONTAL)
9817 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9818 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9822 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9823 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9829 if (moved & MF_MOVING && !ScreenMovPos &&
9830 (player == local_player || !options.network))
9832 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9833 int offset = (setup.scroll_delay ? 3 : 0);
9835 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9837 /* actual player has left the screen -- scroll in that direction */
9838 if (jx != old_jx) /* player has moved horizontally */
9839 scroll_x += (jx - old_jx);
9840 else /* player has moved vertically */
9841 scroll_y += (jy - old_jy);
9845 if (jx != old_jx) /* player has moved horizontally */
9847 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9848 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9849 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9851 /* don't scroll over playfield boundaries */
9852 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9853 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9855 /* don't scroll more than one field at a time */
9856 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9858 /* don't scroll against the player's moving direction */
9859 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9860 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9861 scroll_x = old_scroll_x;
9863 else /* player has moved vertically */
9865 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9866 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9867 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9869 /* don't scroll over playfield boundaries */
9870 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9871 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9873 /* don't scroll more than one field at a time */
9874 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9876 /* don't scroll against the player's moving direction */
9877 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9878 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9879 scroll_y = old_scroll_y;
9883 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9885 if (!options.network && !AllPlayersInVisibleScreen())
9887 scroll_x = old_scroll_x;
9888 scroll_y = old_scroll_y;
9892 ScrollScreen(player, SCROLL_INIT);
9893 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9900 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9902 if (!(moved & MF_MOVING) && !player->is_pushing)
9907 player->StepFrame = 0;
9909 if (moved & MF_MOVING)
9912 printf("::: REALLY moves now\n");
9915 if (old_jx != jx && old_jy == jy)
9916 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9917 else if (old_jx == jx && old_jy != jy)
9918 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9920 DrawLevelField(jx, jy); /* for "crumbled sand" */
9922 player->last_move_dir = player->MovDir;
9923 player->is_moving = TRUE;
9925 player->is_snapping = FALSE;
9929 player->is_switching = FALSE;
9932 player->is_dropping = FALSE;
9936 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9939 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9942 int move_direction = player->MovDir;
9944 int enter_side = MV_DIR_OPPOSITE(move_direction);
9945 int leave_side = move_direction;
9947 static int trigger_sides[4][2] =
9949 /* enter side leave side */
9950 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9951 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9952 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9953 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9955 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9956 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9958 int old_element = Feld[old_jx][old_jy];
9959 int new_element = Feld[jx][jy];
9962 /* !!! TEST ONLY !!! */
9963 if (IS_CUSTOM_ELEMENT(old_element))
9964 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9966 player->index_bit, leave_side);
9968 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9970 player->index_bit, leave_side);
9972 if (IS_CUSTOM_ELEMENT(new_element))
9973 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9974 player->index_bit, enter_side);
9976 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9977 CE_OTHER_GETS_ENTERED,
9978 player->index_bit, enter_side);
9988 CheckGravityMovementWhenNotMoving(player);
9991 player->last_move_dir = MV_NO_MOVING;
9993 player->is_moving = FALSE;
9995 #if USE_NEW_MOVE_STYLE
9996 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9997 /* ensure that the player is also allowed to move in the next frame */
9998 /* (currently, the player is forced to wait eight frames before he can try
10001 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10002 player->move_delay = 0; /* allow direct movement in the next frame */
10006 #if USE_NEW_MOVE_DELAY
10007 if (player->move_delay == -1) /* not yet initialized by DigField() */
10008 player->move_delay = player->move_delay_value;
10011 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10013 TestIfHeroTouchesBadThing(jx, jy);
10014 TestIfPlayerTouchesCustomElement(jx, jy);
10017 if (!player->active)
10018 RemoveHero(player);
10023 void ScrollPlayer(struct PlayerInfo *player, int mode)
10025 int jx = player->jx, jy = player->jy;
10026 int last_jx = player->last_jx, last_jy = player->last_jy;
10027 int move_stepsize = TILEX / player->move_delay_value;
10029 if (!player->active || !player->MovPos)
10032 if (mode == SCROLL_INIT)
10034 player->actual_frame_counter = FrameCounter;
10035 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10038 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10040 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10041 player->block_delay);
10044 #if USE_NEW_BLOCK_STYLE
10047 if (player->block_delay <= 0)
10048 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10051 if (player->block_delay > 0 &&
10052 Feld[last_jx][last_jy] == EL_EMPTY)
10054 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10055 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10058 #if USE_NEW_MOVE_STYLE
10059 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10060 player->block_last_field) &&
10061 Feld[last_jx][last_jy] == EL_EMPTY)
10062 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10064 if (Feld[last_jx][last_jy] == EL_EMPTY)
10065 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10070 DrawPlayer(player);
10075 else if (!FrameReached(&player->actual_frame_counter, 1))
10078 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10079 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10081 #if USE_NEW_BLOCK_STYLE
10083 if (!player->block_last_field &&
10084 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10086 RemoveField(last_jx, last_jy);
10088 Feld[last_jx][last_jy] = EL_EMPTY;
10092 /* before DrawPlayer() to draw correct player graphic for this case */
10093 if (player->MovPos == 0)
10094 CheckGravityMovement(player);
10097 DrawPlayer(player); /* needed here only to cleanup last field */
10100 if (player->MovPos == 0) /* player reached destination field */
10103 if (player->move_delay_reset_counter > 0)
10105 player->move_delay_reset_counter--;
10107 if (player->move_delay_reset_counter == 0)
10109 /* continue with normal speed after quickly moving through gate */
10110 HALVE_PLAYER_SPEED(player);
10112 /* be able to make the next move without delay */
10113 player->move_delay = 0;
10117 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10119 /* continue with normal speed after quickly moving through gate */
10120 HALVE_PLAYER_SPEED(player);
10122 /* be able to make the next move without delay */
10123 player->move_delay = 0;
10127 #if USE_NEW_BLOCK_STYLE
10129 if (player->block_last_field &&
10130 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10132 RemoveField(last_jx, last_jy);
10134 Feld[last_jx][last_jy] = EL_EMPTY;
10138 player->last_jx = jx;
10139 player->last_jy = jy;
10141 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10142 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10143 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10145 DrawPlayer(player); /* needed here only to cleanup last field */
10146 RemoveHero(player);
10148 if (local_player->friends_still_needed == 0 ||
10149 IS_SP_ELEMENT(Feld[jx][jy]))
10150 player->LevelSolved = player->GameOver = TRUE;
10154 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10155 /* this breaks one level: "machine", level 000 */
10157 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10160 int move_direction = player->MovDir;
10162 int enter_side = MV_DIR_OPPOSITE(move_direction);
10163 int leave_side = move_direction;
10165 static int trigger_sides[4][2] =
10167 /* enter side leave side */
10168 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10169 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10170 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10171 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10173 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10174 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10176 int old_jx = last_jx;
10177 int old_jy = last_jy;
10178 int old_element = Feld[old_jx][old_jy];
10179 int new_element = Feld[jx][jy];
10182 /* !!! TEST ONLY !!! */
10183 if (IS_CUSTOM_ELEMENT(old_element))
10184 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10186 player->index_bit, leave_side);
10188 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10189 CE_OTHER_GETS_LEFT,
10190 player->index_bit, leave_side);
10192 if (IS_CUSTOM_ELEMENT(new_element))
10193 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10194 player->index_bit, enter_side);
10196 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10197 CE_OTHER_GETS_ENTERED,
10198 player->index_bit, enter_side);
10204 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10206 TestIfHeroTouchesBadThing(jx, jy);
10207 TestIfPlayerTouchesCustomElement(jx, jy);
10210 /* needed because pushed element has not yet reached its destination,
10211 so it would trigger a change event at its previous field location */
10212 if (!player->is_pushing)
10214 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10217 if (!player->active)
10218 RemoveHero(player);
10221 if (level.use_step_counter)
10231 if (TimeLeft <= 10 && setup.time_limit)
10232 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10234 DrawGameValue_Time(TimeLeft);
10236 if (!TimeLeft && setup.time_limit)
10237 for (i = 0; i < MAX_PLAYERS; i++)
10238 KillHero(&stored_player[i]);
10240 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10241 DrawGameValue_Time(TimePlayed);
10244 if (tape.single_step && tape.recording && !tape.pausing &&
10245 !player->programmed_action)
10246 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10250 void ScrollScreen(struct PlayerInfo *player, int mode)
10252 static unsigned long screen_frame_counter = 0;
10254 if (mode == SCROLL_INIT)
10256 /* set scrolling step size according to actual player's moving speed */
10257 ScrollStepSize = TILEX / player->move_delay_value;
10259 screen_frame_counter = FrameCounter;
10260 ScreenMovDir = player->MovDir;
10261 ScreenMovPos = player->MovPos;
10262 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10265 else if (!FrameReached(&screen_frame_counter, 1))
10270 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10271 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10272 redraw_mask |= REDRAW_FIELD;
10275 ScreenMovDir = MV_NO_MOVING;
10278 void TestIfPlayerTouchesCustomElement(int x, int y)
10280 static int xy[4][2] =
10287 static int trigger_sides[4][2] =
10289 /* center side border side */
10290 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10291 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10292 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10293 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10295 static int touch_dir[4] =
10297 MV_LEFT | MV_RIGHT,
10302 int center_element = Feld[x][y]; /* should always be non-moving! */
10305 for (i = 0; i < NUM_DIRECTIONS; i++)
10307 int xx = x + xy[i][0];
10308 int yy = y + xy[i][1];
10309 int center_side = trigger_sides[i][0];
10310 int border_side = trigger_sides[i][1];
10311 int border_element;
10313 if (!IN_LEV_FIELD(xx, yy))
10316 if (IS_PLAYER(x, y))
10318 struct PlayerInfo *player = PLAYERINFO(x, y);
10320 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10321 border_element = Feld[xx][yy]; /* may be moving! */
10322 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10323 border_element = Feld[xx][yy];
10324 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10325 border_element = MovingOrBlocked2Element(xx, yy);
10327 continue; /* center and border element do not touch */
10330 /* !!! TEST ONLY !!! */
10331 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10332 player->index_bit, border_side);
10333 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10334 CE_OTHER_GETS_TOUCHED,
10335 player->index_bit, border_side);
10337 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10338 CE_OTHER_GETS_TOUCHED,
10339 player->index_bit, border_side);
10340 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10341 player->index_bit, border_side);
10344 else if (IS_PLAYER(xx, yy))
10346 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10348 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10350 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10351 continue; /* center and border element do not touch */
10355 /* !!! TEST ONLY !!! */
10356 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10357 player->index_bit, center_side);
10358 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10359 CE_OTHER_GETS_TOUCHED,
10360 player->index_bit, center_side);
10362 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10363 CE_OTHER_GETS_TOUCHED,
10364 player->index_bit, center_side);
10365 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10366 player->index_bit, center_side);
10374 void TestIfElementTouchesCustomElement(int x, int y)
10376 static int xy[4][2] =
10383 static int trigger_sides[4][2] =
10385 /* center side border side */
10386 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10387 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10388 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10389 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10391 static int touch_dir[4] =
10393 MV_LEFT | MV_RIGHT,
10398 boolean change_center_element = FALSE;
10399 int center_element_change_page = 0;
10400 int center_element = Feld[x][y]; /* should always be non-moving! */
10401 int border_trigger_element = EL_UNDEFINED;
10404 for (i = 0; i < NUM_DIRECTIONS; i++)
10406 int xx = x + xy[i][0];
10407 int yy = y + xy[i][1];
10408 int center_side = trigger_sides[i][0];
10409 int border_side = trigger_sides[i][1];
10410 int border_element;
10412 if (!IN_LEV_FIELD(xx, yy))
10415 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10416 border_element = Feld[xx][yy]; /* may be moving! */
10417 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10418 border_element = Feld[xx][yy];
10419 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10420 border_element = MovingOrBlocked2Element(xx, yy);
10422 continue; /* center and border element do not touch */
10424 /* check for change of center element (but change it only once) */
10425 if (IS_CUSTOM_ELEMENT(center_element) &&
10426 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10427 !change_center_element)
10429 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10431 struct ElementChangeInfo *change =
10432 &element_info[center_element].change_page[j];
10434 if (change->can_change &&
10435 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10436 change->trigger_side & border_side &&
10438 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10440 change->trigger_element == border_element
10444 change_center_element = TRUE;
10445 center_element_change_page = j;
10446 border_trigger_element = border_element;
10453 /* check for change of border element */
10454 if (IS_CUSTOM_ELEMENT(border_element) &&
10455 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10457 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10459 struct ElementChangeInfo *change =
10460 &element_info[border_element].change_page[j];
10462 if (change->can_change &&
10463 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10464 change->trigger_side & center_side &&
10466 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10468 change->trigger_element == center_element
10473 printf("::: border_element %d, %d\n", x, y);
10476 CheckElementChangeByPage(xx, yy, border_element, center_element,
10477 CE_OTHER_IS_TOUCHING, j);
10484 if (change_center_element)
10487 printf("::: center_element %d, %d\n", x, y);
10490 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10491 CE_OTHER_IS_TOUCHING, center_element_change_page);
10495 void TestIfElementHitsCustomElement(int x, int y, int direction)
10497 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10498 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10499 int hitx = x + dx, hity = y + dy;
10500 int hitting_element = Feld[x][y];
10501 int touched_element;
10503 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10504 !IS_FREE(hitx, hity) &&
10505 (!IS_MOVING(hitx, hity) ||
10506 MovDir[hitx][hity] != direction ||
10507 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10510 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10514 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10518 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10519 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10521 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10522 CE_HITTING_SOMETHING, direction);
10524 if (IN_LEV_FIELD(hitx, hity))
10526 int opposite_direction = MV_DIR_OPPOSITE(direction);
10527 int hitting_side = direction;
10528 int touched_side = opposite_direction;
10530 int touched_element = MovingOrBlocked2Element(hitx, hity);
10533 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10534 MovDir[hitx][hity] != direction ||
10535 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10544 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10545 CE_HIT_BY_SOMETHING, opposite_direction);
10547 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10548 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10550 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10552 struct ElementChangeInfo *change =
10553 &element_info[hitting_element].change_page[i];
10555 if (change->can_change &&
10556 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10557 change->trigger_side & touched_side &&
10560 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10562 change->trigger_element == touched_element
10566 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10567 CE_OTHER_IS_HITTING, i);
10573 if (IS_CUSTOM_ELEMENT(touched_element) &&
10574 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10576 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10578 struct ElementChangeInfo *change =
10579 &element_info[touched_element].change_page[i];
10581 if (change->can_change &&
10582 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10583 change->trigger_side & hitting_side &&
10585 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10587 change->trigger_element == hitting_element
10591 CheckElementChangeByPage(hitx, hity, touched_element,
10592 hitting_element, CE_OTHER_GETS_HIT, i);
10602 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10604 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10605 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10606 int hitx = x + dx, hity = y + dy;
10607 int hitting_element = Feld[x][y];
10608 int touched_element;
10610 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10611 !IS_FREE(hitx, hity) &&
10612 (!IS_MOVING(hitx, hity) ||
10613 MovDir[hitx][hity] != direction ||
10614 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10617 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10621 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10625 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10626 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10628 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10629 EP_CAN_SMASH_EVERYTHING, direction);
10631 if (IN_LEV_FIELD(hitx, hity))
10633 int opposite_direction = MV_DIR_OPPOSITE(direction);
10634 int hitting_side = direction;
10635 int touched_side = opposite_direction;
10637 int touched_element = MovingOrBlocked2Element(hitx, hity);
10640 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10641 MovDir[hitx][hity] != direction ||
10642 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10651 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10652 CE_SMASHED_BY_SOMETHING, opposite_direction);
10654 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10655 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10657 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10659 struct ElementChangeInfo *change =
10660 &element_info[hitting_element].change_page[i];
10662 if (change->can_change &&
10663 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10664 change->trigger_side & touched_side &&
10667 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10669 change->trigger_element == touched_element
10673 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10674 CE_OTHER_IS_SMASHING, i);
10680 if (IS_CUSTOM_ELEMENT(touched_element) &&
10681 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10683 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10685 struct ElementChangeInfo *change =
10686 &element_info[touched_element].change_page[i];
10688 if (change->can_change &&
10689 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10690 change->trigger_side & hitting_side &&
10692 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10694 change->trigger_element == hitting_element
10698 CheckElementChangeByPage(hitx, hity, touched_element,
10699 hitting_element, CE_OTHER_GETS_SMASHED,i);
10709 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10711 int i, kill_x = -1, kill_y = -1;
10712 int bad_element = -1;
10713 static int test_xy[4][2] =
10720 static int test_dir[4] =
10728 for (i = 0; i < NUM_DIRECTIONS; i++)
10730 int test_x, test_y, test_move_dir, test_element;
10732 test_x = good_x + test_xy[i][0];
10733 test_y = good_y + test_xy[i][1];
10735 if (!IN_LEV_FIELD(test_x, test_y))
10739 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10742 test_element = Feld[test_x][test_y];
10744 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10747 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10748 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10750 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10751 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10755 bad_element = test_element;
10761 if (kill_x != -1 || kill_y != -1)
10763 if (IS_PLAYER(good_x, good_y))
10765 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10768 if (player->shield_deadly_time_left > 0 &&
10769 !IS_INDESTRUCTIBLE(bad_element))
10770 Bang(kill_x, kill_y);
10771 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10774 if (player->shield_deadly_time_left > 0)
10775 Bang(kill_x, kill_y);
10776 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10781 Bang(good_x, good_y);
10785 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10787 int i, kill_x = -1, kill_y = -1;
10788 int bad_element = Feld[bad_x][bad_y];
10789 static int test_xy[4][2] =
10796 static int touch_dir[4] =
10798 MV_LEFT | MV_RIGHT,
10803 static int test_dir[4] =
10811 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10814 for (i = 0; i < NUM_DIRECTIONS; i++)
10816 int test_x, test_y, test_move_dir, test_element;
10818 test_x = bad_x + test_xy[i][0];
10819 test_y = bad_y + test_xy[i][1];
10820 if (!IN_LEV_FIELD(test_x, test_y))
10824 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10826 test_element = Feld[test_x][test_y];
10828 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10829 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10831 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10832 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10834 /* good thing is player or penguin that does not move away */
10835 if (IS_PLAYER(test_x, test_y))
10837 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10839 if (bad_element == EL_ROBOT && player->is_moving)
10840 continue; /* robot does not kill player if he is moving */
10842 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10844 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10845 continue; /* center and border element do not touch */
10852 else if (test_element == EL_PENGUIN)
10861 if (kill_x != -1 || kill_y != -1)
10863 if (IS_PLAYER(kill_x, kill_y))
10865 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10868 if (player->shield_deadly_time_left > 0 &&
10869 !IS_INDESTRUCTIBLE(bad_element))
10870 Bang(bad_x, bad_y);
10871 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10874 if (player->shield_deadly_time_left > 0)
10875 Bang(bad_x, bad_y);
10876 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10881 Bang(kill_x, kill_y);
10885 void TestIfHeroTouchesBadThing(int x, int y)
10887 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10890 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10892 TestIfGoodThingHitsBadThing(x, y, move_dir);
10895 void TestIfBadThingTouchesHero(int x, int y)
10897 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10900 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10902 TestIfBadThingHitsGoodThing(x, y, move_dir);
10905 void TestIfFriendTouchesBadThing(int x, int y)
10907 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10910 void TestIfBadThingTouchesFriend(int x, int y)
10912 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10915 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10917 int i, kill_x = bad_x, kill_y = bad_y;
10918 static int xy[4][2] =
10926 for (i = 0; i < NUM_DIRECTIONS; i++)
10930 x = bad_x + xy[i][0];
10931 y = bad_y + xy[i][1];
10932 if (!IN_LEV_FIELD(x, y))
10935 element = Feld[x][y];
10936 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10937 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10945 if (kill_x != bad_x || kill_y != bad_y)
10946 Bang(bad_x, bad_y);
10949 void KillHero(struct PlayerInfo *player)
10951 int jx = player->jx, jy = player->jy;
10953 if (!player->active)
10956 /* remove accessible field at the player's position */
10957 Feld[jx][jy] = EL_EMPTY;
10959 /* deactivate shield (else Bang()/Explode() would not work right) */
10960 player->shield_normal_time_left = 0;
10961 player->shield_deadly_time_left = 0;
10967 static void KillHeroUnlessEnemyProtected(int x, int y)
10969 if (!PLAYER_ENEMY_PROTECTED(x, y))
10970 KillHero(PLAYERINFO(x, y));
10973 static void KillHeroUnlessExplosionProtected(int x, int y)
10975 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10976 KillHero(PLAYERINFO(x, y));
10979 void BuryHero(struct PlayerInfo *player)
10981 int jx = player->jx, jy = player->jy;
10983 if (!player->active)
10987 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10989 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10991 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10993 player->GameOver = TRUE;
10994 RemoveHero(player);
10997 void RemoveHero(struct PlayerInfo *player)
10999 int jx = player->jx, jy = player->jy;
11000 int i, found = FALSE;
11002 player->present = FALSE;
11003 player->active = FALSE;
11005 if (!ExplodeField[jx][jy])
11006 StorePlayer[jx][jy] = 0;
11008 for (i = 0; i < MAX_PLAYERS; i++)
11009 if (stored_player[i].active)
11013 AllPlayersGone = TRUE;
11020 =============================================================================
11021 checkDiagonalPushing()
11022 -----------------------------------------------------------------------------
11023 check if diagonal input device direction results in pushing of object
11024 (by checking if the alternative direction is walkable, diggable, ...)
11025 =============================================================================
11028 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11029 int x, int y, int real_dx, int real_dy)
11031 int jx, jy, dx, dy, xx, yy;
11033 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11036 /* diagonal direction: check alternative direction */
11041 xx = jx + (dx == 0 ? real_dx : 0);
11042 yy = jy + (dy == 0 ? real_dy : 0);
11044 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11048 =============================================================================
11050 -----------------------------------------------------------------------------
11051 x, y: field next to player (non-diagonal) to try to dig to
11052 real_dx, real_dy: direction as read from input device (can be diagonal)
11053 =============================================================================
11056 int DigField(struct PlayerInfo *player,
11057 int oldx, int oldy, int x, int y,
11058 int real_dx, int real_dy, int mode)
11061 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11063 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11064 boolean player_was_pushing = player->is_pushing;
11065 int jx = oldx, jy = oldy;
11066 int dx = x - jx, dy = y - jy;
11067 int nextx = x + dx, nexty = y + dy;
11068 int move_direction = (dx == -1 ? MV_LEFT :
11069 dx == +1 ? MV_RIGHT :
11071 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11072 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11074 int dig_side = MV_DIR_OPPOSITE(move_direction);
11076 static int trigger_sides[4] =
11078 CH_SIDE_RIGHT, /* moving left */
11079 CH_SIDE_LEFT, /* moving right */
11080 CH_SIDE_BOTTOM, /* moving up */
11081 CH_SIDE_TOP, /* moving down */
11083 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11085 int old_element = Feld[jx][jy];
11088 if (is_player) /* function can also be called by EL_PENGUIN */
11090 if (player->MovPos == 0)
11092 player->is_digging = FALSE;
11093 player->is_collecting = FALSE;
11096 if (player->MovPos == 0) /* last pushing move finished */
11097 player->is_pushing = FALSE;
11099 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11101 player->is_switching = FALSE;
11102 #if USE_NEW_PUSH_DELAY
11103 player->push_delay = -1;
11105 player->push_delay = 0;
11108 return MF_NO_ACTION;
11112 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11113 return MF_NO_ACTION;
11118 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11120 if (IS_TUBE(Feld[jx][jy]) ||
11121 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11125 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11126 int tube_leave_directions[][2] =
11128 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11129 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11130 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11131 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11132 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11133 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11134 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11135 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11136 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11137 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11138 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11139 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11142 while (tube_leave_directions[i][0] != tube_element)
11145 if (tube_leave_directions[i][0] == -1) /* should not happen */
11149 if (!(tube_leave_directions[i][1] & move_direction))
11150 return MF_NO_ACTION; /* tube has no opening in this direction */
11155 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11156 old_element = Back[jx][jy];
11160 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11161 return MF_NO_ACTION; /* field has no opening in this direction */
11163 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11164 return MF_NO_ACTION; /* field has no opening in this direction */
11166 element = Feld[x][y];
11168 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11169 return MF_NO_ACTION;
11171 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11172 game.engine_version >= VERSION_IDENT(2,2,0,0))
11173 return MF_NO_ACTION;
11176 if (game.gravity && is_player && !player->is_auto_moving &&
11177 canFallDown(player) && move_direction != MV_DOWN &&
11178 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11179 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11183 if (element == EL_EMPTY_SPACE &&
11184 game.gravity && !player->is_auto_moving &&
11185 canFallDown(player) && move_direction != MV_DOWN)
11186 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11192 case EL_SP_PORT_LEFT:
11193 case EL_SP_PORT_RIGHT:
11194 case EL_SP_PORT_UP:
11195 case EL_SP_PORT_DOWN:
11196 case EL_SP_PORT_HORIZONTAL:
11197 case EL_SP_PORT_VERTICAL:
11198 case EL_SP_PORT_ANY:
11199 case EL_SP_GRAVITY_PORT_LEFT:
11200 case EL_SP_GRAVITY_PORT_RIGHT:
11201 case EL_SP_GRAVITY_PORT_UP:
11202 case EL_SP_GRAVITY_PORT_DOWN:
11204 if (!canEnterSupaplexPort(x, y, dx, dy))
11205 return MF_NO_ACTION;
11208 element != EL_SP_PORT_LEFT &&
11209 element != EL_SP_GRAVITY_PORT_LEFT &&
11210 element != EL_SP_PORT_HORIZONTAL &&
11211 element != EL_SP_PORT_ANY) ||
11213 element != EL_SP_PORT_RIGHT &&
11214 element != EL_SP_GRAVITY_PORT_RIGHT &&
11215 element != EL_SP_PORT_HORIZONTAL &&
11216 element != EL_SP_PORT_ANY) ||
11218 element != EL_SP_PORT_UP &&
11219 element != EL_SP_GRAVITY_PORT_UP &&
11220 element != EL_SP_PORT_VERTICAL &&
11221 element != EL_SP_PORT_ANY) ||
11223 element != EL_SP_PORT_DOWN &&
11224 element != EL_SP_GRAVITY_PORT_DOWN &&
11225 element != EL_SP_PORT_VERTICAL &&
11226 element != EL_SP_PORT_ANY) ||
11227 !IN_LEV_FIELD(nextx, nexty) ||
11228 !IS_FREE(nextx, nexty))
11229 return MF_NO_ACTION;
11232 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11233 element == EL_SP_GRAVITY_PORT_RIGHT ||
11234 element == EL_SP_GRAVITY_PORT_UP ||
11235 element == EL_SP_GRAVITY_PORT_DOWN)
11236 game.gravity = !game.gravity;
11238 /* automatically move to the next field with double speed */
11239 player->programmed_action = move_direction;
11241 if (player->move_delay_reset_counter == 0)
11243 player->move_delay_reset_counter = 2; /* two double speed steps */
11245 DOUBLE_PLAYER_SPEED(player);
11248 player->move_delay_reset_counter = 2;
11250 DOUBLE_PLAYER_SPEED(player);
11254 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11257 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11263 case EL_TUBE_VERTICAL:
11264 case EL_TUBE_HORIZONTAL:
11265 case EL_TUBE_VERTICAL_LEFT:
11266 case EL_TUBE_VERTICAL_RIGHT:
11267 case EL_TUBE_HORIZONTAL_UP:
11268 case EL_TUBE_HORIZONTAL_DOWN:
11269 case EL_TUBE_LEFT_UP:
11270 case EL_TUBE_LEFT_DOWN:
11271 case EL_TUBE_RIGHT_UP:
11272 case EL_TUBE_RIGHT_DOWN:
11275 int tube_enter_directions[][2] =
11277 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11278 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11279 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11280 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11281 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11282 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11283 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11284 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11285 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11286 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11287 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11288 { -1, MV_NO_MOVING }
11291 while (tube_enter_directions[i][0] != element)
11294 if (tube_enter_directions[i][0] == -1) /* should not happen */
11298 if (!(tube_enter_directions[i][1] & move_direction))
11299 return MF_NO_ACTION; /* tube has no opening in this direction */
11301 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11309 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11311 if (IS_WALKABLE(element))
11314 int sound_element = SND_ELEMENT(element);
11315 int sound_action = ACTION_WALKING;
11318 if (!ACCESS_FROM(element, opposite_direction))
11319 return MF_NO_ACTION; /* field not accessible from this direction */
11323 if (element == EL_EMPTY_SPACE &&
11324 game.gravity && !player->is_auto_moving &&
11325 canFallDown(player) && move_direction != MV_DOWN)
11326 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11329 if (IS_GATE(element))
11331 if (!player->key[element - EL_GATE_1])
11332 return MF_NO_ACTION;
11334 else if (IS_GATE_GRAY(element))
11336 if (!player->key[element - EL_GATE_1_GRAY])
11337 return MF_NO_ACTION;
11339 else if (element == EL_EXIT_OPEN ||
11340 element == EL_SP_EXIT_OPEN ||
11341 element == EL_SP_EXIT_OPENING)
11343 sound_action = ACTION_PASSING; /* player is passing exit */
11345 else if (element == EL_EMPTY)
11347 sound_action = ACTION_MOVING; /* nothing to walk on */
11350 /* play sound from background or player, whatever is available */
11351 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11352 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11354 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11359 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11361 else if (IS_PASSABLE(element))
11365 if (!canPassField(x, y, move_direction))
11366 return MF_NO_ACTION;
11371 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11372 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11373 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11374 return MF_NO_ACTION;
11376 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11377 return MF_NO_ACTION;
11382 if (!ACCESS_FROM(element, opposite_direction))
11383 return MF_NO_ACTION; /* field not accessible from this direction */
11385 if (IS_CUSTOM_ELEMENT(element) &&
11386 !ACCESS_FROM(element, opposite_direction))
11387 return MF_NO_ACTION; /* field not accessible from this direction */
11391 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11392 return MF_NO_ACTION;
11397 if (IS_EM_GATE(element))
11399 if (!player->key[element - EL_EM_GATE_1])
11400 return MF_NO_ACTION;
11402 else if (IS_EM_GATE_GRAY(element))
11404 if (!player->key[element - EL_EM_GATE_1_GRAY])
11405 return MF_NO_ACTION;
11407 else if (IS_SP_PORT(element))
11409 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11410 element == EL_SP_GRAVITY_PORT_RIGHT ||
11411 element == EL_SP_GRAVITY_PORT_UP ||
11412 element == EL_SP_GRAVITY_PORT_DOWN)
11413 game.gravity = !game.gravity;
11414 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11415 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11416 element == EL_SP_GRAVITY_ON_PORT_UP ||
11417 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11418 game.gravity = TRUE;
11419 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11420 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11421 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11422 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11423 game.gravity = FALSE;
11426 /* automatically move to the next field with double speed */
11427 player->programmed_action = move_direction;
11429 if (player->move_delay_reset_counter == 0)
11431 player->move_delay_reset_counter = 2; /* two double speed steps */
11433 DOUBLE_PLAYER_SPEED(player);
11436 player->move_delay_reset_counter = 2;
11438 DOUBLE_PLAYER_SPEED(player);
11441 PlayLevelSoundAction(x, y, ACTION_PASSING);
11445 else if (IS_DIGGABLE(element))
11449 if (mode != DF_SNAP)
11452 GfxElement[x][y] = GFX_ELEMENT(element);
11455 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11457 player->is_digging = TRUE;
11460 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11462 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11463 player->index_bit, dig_side);
11466 if (mode == DF_SNAP)
11467 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11472 else if (IS_COLLECTIBLE(element))
11476 if (is_player && mode != DF_SNAP)
11478 GfxElement[x][y] = element;
11479 player->is_collecting = TRUE;
11482 if (element == EL_SPEED_PILL)
11483 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11484 else if (element == EL_EXTRA_TIME && level.time > 0)
11487 DrawGameValue_Time(TimeLeft);
11489 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11491 player->shield_normal_time_left += 10;
11492 if (element == EL_SHIELD_DEADLY)
11493 player->shield_deadly_time_left += 10;
11495 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11497 if (player->inventory_size < MAX_INVENTORY_SIZE)
11498 player->inventory_element[player->inventory_size++] = element;
11500 DrawGameValue_Dynamite(local_player->inventory_size);
11502 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11504 player->dynabomb_count++;
11505 player->dynabombs_left++;
11507 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11509 player->dynabomb_size++;
11511 else if (element == EL_DYNABOMB_INCREASE_POWER)
11513 player->dynabomb_xl = TRUE;
11515 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11516 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11518 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11519 element - EL_KEY_1 : element - EL_EM_KEY_1);
11521 player->key[key_nr] = TRUE;
11523 DrawGameValue_Keys(player);
11525 redraw_mask |= REDRAW_DOOR_1;
11527 else if (IS_ENVELOPE(element))
11530 player->show_envelope = element;
11532 ShowEnvelope(element - EL_ENVELOPE_1);
11535 else if (IS_DROPPABLE(element) ||
11536 IS_THROWABLE(element)) /* can be collected and dropped */
11540 if (element_info[element].collect_count == 0)
11541 player->inventory_infinite_element = element;
11543 for (i = 0; i < element_info[element].collect_count; i++)
11544 if (player->inventory_size < MAX_INVENTORY_SIZE)
11545 player->inventory_element[player->inventory_size++] = element;
11547 DrawGameValue_Dynamite(local_player->inventory_size);
11549 else if (element_info[element].collect_count > 0)
11551 local_player->gems_still_needed -=
11552 element_info[element].collect_count;
11553 if (local_player->gems_still_needed < 0)
11554 local_player->gems_still_needed = 0;
11556 DrawGameValue_Emeralds(local_player->gems_still_needed);
11559 RaiseScoreElement(element);
11560 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11563 CheckTriggeredElementChangeByPlayer(x, y, element,
11564 CE_OTHER_GETS_COLLECTED,
11565 player->index_bit, dig_side);
11568 if (mode == DF_SNAP)
11569 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11574 else if (IS_PUSHABLE(element))
11576 if (mode == DF_SNAP && element != EL_BD_ROCK)
11577 return MF_NO_ACTION;
11579 if (CAN_FALL(element) && dy)
11580 return MF_NO_ACTION;
11582 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11583 !(element == EL_SPRING && level.use_spring_bug))
11584 return MF_NO_ACTION;
11587 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11588 ((move_direction & MV_VERTICAL &&
11589 ((element_info[element].move_pattern & MV_LEFT &&
11590 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11591 (element_info[element].move_pattern & MV_RIGHT &&
11592 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11593 (move_direction & MV_HORIZONTAL &&
11594 ((element_info[element].move_pattern & MV_UP &&
11595 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11596 (element_info[element].move_pattern & MV_DOWN &&
11597 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11598 return MF_NO_ACTION;
11602 /* do not push elements already moving away faster than player */
11603 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11604 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11605 return MF_NO_ACTION;
11607 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11608 return MF_NO_ACTION;
11614 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11616 if (player->push_delay_value == -1 || !player_was_pushing)
11617 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11619 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11621 if (player->push_delay_value == -1)
11622 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11625 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11627 if (player->push_delay_value == -1 || !player_was_pushing)
11628 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11631 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11633 if (!player->is_pushing)
11634 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11638 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11639 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11640 !player_is_pushing))
11641 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11644 if (!player->is_pushing &&
11645 game.engine_version >= VERSION_IDENT(2,2,0,7))
11646 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11650 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11651 player->push_delay, player->push_delay_value,
11652 FrameCounter, game.engine_version,
11653 player_was_pushing, player->is_pushing,
11654 element, element_info[element].token_name,
11655 GET_NEW_PUSH_DELAY(element));
11658 player->is_pushing = TRUE;
11660 if (!(IN_LEV_FIELD(nextx, nexty) &&
11661 (IS_FREE(nextx, nexty) ||
11662 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11663 IS_SB_ELEMENT(element)))))
11664 return MF_NO_ACTION;
11666 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11667 return MF_NO_ACTION;
11669 #if USE_NEW_PUSH_DELAY
11672 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11673 printf("::: ALERT: %d, %d [%d / %d]\n",
11674 player->push_delay, player->push_delay2,
11675 FrameCounter, FrameCounter / 50);
11678 if (player->push_delay == -1) /* new pushing; restart delay */
11679 player->push_delay = 0;
11681 if (player->push_delay == 0) /* new pushing; restart delay */
11682 player->push_delay = FrameCounter;
11685 #if USE_NEW_PUSH_DELAY
11687 if ( (player->push_delay > 0) != (!xxx_fr) )
11688 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11689 player->push_delay,
11690 xxx_pdv2, player->push_delay2, player->push_delay_value,
11691 FrameCounter, FrameCounter / 50);
11695 if (player->push_delay > 0 &&
11696 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11697 element != EL_SPRING && element != EL_BALLOON)
11700 if (player->push_delay < player->push_delay_value &&
11701 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11702 element != EL_SPRING && element != EL_BALLOON)
11706 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11707 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11708 element != EL_SPRING && element != EL_BALLOON)
11711 /* make sure that there is no move delay before next try to push */
11712 #if USE_NEW_MOVE_DELAY
11713 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11714 player->move_delay = 0;
11716 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11717 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11720 return MF_NO_ACTION;
11724 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11727 if (IS_SB_ELEMENT(element))
11729 if (element == EL_SOKOBAN_FIELD_FULL)
11731 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11732 local_player->sokobanfields_still_needed++;
11735 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11737 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11738 local_player->sokobanfields_still_needed--;
11741 Feld[x][y] = EL_SOKOBAN_OBJECT;
11743 if (Back[x][y] == Back[nextx][nexty])
11744 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11745 else if (Back[x][y] != 0)
11746 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11749 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11752 if (local_player->sokobanfields_still_needed == 0 &&
11753 game.emulation == EMU_SOKOBAN)
11755 player->LevelSolved = player->GameOver = TRUE;
11756 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11760 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11762 InitMovingField(x, y, move_direction);
11763 GfxAction[x][y] = ACTION_PUSHING;
11765 if (mode == DF_SNAP)
11766 ContinueMoving(x, y);
11768 MovPos[x][y] = (dx != 0 ? dx : dy);
11770 Pushed[x][y] = TRUE;
11771 Pushed[nextx][nexty] = TRUE;
11773 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11774 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11776 player->push_delay_value = -1; /* get new value later */
11778 #if USE_PUSH_BUGFIX
11779 /* now: check for element change _after_ element has been pushed! */
11781 if (game.use_bug_change_when_pushing)
11783 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11786 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11787 player->index_bit, dig_side);
11788 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11789 player->index_bit, dig_side);
11795 /* check for element change _after_ element has been pushed! */
11799 /* !!! TEST ONLY !!! */
11800 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11801 player->index_bit, dig_side);
11802 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11803 player->index_bit, dig_side);
11805 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11806 player->index_bit, dig_side);
11807 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11808 player->index_bit, dig_side);
11816 else if (IS_SWITCHABLE(element))
11818 if (PLAYER_SWITCHING(player, x, y))
11820 CheckTriggeredElementChangeByPlayer(x,y, element,
11821 CE_OTHER_GETS_PRESSED,
11822 player->index_bit, dig_side);
11827 player->is_switching = TRUE;
11828 player->switch_x = x;
11829 player->switch_y = y;
11831 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11833 if (element == EL_ROBOT_WHEEL)
11835 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11839 DrawLevelField(x, y);
11841 else if (element == EL_SP_TERMINAL)
11845 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11847 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11849 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11850 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11853 else if (IS_BELT_SWITCH(element))
11855 ToggleBeltSwitch(x, y);
11857 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11858 element == EL_SWITCHGATE_SWITCH_DOWN)
11860 ToggleSwitchgateSwitch(x, y);
11862 else if (element == EL_LIGHT_SWITCH ||
11863 element == EL_LIGHT_SWITCH_ACTIVE)
11865 ToggleLightSwitch(x, y);
11868 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11869 SND_LIGHT_SWITCH_ACTIVATING :
11870 SND_LIGHT_SWITCH_DEACTIVATING);
11873 else if (element == EL_TIMEGATE_SWITCH)
11875 ActivateTimegateSwitch(x, y);
11877 else if (element == EL_BALLOON_SWITCH_LEFT ||
11878 element == EL_BALLOON_SWITCH_RIGHT ||
11879 element == EL_BALLOON_SWITCH_UP ||
11880 element == EL_BALLOON_SWITCH_DOWN ||
11881 element == EL_BALLOON_SWITCH_ANY)
11883 if (element == EL_BALLOON_SWITCH_ANY)
11884 game.balloon_dir = move_direction;
11886 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11887 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11888 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11889 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11892 else if (element == EL_LAMP)
11894 Feld[x][y] = EL_LAMP_ACTIVE;
11895 local_player->lights_still_needed--;
11897 DrawLevelField(x, y);
11899 else if (element == EL_TIME_ORB_FULL)
11901 Feld[x][y] = EL_TIME_ORB_EMPTY;
11903 DrawGameValue_Time(TimeLeft);
11905 DrawLevelField(x, y);
11908 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11912 CheckTriggeredElementChangeByPlayer(x, y, element,
11913 CE_OTHER_IS_SWITCHING,
11914 player->index_bit, dig_side);
11916 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11917 player->index_bit, dig_side);
11923 if (!PLAYER_SWITCHING(player, x, y))
11925 player->is_switching = TRUE;
11926 player->switch_x = x;
11927 player->switch_y = y;
11930 /* !!! TEST ONLY !!! */
11931 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11932 player->index_bit, dig_side);
11933 CheckTriggeredElementChangeByPlayer(x, y, element,
11934 CE_OTHER_IS_SWITCHING,
11935 player->index_bit, dig_side);
11937 CheckTriggeredElementChangeByPlayer(x, y, element,
11938 CE_OTHER_IS_SWITCHING,
11939 player->index_bit, dig_side);
11940 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11941 player->index_bit, dig_side);
11946 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11947 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11948 player->index_bit, dig_side);
11949 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11950 player->index_bit, dig_side);
11952 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11953 player->index_bit, dig_side);
11954 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11955 player->index_bit, dig_side);
11959 return MF_NO_ACTION;
11962 #if USE_NEW_PUSH_DELAY
11963 player->push_delay = -1;
11965 player->push_delay = 0;
11968 if (Feld[x][y] != element) /* really digged/collected something */
11969 player->is_collecting = !player->is_digging;
11974 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11976 int jx = player->jx, jy = player->jy;
11977 int x = jx + dx, y = jy + dy;
11978 int snap_direction = (dx == -1 ? MV_LEFT :
11979 dx == +1 ? MV_RIGHT :
11981 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11984 if (player->MovPos != 0)
11987 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11991 if (!player->active || !IN_LEV_FIELD(x, y))
11999 if (player->MovPos == 0)
12000 player->is_pushing = FALSE;
12002 player->is_snapping = FALSE;
12004 if (player->MovPos == 0)
12006 player->is_moving = FALSE;
12007 player->is_digging = FALSE;
12008 player->is_collecting = FALSE;
12014 if (player->is_snapping)
12017 player->MovDir = snap_direction;
12020 if (player->MovPos == 0)
12023 player->is_moving = FALSE;
12024 player->is_digging = FALSE;
12025 player->is_collecting = FALSE;
12028 player->is_dropping = FALSE;
12030 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12033 player->is_snapping = TRUE;
12036 if (player->MovPos == 0)
12039 player->is_moving = FALSE;
12040 player->is_digging = FALSE;
12041 player->is_collecting = FALSE;
12045 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12046 DrawLevelField(player->last_jx, player->last_jy);
12049 DrawLevelField(x, y);
12058 boolean DropElement(struct PlayerInfo *player)
12060 int old_element, new_element;
12061 int dropx = player->jx, dropy = player->jy;
12062 int drop_direction = player->MovDir;
12064 int drop_side = drop_direction;
12066 static int trigger_sides[4] =
12068 CH_SIDE_LEFT, /* dropping left */
12069 CH_SIDE_RIGHT, /* dropping right */
12070 CH_SIDE_TOP, /* dropping up */
12071 CH_SIDE_BOTTOM, /* dropping down */
12073 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12075 int drop_element = (player->inventory_size > 0 ?
12076 player->inventory_element[player->inventory_size - 1] :
12077 player->inventory_infinite_element != EL_UNDEFINED ?
12078 player->inventory_infinite_element :
12079 player->dynabombs_left > 0 ?
12080 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12083 if (IS_THROWABLE(drop_element))
12085 dropx += GET_DX_FROM_DIR(drop_direction);
12086 dropy += GET_DY_FROM_DIR(drop_direction);
12088 if (!IN_LEV_FIELD(dropx, dropy))
12092 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12093 new_element = drop_element; /* default: no change when dropping */
12095 /* check if player is active, not moving and ready to drop */
12096 if (!player->active || player->MovPos || player->drop_delay > 0)
12099 /* check if player has anything that can be dropped */
12101 if (new_element == EL_UNDEFINED)
12104 if (player->inventory_size == 0 &&
12105 player->inventory_infinite_element == EL_UNDEFINED &&
12106 player->dynabombs_left == 0)
12110 /* check if anything can be dropped at the current position */
12111 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12114 /* collected custom elements can only be dropped on empty fields */
12116 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12119 if (player->inventory_size > 0 &&
12120 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12121 && old_element != EL_EMPTY)
12125 if (old_element != EL_EMPTY)
12126 Back[dropx][dropy] = old_element; /* store old element on this field */
12128 ResetGfxAnimation(dropx, dropy);
12129 ResetRandomAnimationValue(dropx, dropy);
12131 if (player->inventory_size > 0 ||
12132 player->inventory_infinite_element != EL_UNDEFINED)
12134 if (player->inventory_size > 0)
12136 player->inventory_size--;
12139 new_element = player->inventory_element[player->inventory_size];
12142 DrawGameValue_Dynamite(local_player->inventory_size);
12144 if (new_element == EL_DYNAMITE)
12145 new_element = EL_DYNAMITE_ACTIVE;
12146 else if (new_element == EL_SP_DISK_RED)
12147 new_element = EL_SP_DISK_RED_ACTIVE;
12150 Feld[dropx][dropy] = new_element;
12152 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12153 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12154 el2img(Feld[dropx][dropy]), 0);
12156 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12159 /* needed if previous element just changed to "empty" in the last frame */
12160 Changed[dropx][dropy] = 0; /* allow another change */
12164 /* !!! TEST ONLY !!! */
12165 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12166 player->index_bit, drop_side);
12167 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12168 CE_OTHER_GETS_DROPPED,
12169 player->index_bit, drop_side);
12171 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12172 CE_OTHER_GETS_DROPPED,
12173 player->index_bit, drop_side);
12174 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12175 player->index_bit, drop_side);
12178 TestIfElementTouchesCustomElement(dropx, dropy);
12180 else /* player is dropping a dyna bomb */
12182 player->dynabombs_left--;
12185 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12188 Feld[dropx][dropy] = new_element;
12190 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12191 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12192 el2img(Feld[dropx][dropy]), 0);
12194 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12201 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12204 InitField_WithBug1(dropx, dropy, FALSE);
12206 InitField(dropx, dropy, FALSE);
12207 if (CAN_MOVE(Feld[dropx][dropy]))
12208 InitMovDir(dropx, dropy);
12212 new_element = Feld[dropx][dropy]; /* element might have changed */
12214 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12215 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12218 int move_stepsize = element_info[new_element].move_stepsize;
12220 int move_direction, nextx, nexty;
12222 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12223 MovDir[dropx][dropy] = drop_direction;
12225 move_direction = MovDir[dropx][dropy];
12226 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12227 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12230 Changed[dropx][dropy] = 0; /* allow another change */
12231 CheckCollision[dropx][dropy] = 2;
12234 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12237 WasJustMoving[dropx][dropy] = 3;
12240 InitMovingField(dropx, dropy, move_direction);
12241 ContinueMoving(dropx, dropy);
12246 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12249 Changed[dropx][dropy] = 0; /* allow another change */
12252 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12254 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12255 CE_HITTING_SOMETHING, move_direction);
12263 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12268 player->drop_delay = 8 + 8 + 8;
12272 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12277 player->is_dropping = TRUE;
12283 /* ------------------------------------------------------------------------- */
12284 /* game sound playing functions */
12285 /* ------------------------------------------------------------------------- */
12287 static int *loop_sound_frame = NULL;
12288 static int *loop_sound_volume = NULL;
12290 void InitPlayLevelSound()
12292 int num_sounds = getSoundListSize();
12294 checked_free(loop_sound_frame);
12295 checked_free(loop_sound_volume);
12297 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12298 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12301 static void PlayLevelSound(int x, int y, int nr)
12303 int sx = SCREENX(x), sy = SCREENY(y);
12304 int volume, stereo_position;
12305 int max_distance = 8;
12306 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12308 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12309 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12312 if (!IN_LEV_FIELD(x, y) ||
12313 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12314 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12317 volume = SOUND_MAX_VOLUME;
12319 if (!IN_SCR_FIELD(sx, sy))
12321 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12322 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12324 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12327 stereo_position = (SOUND_MAX_LEFT +
12328 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12329 (SCR_FIELDX + 2 * max_distance));
12331 if (IS_LOOP_SOUND(nr))
12333 /* This assures that quieter loop sounds do not overwrite louder ones,
12334 while restarting sound volume comparison with each new game frame. */
12336 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12339 loop_sound_volume[nr] = volume;
12340 loop_sound_frame[nr] = FrameCounter;
12343 PlaySoundExt(nr, volume, stereo_position, type);
12346 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12348 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12349 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12350 y < LEVELY(BY1) ? LEVELY(BY1) :
12351 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12355 static void PlayLevelSoundAction(int x, int y, int action)
12357 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12360 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12362 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12364 if (sound_effect != SND_UNDEFINED)
12365 PlayLevelSound(x, y, sound_effect);
12368 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12371 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12373 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12374 PlayLevelSound(x, y, sound_effect);
12377 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12379 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12381 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12382 PlayLevelSound(x, y, sound_effect);
12385 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12387 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12389 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12390 StopSound(sound_effect);
12393 static void PlayLevelMusic()
12395 if (levelset.music[level_nr] != MUS_UNDEFINED)
12396 PlayMusic(levelset.music[level_nr]); /* from config file */
12398 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12401 void RaiseScore(int value)
12403 local_player->score += value;
12405 DrawGameValue_Score(local_player->score);
12408 void RaiseScoreElement(int element)
12413 case EL_BD_DIAMOND:
12414 case EL_EMERALD_YELLOW:
12415 case EL_EMERALD_RED:
12416 case EL_EMERALD_PURPLE:
12417 case EL_SP_INFOTRON:
12418 RaiseScore(level.score[SC_EMERALD]);
12421 RaiseScore(level.score[SC_DIAMOND]);
12424 RaiseScore(level.score[SC_CRYSTAL]);
12427 RaiseScore(level.score[SC_PEARL]);
12430 case EL_BD_BUTTERFLY:
12431 case EL_SP_ELECTRON:
12432 RaiseScore(level.score[SC_BUG]);
12435 case EL_BD_FIREFLY:
12436 case EL_SP_SNIKSNAK:
12437 RaiseScore(level.score[SC_SPACESHIP]);
12440 case EL_DARK_YAMYAM:
12441 RaiseScore(level.score[SC_YAMYAM]);
12444 RaiseScore(level.score[SC_ROBOT]);
12447 RaiseScore(level.score[SC_PACMAN]);
12450 RaiseScore(level.score[SC_NUT]);
12453 case EL_SP_DISK_RED:
12454 case EL_DYNABOMB_INCREASE_NUMBER:
12455 case EL_DYNABOMB_INCREASE_SIZE:
12456 case EL_DYNABOMB_INCREASE_POWER:
12457 RaiseScore(level.score[SC_DYNAMITE]);
12459 case EL_SHIELD_NORMAL:
12460 case EL_SHIELD_DEADLY:
12461 RaiseScore(level.score[SC_SHIELD]);
12463 case EL_EXTRA_TIME:
12464 RaiseScore(level.score[SC_TIME_BONUS]);
12470 RaiseScore(level.score[SC_KEY]);
12473 RaiseScore(element_info[element].collect_score);
12478 void RequestQuitGame(boolean ask_if_really_quit)
12480 if (AllPlayersGone ||
12481 !ask_if_really_quit ||
12482 level_editor_test_game ||
12483 Request("Do you really want to quit the game ?",
12484 REQ_ASK | REQ_STAY_CLOSED))
12486 #if defined(NETWORK_AVALIABLE)
12487 if (options.network)
12488 SendToServer_StopPlaying();
12492 game_status = GAME_MODE_MAIN;
12500 if (tape.playing && tape.deactivate_display)
12501 TapeDeactivateDisplayOff(TRUE);
12504 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12507 if (tape.playing && tape.deactivate_display)
12508 TapeDeactivateDisplayOn();
12515 /* ---------- new game button stuff ---------------------------------------- */
12517 /* graphic position values for game buttons */
12518 #define GAME_BUTTON_XSIZE 30
12519 #define GAME_BUTTON_YSIZE 30
12520 #define GAME_BUTTON_XPOS 5
12521 #define GAME_BUTTON_YPOS 215
12522 #define SOUND_BUTTON_XPOS 5
12523 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12525 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12526 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12527 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12528 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12529 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12530 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12537 } gamebutton_info[NUM_GAME_BUTTONS] =
12540 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12545 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12546 GAME_CTRL_ID_PAUSE,
12550 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12555 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12556 SOUND_CTRL_ID_MUSIC,
12557 "background music on/off"
12560 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12561 SOUND_CTRL_ID_LOOPS,
12562 "sound loops on/off"
12565 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12566 SOUND_CTRL_ID_SIMPLE,
12567 "normal sounds on/off"
12571 void CreateGameButtons()
12575 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12577 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12578 struct GadgetInfo *gi;
12581 unsigned long event_mask;
12582 int gd_xoffset, gd_yoffset;
12583 int gd_x1, gd_x2, gd_y1, gd_y2;
12586 gd_xoffset = gamebutton_info[i].x;
12587 gd_yoffset = gamebutton_info[i].y;
12588 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12589 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12591 if (id == GAME_CTRL_ID_STOP ||
12592 id == GAME_CTRL_ID_PAUSE ||
12593 id == GAME_CTRL_ID_PLAY)
12595 button_type = GD_TYPE_NORMAL_BUTTON;
12597 event_mask = GD_EVENT_RELEASED;
12598 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12599 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12603 button_type = GD_TYPE_CHECK_BUTTON;
12605 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12606 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12607 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12608 event_mask = GD_EVENT_PRESSED;
12609 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12610 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12613 gi = CreateGadget(GDI_CUSTOM_ID, id,
12614 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12615 GDI_X, DX + gd_xoffset,
12616 GDI_Y, DY + gd_yoffset,
12617 GDI_WIDTH, GAME_BUTTON_XSIZE,
12618 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12619 GDI_TYPE, button_type,
12620 GDI_STATE, GD_BUTTON_UNPRESSED,
12621 GDI_CHECKED, checked,
12622 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12623 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12624 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12625 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12626 GDI_EVENT_MASK, event_mask,
12627 GDI_CALLBACK_ACTION, HandleGameButtons,
12631 Error(ERR_EXIT, "cannot create gadget");
12633 game_gadget[id] = gi;
12637 void FreeGameButtons()
12641 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12642 FreeGadget(game_gadget[i]);
12645 static void MapGameButtons()
12649 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12650 MapGadget(game_gadget[i]);
12653 void UnmapGameButtons()
12657 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12658 UnmapGadget(game_gadget[i]);
12661 static void HandleGameButtons(struct GadgetInfo *gi)
12663 int id = gi->custom_id;
12665 if (game_status != GAME_MODE_PLAYING)
12670 case GAME_CTRL_ID_STOP:
12671 RequestQuitGame(TRUE);
12674 case GAME_CTRL_ID_PAUSE:
12675 if (options.network)
12677 #if defined(NETWORK_AVALIABLE)
12679 SendToServer_ContinuePlaying();
12681 SendToServer_PausePlaying();
12685 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12688 case GAME_CTRL_ID_PLAY:
12691 #if defined(NETWORK_AVALIABLE)
12692 if (options.network)
12693 SendToServer_ContinuePlaying();
12697 tape.pausing = FALSE;
12698 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12703 case SOUND_CTRL_ID_MUSIC:
12704 if (setup.sound_music)
12706 setup.sound_music = FALSE;
12709 else if (audio.music_available)
12711 setup.sound = setup.sound_music = TRUE;
12713 SetAudioMode(setup.sound);
12719 case SOUND_CTRL_ID_LOOPS:
12720 if (setup.sound_loops)
12721 setup.sound_loops = FALSE;
12722 else if (audio.loops_available)
12724 setup.sound = setup.sound_loops = TRUE;
12725 SetAudioMode(setup.sound);
12729 case SOUND_CTRL_ID_SIMPLE:
12730 if (setup.sound_simple)
12731 setup.sound_simple = FALSE;
12732 else if (audio.sound_available)
12734 setup.sound = setup.sound_simple = TRUE;
12735 SetAudioMode(setup.sound);