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);
2063 /* !!! FIX THIS (START) !!! */
2064 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2066 if (em_main_init_game(level_nr, level.file_info.filename) != 0)
2068 game_status = GAME_MODE_MAIN;
2079 /* after drawing the level, correct some elements */
2080 if (game.timegate_time_left == 0)
2081 CloseAllOpenTimegates();
2083 if (setup.soft_scrolling)
2084 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2086 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2089 /* !!! FIX THIS (END) !!! */
2091 /* copy default game door content to main double buffer */
2092 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2093 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2095 DrawGameDoorValues();
2099 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2100 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2101 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2105 /* copy actual game door content to door double buffer for OpenDoor() */
2106 BlitBitmap(drawto, bitmap_db_door,
2107 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2109 OpenDoor(DOOR_OPEN_ALL);
2111 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2113 if (setup.sound_music)
2116 KeyboardAutoRepeatOffUnlessAutoplay();
2120 for (i = 0; i < MAX_PLAYERS; i++)
2121 printf("Player %d %sactive.\n",
2122 i + 1, (stored_player[i].active ? "" : "not "));
2126 printf("::: starting game [%d]\n", FrameCounter);
2130 void InitMovDir(int x, int y)
2132 int i, element = Feld[x][y];
2133 static int xy[4][2] =
2140 static int direction[3][4] =
2142 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2143 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2144 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2153 Feld[x][y] = EL_BUG;
2154 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2157 case EL_SPACESHIP_RIGHT:
2158 case EL_SPACESHIP_UP:
2159 case EL_SPACESHIP_LEFT:
2160 case EL_SPACESHIP_DOWN:
2161 Feld[x][y] = EL_SPACESHIP;
2162 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2165 case EL_BD_BUTTERFLY_RIGHT:
2166 case EL_BD_BUTTERFLY_UP:
2167 case EL_BD_BUTTERFLY_LEFT:
2168 case EL_BD_BUTTERFLY_DOWN:
2169 Feld[x][y] = EL_BD_BUTTERFLY;
2170 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2173 case EL_BD_FIREFLY_RIGHT:
2174 case EL_BD_FIREFLY_UP:
2175 case EL_BD_FIREFLY_LEFT:
2176 case EL_BD_FIREFLY_DOWN:
2177 Feld[x][y] = EL_BD_FIREFLY;
2178 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2181 case EL_PACMAN_RIGHT:
2183 case EL_PACMAN_LEFT:
2184 case EL_PACMAN_DOWN:
2185 Feld[x][y] = EL_PACMAN;
2186 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2189 case EL_SP_SNIKSNAK:
2190 MovDir[x][y] = MV_UP;
2193 case EL_SP_ELECTRON:
2194 MovDir[x][y] = MV_LEFT;
2201 Feld[x][y] = EL_MOLE;
2202 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2206 if (IS_CUSTOM_ELEMENT(element))
2208 struct ElementInfo *ei = &element_info[element];
2209 int move_direction_initial = ei->move_direction_initial;
2210 int move_pattern = ei->move_pattern;
2212 if (move_direction_initial == MV_START_PREVIOUS)
2214 if (MovDir[x][y] != MV_NO_MOVING)
2217 move_direction_initial = MV_START_AUTOMATIC;
2220 if (move_direction_initial == MV_START_RANDOM)
2221 MovDir[x][y] = 1 << RND(4);
2222 else if (move_direction_initial & MV_ANY_DIRECTION)
2223 MovDir[x][y] = move_direction_initial;
2224 else if (move_pattern == MV_ALL_DIRECTIONS ||
2225 move_pattern == MV_TURNING_LEFT ||
2226 move_pattern == MV_TURNING_RIGHT ||
2227 move_pattern == MV_TURNING_LEFT_RIGHT ||
2228 move_pattern == MV_TURNING_RIGHT_LEFT ||
2229 move_pattern == MV_TURNING_RANDOM)
2230 MovDir[x][y] = 1 << RND(4);
2231 else if (move_pattern == MV_HORIZONTAL)
2232 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2233 else if (move_pattern == MV_VERTICAL)
2234 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2235 else if (move_pattern & MV_ANY_DIRECTION)
2236 MovDir[x][y] = element_info[element].move_pattern;
2237 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2238 move_pattern == MV_ALONG_RIGHT_SIDE)
2241 /* use random direction as default start direction */
2242 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2243 MovDir[x][y] = 1 << RND(4);
2246 for (i = 0; i < NUM_DIRECTIONS; i++)
2248 int x1 = x + xy[i][0];
2249 int y1 = y + xy[i][1];
2251 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2253 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2254 MovDir[x][y] = direction[0][i];
2256 MovDir[x][y] = direction[1][i];
2265 MovDir[x][y] = 1 << RND(4);
2267 if (element != EL_BUG &&
2268 element != EL_SPACESHIP &&
2269 element != EL_BD_BUTTERFLY &&
2270 element != EL_BD_FIREFLY)
2273 for (i = 0; i < NUM_DIRECTIONS; i++)
2275 int x1 = x + xy[i][0];
2276 int y1 = y + xy[i][1];
2278 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2280 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2282 MovDir[x][y] = direction[0][i];
2285 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2286 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2288 MovDir[x][y] = direction[1][i];
2297 GfxDir[x][y] = MovDir[x][y];
2300 void InitAmoebaNr(int x, int y)
2303 int group_nr = AmoebeNachbarNr(x, y);
2307 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2309 if (AmoebaCnt[i] == 0)
2317 AmoebaNr[x][y] = group_nr;
2318 AmoebaCnt[group_nr]++;
2319 AmoebaCnt2[group_nr]++;
2325 boolean raise_level = FALSE;
2327 if (local_player->MovPos)
2331 if (tape.auto_play) /* tape might already be stopped here */
2332 tape.auto_play_level_solved = TRUE;
2334 if (tape.playing && tape.auto_play)
2335 tape.auto_play_level_solved = TRUE;
2338 local_player->LevelSolved = FALSE;
2340 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2344 if (!tape.playing && setup.sound_loops)
2345 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2346 SND_CTRL_PLAY_LOOP);
2348 while (TimeLeft > 0)
2350 if (!tape.playing && !setup.sound_loops)
2351 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2352 if (TimeLeft > 0 && !(TimeLeft % 10))
2353 RaiseScore(level.score[SC_TIME_BONUS]);
2354 if (TimeLeft > 100 && !(TimeLeft % 10))
2359 DrawGameValue_Time(TimeLeft);
2367 if (!tape.playing && setup.sound_loops)
2368 StopSound(SND_GAME_LEVELTIME_BONUS);
2370 else if (level.time == 0) /* level without time limit */
2372 if (!tape.playing && setup.sound_loops)
2373 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2374 SND_CTRL_PLAY_LOOP);
2376 while (TimePlayed < 999)
2378 if (!tape.playing && !setup.sound_loops)
2379 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2380 if (TimePlayed < 999 && !(TimePlayed % 10))
2381 RaiseScore(level.score[SC_TIME_BONUS]);
2382 if (TimePlayed < 900 && !(TimePlayed % 10))
2387 DrawGameValue_Time(TimePlayed);
2395 if (!tape.playing && setup.sound_loops)
2396 StopSound(SND_GAME_LEVELTIME_BONUS);
2399 /* close exit door after last player */
2400 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2401 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2403 int element = Feld[ExitX][ExitY];
2405 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2406 EL_SP_EXIT_CLOSING);
2408 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2411 /* Hero disappears */
2412 DrawLevelField(ExitX, ExitY);
2418 CloseDoor(DOOR_CLOSE_1);
2423 SaveTape(tape.level_nr); /* Ask to save tape */
2426 if (level_nr == leveldir_current->handicap_level)
2428 leveldir_current->handicap_level++;
2429 SaveLevelSetup_SeriesInfo();
2432 if (level_editor_test_game)
2433 local_player->score = -1; /* no highscore when playing from editor */
2434 else if (level_nr < leveldir_current->last_level)
2435 raise_level = TRUE; /* advance to next level */
2437 if ((hi_pos = NewHiScore()) >= 0)
2439 game_status = GAME_MODE_SCORES;
2440 DrawHallOfFame(hi_pos);
2449 game_status = GAME_MODE_MAIN;
2466 LoadScore(level_nr);
2468 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2469 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2472 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2474 if (local_player->score > highscore[k].Score)
2476 /* player has made it to the hall of fame */
2478 if (k < MAX_SCORE_ENTRIES - 1)
2480 int m = MAX_SCORE_ENTRIES - 1;
2483 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2484 if (!strcmp(setup.player_name, highscore[l].Name))
2486 if (m == k) /* player's new highscore overwrites his old one */
2490 for (l = m; l > k; l--)
2492 strcpy(highscore[l].Name, highscore[l - 1].Name);
2493 highscore[l].Score = highscore[l - 1].Score;
2500 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2501 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2502 highscore[k].Score = local_player->score;
2508 else if (!strncmp(setup.player_name, highscore[k].Name,
2509 MAX_PLAYER_NAME_LEN))
2510 break; /* player already there with a higher score */
2516 SaveScore(level_nr);
2521 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2523 if (player->GfxAction != action || player->GfxDir != dir)
2526 printf("Player frame reset! (%d => %d, %d => %d)\n",
2527 player->GfxAction, action, player->GfxDir, dir);
2530 player->GfxAction = action;
2531 player->GfxDir = dir;
2533 player->StepFrame = 0;
2537 static void ResetRandomAnimationValue(int x, int y)
2539 GfxRandom[x][y] = INIT_GFX_RANDOM();
2542 static void ResetGfxAnimation(int x, int y)
2545 GfxAction[x][y] = ACTION_DEFAULT;
2546 GfxDir[x][y] = MovDir[x][y];
2549 void InitMovingField(int x, int y, int direction)
2551 int element = Feld[x][y];
2552 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2553 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2557 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2558 ResetGfxAnimation(x, y);
2560 MovDir[newx][newy] = MovDir[x][y] = direction;
2561 GfxDir[x][y] = direction;
2563 if (Feld[newx][newy] == EL_EMPTY)
2564 Feld[newx][newy] = EL_BLOCKED;
2566 if (direction == MV_DOWN && CAN_FALL(element))
2567 GfxAction[x][y] = ACTION_FALLING;
2569 GfxAction[x][y] = ACTION_MOVING;
2571 GfxFrame[newx][newy] = GfxFrame[x][y];
2572 GfxRandom[newx][newy] = GfxRandom[x][y];
2573 GfxAction[newx][newy] = GfxAction[x][y];
2574 GfxDir[newx][newy] = GfxDir[x][y];
2577 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2579 int direction = MovDir[x][y];
2580 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2581 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2587 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2589 int oldx = x, oldy = y;
2590 int direction = MovDir[x][y];
2592 if (direction == MV_LEFT)
2594 else if (direction == MV_RIGHT)
2596 else if (direction == MV_UP)
2598 else if (direction == MV_DOWN)
2601 *comes_from_x = oldx;
2602 *comes_from_y = oldy;
2605 int MovingOrBlocked2Element(int x, int y)
2607 int element = Feld[x][y];
2609 if (element == EL_BLOCKED)
2613 Blocked2Moving(x, y, &oldx, &oldy);
2614 return Feld[oldx][oldy];
2620 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2622 /* like MovingOrBlocked2Element(), but if element is moving
2623 and (x,y) is the field the moving element is just leaving,
2624 return EL_BLOCKED instead of the element value */
2625 int element = Feld[x][y];
2627 if (IS_MOVING(x, y))
2629 if (element == EL_BLOCKED)
2633 Blocked2Moving(x, y, &oldx, &oldy);
2634 return Feld[oldx][oldy];
2643 static void RemoveField(int x, int y)
2645 Feld[x][y] = EL_EMPTY;
2652 ChangeDelay[x][y] = 0;
2653 ChangePage[x][y] = -1;
2654 Pushed[x][y] = FALSE;
2657 ExplodeField[x][y] = EX_TYPE_NONE;
2660 GfxElement[x][y] = EL_UNDEFINED;
2661 GfxAction[x][y] = ACTION_DEFAULT;
2662 GfxDir[x][y] = MV_NO_MOVING;
2665 void RemoveMovingField(int x, int y)
2667 int oldx = x, oldy = y, newx = x, newy = y;
2668 int element = Feld[x][y];
2669 int next_element = EL_UNDEFINED;
2671 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2674 if (IS_MOVING(x, y))
2676 Moving2Blocked(x, y, &newx, &newy);
2678 if (Feld[newx][newy] != EL_BLOCKED)
2681 if (Feld[newx][newy] != EL_BLOCKED)
2683 /* element is moving, but target field is not free (blocked), but
2684 already occupied by something different (example: acid pool);
2685 in this case, only remove the moving field, but not the target */
2687 RemoveField(oldx, oldy);
2689 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2691 DrawLevelField(oldx, oldy);
2697 else if (element == EL_BLOCKED)
2699 Blocked2Moving(x, y, &oldx, &oldy);
2700 if (!IS_MOVING(oldx, oldy))
2704 if (element == EL_BLOCKED &&
2705 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2706 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2707 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2708 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2709 next_element = get_next_element(Feld[oldx][oldy]);
2711 RemoveField(oldx, oldy);
2712 RemoveField(newx, newy);
2714 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2716 if (next_element != EL_UNDEFINED)
2717 Feld[oldx][oldy] = next_element;
2719 DrawLevelField(oldx, oldy);
2720 DrawLevelField(newx, newy);
2723 void DrawDynamite(int x, int y)
2725 int sx = SCREENX(x), sy = SCREENY(y);
2726 int graphic = el2img(Feld[x][y]);
2729 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2732 if (IS_WALKABLE_INSIDE(Back[x][y]))
2736 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2737 else if (Store[x][y])
2738 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2740 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2743 if (Back[x][y] || Store[x][y])
2744 DrawGraphicThruMask(sx, sy, graphic, frame);
2746 DrawGraphic(sx, sy, graphic, frame);
2748 if (game.emulation == EMU_SUPAPLEX)
2749 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2750 else if (Store[x][y])
2751 DrawGraphicThruMask(sx, sy, graphic, frame);
2753 DrawGraphic(sx, sy, graphic, frame);
2757 void CheckDynamite(int x, int y)
2759 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2763 if (MovDelay[x][y] != 0)
2766 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2773 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2775 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2776 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2777 StopSound(SND_DYNAMITE_ACTIVE);
2779 StopSound(SND_DYNABOMB_ACTIVE);
2785 void DrawRelocatePlayer(struct PlayerInfo *player)
2787 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2788 boolean no_delay = (tape.warp_forward);
2789 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2790 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2791 int jx = player->jx;
2792 int jy = player->jy;
2794 if (level.instant_relocation)
2797 int offset = (setup.scroll_delay ? 3 : 0);
2799 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2801 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2802 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2803 local_player->jx - MIDPOSX);
2805 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2806 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2807 local_player->jy - MIDPOSY);
2811 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2812 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2813 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2815 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2816 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2817 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2819 /* don't scroll over playfield boundaries */
2820 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2821 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2823 /* don't scroll over playfield boundaries */
2824 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2825 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2828 scroll_x += (local_player->jx - old_jx);
2829 scroll_y += (local_player->jy - old_jy);
2831 /* don't scroll over playfield boundaries */
2832 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2833 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2835 /* don't scroll over playfield boundaries */
2836 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2837 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2840 RedrawPlayfield(TRUE, 0,0,0,0);
2846 int offset = (setup.scroll_delay ? 3 : 0);
2848 int scroll_xx = -999, scroll_yy = -999;
2850 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2852 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2855 int fx = FX, fy = FY;
2857 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2858 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2859 local_player->jx - MIDPOSX);
2861 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2862 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2863 local_player->jy - MIDPOSY);
2865 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2866 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2869 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2872 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2879 fx += dx * TILEX / 2;
2880 fy += dy * TILEY / 2;
2882 ScrollLevel(dx, dy);
2885 /* scroll in two steps of half tile size to make things smoother */
2886 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2888 Delay(wait_delay_value);
2890 /* scroll second step to align at full tile size */
2892 Delay(wait_delay_value);
2895 int scroll_xx = -999, scroll_yy = -999;
2897 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2899 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2902 int fx = FX, fy = FY;
2904 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2905 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2906 local_player->jx - MIDPOSX);
2908 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2909 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2910 local_player->jy - MIDPOSY);
2912 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2913 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2916 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2919 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2926 fx += dx * TILEX / 2;
2927 fy += dy * TILEY / 2;
2929 ScrollLevel(dx, dy);
2932 /* scroll in two steps of half tile size to make things smoother */
2933 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2935 Delay(wait_delay_value);
2937 /* scroll second step to align at full tile size */
2939 Delay(wait_delay_value);
2945 Delay(wait_delay_value);
2949 void RelocatePlayer(int jx, int jy, int el_player_raw)
2952 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2954 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2956 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2957 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2958 boolean no_delay = (tape.warp_forward);
2959 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2960 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2961 int old_jx = player->jx;
2962 int old_jy = player->jy;
2963 int old_element = Feld[old_jx][old_jy];
2964 int element = Feld[jx][jy];
2965 boolean player_relocated = (old_jx != jx || old_jy != jy);
2967 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2968 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2970 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2971 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2972 int leave_side_horiz = move_dir_horiz;
2973 int leave_side_vert = move_dir_vert;
2975 static int trigger_sides[4][2] =
2977 /* enter side leave side */
2978 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2979 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2980 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2981 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2983 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2984 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2985 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2986 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2988 int enter_side = enter_side_horiz | enter_side_vert;
2989 int leave_side = leave_side_horiz | leave_side_vert;
2991 if (player->GameOver) /* do not reanimate dead player */
2994 if (!player_relocated) /* no need to relocate the player */
2997 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2999 RemoveField(jx, jy); /* temporarily remove newly placed player */
3000 DrawLevelField(jx, jy);
3003 if (player->present)
3005 while (player->MovPos)
3007 ScrollPlayer(player, SCROLL_GO_ON);
3008 ScrollScreen(NULL, SCROLL_GO_ON);
3010 #if USE_NEW_MOVE_DELAY
3011 AdvanceFrameAndPlayerCounters(player->index_nr);
3019 Delay(wait_delay_value);
3022 DrawPlayer(player); /* needed here only to cleanup last field */
3023 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3025 player->is_moving = FALSE;
3029 if (IS_CUSTOM_ELEMENT(old_element))
3030 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3032 player->index_bit, leave_side);
3034 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3036 player->index_bit, leave_side);
3039 Feld[jx][jy] = el_player;
3040 InitPlayerField(jx, jy, el_player, TRUE);
3042 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3044 Feld[jx][jy] = element;
3045 InitField(jx, jy, FALSE);
3049 if (player == local_player) /* only visually relocate local player */
3050 DrawRelocatePlayer(player);
3054 TestIfHeroTouchesBadThing(jx, jy);
3055 TestIfPlayerTouchesCustomElement(jx, jy);
3059 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3064 /* needed to allow change of walkable custom element by entering player */
3065 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3066 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3068 /* needed to allow change of walkable custom element by entering player */
3069 Changed[jx][jy] = 0; /* allow another change */
3074 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3075 enter_side == MV_LEFT ? "left" :
3076 enter_side == MV_RIGHT ? "right" :
3077 enter_side == MV_UP ? "top" :
3078 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3082 if (IS_CUSTOM_ELEMENT(element))
3083 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3084 player->index_bit, enter_side);
3086 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3087 CE_OTHER_GETS_ENTERED,
3088 player->index_bit, enter_side);
3092 void Explode(int ex, int ey, int phase, int mode)
3099 /* !!! eliminate this variable !!! */
3100 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3105 int last_phase = num_phase * delay;
3106 int half_phase = (num_phase / 2) * delay;
3107 int first_phase_after_start = EX_PHASE_START + 1;
3111 if (game.explosions_delayed)
3113 ExplodeField[ex][ey] = mode;
3117 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3119 int center_element = Feld[ex][ey];
3122 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3126 /* --- This is only really needed (and now handled) in "Impact()". --- */
3127 /* do not explode moving elements that left the explode field in time */
3128 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3129 center_element == EL_EMPTY &&
3130 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3134 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3135 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3137 /* remove things displayed in background while burning dynamite */
3138 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3141 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3143 /* put moving element to center field (and let it explode there) */
3144 center_element = MovingOrBlocked2Element(ex, ey);
3145 RemoveMovingField(ex, ey);
3146 Feld[ex][ey] = center_element;
3152 last_phase = element_info[center_element].explosion_delay + 1;
3154 last_phase = element_info[center_element].explosion_delay;
3158 printf("::: %d -> %d\n", center_element, last_phase);
3162 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3164 int xx = x - ex + 1;
3165 int yy = y - ey + 1;
3170 if (!IN_LEV_FIELD(x, y) ||
3171 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3172 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3175 if (!IN_LEV_FIELD(x, y) ||
3176 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3180 if (!IN_LEV_FIELD(x, y) ||
3181 ((mode != EX_TYPE_NORMAL ||
3182 center_element == EL_AMOEBA_TO_DIAMOND) &&
3183 (x != ex || y != ey)))
3187 element = Feld[x][y];
3189 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3191 element = MovingOrBlocked2Element(x, y);
3193 if (!IS_EXPLOSION_PROOF(element))
3194 RemoveMovingField(x, y);
3200 if (IS_EXPLOSION_PROOF(element))
3203 /* indestructible elements can only explode in center (but not flames) */
3205 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3206 mode == EX_TYPE_BORDER)) ||
3207 element == EL_FLAMES)
3210 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3211 element == EL_FLAMES)
3217 if ((IS_INDESTRUCTIBLE(element) &&
3218 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3219 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3220 element == EL_FLAMES)
3225 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3226 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3227 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3229 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3232 if (IS_ACTIVE_BOMB(element))
3234 /* re-activate things under the bomb like gate or penguin */
3236 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3239 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3244 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3245 element_info[Feld[x][y]].token_name,
3246 Store[x][y], Store2[x][y]);
3253 /* save walkable background elements while explosion on same tile */
3255 if (IS_INDESTRUCTIBLE(element))
3256 Back[x][y] = element;
3260 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3261 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3262 Back[x][y] = element;
3264 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3265 (x != ex || y != ey))
3266 Back[x][y] = element;
3269 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3270 Back[x][y] = element;
3274 /* ignite explodable elements reached by other explosion */
3275 if (element == EL_EXPLOSION)
3276 element = Store2[x][y];
3279 if (AmoebaNr[x][y] &&
3280 (element == EL_AMOEBA_FULL ||
3281 element == EL_BD_AMOEBA ||
3282 element == EL_AMOEBA_GROWING))
3284 AmoebaCnt[AmoebaNr[x][y]]--;
3285 AmoebaCnt2[AmoebaNr[x][y]]--;
3291 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3293 switch(StorePlayer[ex][ey])
3296 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3299 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3302 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3306 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3311 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3312 Store[x][y] = EL_EMPTY;
3314 if (game.emulation == EMU_SUPAPLEX)
3315 Store[x][y] = EL_EMPTY;
3318 else if (center_element == EL_MOLE)
3319 Store[x][y] = EL_EMERALD_RED;
3320 else if (center_element == EL_PENGUIN)
3321 Store[x][y] = EL_EMERALD_PURPLE;
3322 else if (center_element == EL_BUG)
3323 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3324 else if (center_element == EL_BD_BUTTERFLY)
3325 Store[x][y] = EL_BD_DIAMOND;
3326 else if (center_element == EL_SP_ELECTRON)
3327 Store[x][y] = EL_SP_INFOTRON;
3328 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3329 Store[x][y] = level.amoeba_content;
3330 else if (center_element == EL_YAMYAM)
3331 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3332 else if (IS_CUSTOM_ELEMENT(center_element) &&
3333 element_info[center_element].content[xx][yy] != EL_EMPTY)
3334 Store[x][y] = element_info[center_element].content[xx][yy];
3335 else if (element == EL_WALL_EMERALD)
3336 Store[x][y] = EL_EMERALD;
3337 else if (element == EL_WALL_DIAMOND)
3338 Store[x][y] = EL_DIAMOND;
3339 else if (element == EL_WALL_BD_DIAMOND)
3340 Store[x][y] = EL_BD_DIAMOND;
3341 else if (element == EL_WALL_EMERALD_YELLOW)
3342 Store[x][y] = EL_EMERALD_YELLOW;
3343 else if (element == EL_WALL_EMERALD_RED)
3344 Store[x][y] = EL_EMERALD_RED;
3345 else if (element == EL_WALL_EMERALD_PURPLE)
3346 Store[x][y] = EL_EMERALD_PURPLE;
3347 else if (element == EL_WALL_PEARL)
3348 Store[x][y] = EL_PEARL;
3349 else if (element == EL_WALL_CRYSTAL)
3350 Store[x][y] = EL_CRYSTAL;
3351 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3352 Store[x][y] = element_info[element].content[1][1];
3354 Store[x][y] = EL_EMPTY;
3356 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3357 center_element == EL_AMOEBA_TO_DIAMOND)
3358 Store2[x][y] = element;
3361 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3362 element_info[Store2[x][y]].token_name);
3366 if (AmoebaNr[x][y] &&
3367 (element == EL_AMOEBA_FULL ||
3368 element == EL_BD_AMOEBA ||
3369 element == EL_AMOEBA_GROWING))
3371 AmoebaCnt[AmoebaNr[x][y]]--;
3372 AmoebaCnt2[AmoebaNr[x][y]]--;
3378 MovDir[x][y] = MovPos[x][y] = 0;
3379 GfxDir[x][y] = MovDir[x][y];
3384 Feld[x][y] = EL_EXPLOSION;
3386 GfxElement[x][y] = center_element;
3388 GfxElement[x][y] = EL_UNDEFINED;
3391 ExplodePhase[x][y] = 1;
3393 ExplodeDelay[x][y] = last_phase;
3398 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3400 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3407 if (center_element == EL_YAMYAM)
3408 game.yamyam_content_nr =
3409 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3412 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3413 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3427 GfxFrame[x][y] = 0; /* restart explosion animation */
3431 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3435 last_phase = ExplodeDelay[x][y];
3438 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3442 /* activate this even in non-DEBUG version until cause for crash in
3443 getGraphicAnimationFrame() (see below) is found and eliminated */
3447 if (GfxElement[x][y] == EL_UNDEFINED)
3450 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3451 printf("Explode(): This should never happen!\n");
3454 GfxElement[x][y] = EL_EMPTY;
3460 border_element = Store2[x][y];
3462 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3463 border_element = StorePlayer[x][y];
3465 if (IS_PLAYER(x, y))
3466 border_element = StorePlayer[x][y];
3470 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3471 element_info[border_element].token_name, Store2[x][y]);
3475 printf("::: phase == %d\n", phase);
3478 if (phase == element_info[border_element].ignition_delay ||
3479 phase == last_phase)
3481 boolean border_explosion = FALSE;
3485 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3486 !PLAYER_EXPLOSION_PROTECTED(x, y))
3488 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3491 if (IS_PLAYER(x, y))
3494 KillHeroUnlessExplosionProtected(x, y);
3495 border_explosion = TRUE;
3498 if (phase == last_phase)
3499 printf("::: IS_PLAYER\n");
3502 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3505 printf("::: %d,%d: %d %s\n", x, y, border_element,
3506 element_info[border_element].token_name);
3509 Feld[x][y] = Store2[x][y];
3512 border_explosion = TRUE;
3515 if (phase == last_phase)
3516 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3519 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3521 AmoebeUmwandeln(x, y);
3523 border_explosion = TRUE;
3526 if (phase == last_phase)
3527 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3528 element_info[border_element].explosion_delay,
3529 element_info[border_element].ignition_delay,
3535 /* if an element just explodes due to another explosion (chain-reaction),
3536 do not immediately end the new explosion when it was the last frame of
3537 the explosion (as it would be done in the following "if"-statement!) */
3538 if (border_explosion && phase == last_phase)
3545 if (phase == first_phase_after_start)
3547 int element = Store2[x][y];
3549 if (element == EL_BLACK_ORB)
3551 Feld[x][y] = Store2[x][y];
3556 else if (phase == half_phase)
3558 int element = Store2[x][y];
3560 if (IS_PLAYER(x, y))
3561 KillHeroUnlessExplosionProtected(x, y);
3562 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3564 Feld[x][y] = Store2[x][y];
3568 else if (element == EL_AMOEBA_TO_DIAMOND)
3569 AmoebeUmwandeln(x, y);
3573 if (phase == last_phase)
3578 printf("::: done: phase == %d\n", phase);
3582 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3585 element = Feld[x][y] = Store[x][y];
3586 Store[x][y] = Store2[x][y] = 0;
3587 GfxElement[x][y] = EL_UNDEFINED;
3589 /* player can escape from explosions and might therefore be still alive */
3590 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3591 element <= EL_PLAYER_IS_EXPLODING_4)
3592 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3594 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3595 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3596 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3599 /* restore probably existing indestructible background element */
3600 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3601 element = Feld[x][y] = Back[x][y];
3604 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3605 GfxDir[x][y] = MV_NO_MOVING;
3606 ChangeDelay[x][y] = 0;
3607 ChangePage[x][y] = -1;
3610 InitField_WithBug2(x, y, FALSE);
3612 InitField(x, y, FALSE);
3614 /* !!! not needed !!! */
3616 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3617 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3620 if (CAN_MOVE(element))
3625 DrawLevelField(x, y);
3627 TestIfElementTouchesCustomElement(x, y);
3629 if (GFX_CRUMBLED(element))
3630 DrawLevelFieldCrumbledSandNeighbours(x, y);
3632 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3633 StorePlayer[x][y] = 0;
3635 if (ELEM_IS_PLAYER(element))
3636 RelocatePlayer(x, y, element);
3639 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3641 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3645 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3647 int stored = Store[x][y];
3648 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3649 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3653 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3655 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3659 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3663 printf("::: %d / %d [%d - %d]\n",
3664 GfxFrame[x][y], phase - delay, phase, delay);
3668 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3669 element_info[GfxElement[x][y]].token_name,
3674 DrawLevelFieldCrumbledSand(x, y);
3676 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3678 DrawLevelElement(x, y, Back[x][y]);
3679 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3681 else if (IS_WALKABLE_UNDER(Back[x][y]))
3683 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3684 DrawLevelElementThruMask(x, y, Back[x][y]);
3686 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3687 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3691 void DynaExplode(int ex, int ey)
3694 int dynabomb_element = Feld[ex][ey];
3695 int dynabomb_size = 1;
3696 boolean dynabomb_xl = FALSE;
3697 struct PlayerInfo *player;
3698 static int xy[4][2] =
3706 if (IS_ACTIVE_BOMB(dynabomb_element))
3708 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3709 dynabomb_size = player->dynabomb_size;
3710 dynabomb_xl = player->dynabomb_xl;
3711 player->dynabombs_left++;
3714 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3716 for (i = 0; i < NUM_DIRECTIONS; i++)
3718 for (j = 1; j <= dynabomb_size; j++)
3720 int x = ex + j * xy[i][0];
3721 int y = ey + j * xy[i][1];
3724 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3727 element = Feld[x][y];
3729 /* do not restart explosions of fields with active bombs */
3730 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3733 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3737 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3738 !IS_DIGGABLE(element) && !dynabomb_xl)
3741 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3742 !CAN_GROW_INTO(element) && !dynabomb_xl)
3746 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3747 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3748 element != EL_SAND && !dynabomb_xl)
3755 void Bang(int x, int y)
3758 int element = MovingOrBlocked2Element(x, y);
3760 int element = Feld[x][y];
3764 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3766 if (IS_PLAYER(x, y))
3769 struct PlayerInfo *player = PLAYERINFO(x, y);
3771 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3772 player->element_nr);
3777 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3779 if (game.emulation == EMU_SUPAPLEX)
3780 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3782 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3787 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3795 case EL_BD_BUTTERFLY:
3798 case EL_DARK_YAMYAM:
3802 RaiseScoreElement(element);
3803 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3805 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3806 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3807 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3808 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3809 case EL_DYNABOMB_INCREASE_NUMBER:
3810 case EL_DYNABOMB_INCREASE_SIZE:
3811 case EL_DYNABOMB_INCREASE_POWER:
3816 case EL_LAMP_ACTIVE:
3818 case EL_AMOEBA_TO_DIAMOND:
3820 if (IS_PLAYER(x, y))
3821 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3823 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3827 if (element_info[element].explosion_type == EXPLODES_CROSS)
3829 if (CAN_EXPLODE_CROSS(element))
3832 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3837 else if (element_info[element].explosion_type == EXPLODES_1X1)
3839 else if (CAN_EXPLODE_1X1(element))
3841 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3843 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3847 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3850 void SplashAcid(int x, int y)
3853 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3854 (!IN_LEV_FIELD(x - 1, y - 2) ||
3855 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3856 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3858 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3859 (!IN_LEV_FIELD(x + 1, y - 2) ||
3860 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3861 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3863 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3865 /* input: position of element entering acid (obsolete) */
3867 int element = Feld[x][y];
3869 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3872 if (element != EL_ACID_SPLASH_LEFT &&
3873 element != EL_ACID_SPLASH_RIGHT)
3875 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3877 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3878 (!IN_LEV_FIELD(x - 1, y - 1) ||
3879 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3880 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3882 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3883 (!IN_LEV_FIELD(x + 1, y - 1) ||
3884 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3885 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3890 static void InitBeltMovement()
3892 static int belt_base_element[4] =
3894 EL_CONVEYOR_BELT_1_LEFT,
3895 EL_CONVEYOR_BELT_2_LEFT,
3896 EL_CONVEYOR_BELT_3_LEFT,
3897 EL_CONVEYOR_BELT_4_LEFT
3899 static int belt_base_active_element[4] =
3901 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3902 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3903 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3904 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3909 /* set frame order for belt animation graphic according to belt direction */
3910 for (i = 0; i < NUM_BELTS; i++)
3914 for (j = 0; j < NUM_BELT_PARTS; j++)
3916 int element = belt_base_active_element[belt_nr] + j;
3917 int graphic = el2img(element);
3919 if (game.belt_dir[i] == MV_LEFT)
3920 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3922 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3926 for (y = 0; y < lev_fieldy; y++)
3928 for (x = 0; x < lev_fieldx; x++)
3930 int element = Feld[x][y];
3932 for (i = 0; i < NUM_BELTS; i++)
3934 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3936 int e_belt_nr = getBeltNrFromBeltElement(element);
3939 if (e_belt_nr == belt_nr)
3941 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3943 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3951 static void ToggleBeltSwitch(int x, int y)
3953 static int belt_base_element[4] =
3955 EL_CONVEYOR_BELT_1_LEFT,
3956 EL_CONVEYOR_BELT_2_LEFT,
3957 EL_CONVEYOR_BELT_3_LEFT,
3958 EL_CONVEYOR_BELT_4_LEFT
3960 static int belt_base_active_element[4] =
3962 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3963 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3964 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3965 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3967 static int belt_base_switch_element[4] =
3969 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3970 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3971 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3972 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3974 static int belt_move_dir[4] =
3982 int element = Feld[x][y];
3983 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3984 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3985 int belt_dir = belt_move_dir[belt_dir_nr];
3988 if (!IS_BELT_SWITCH(element))
3991 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3992 game.belt_dir[belt_nr] = belt_dir;
3994 if (belt_dir_nr == 3)
3997 /* set frame order for belt animation graphic according to belt direction */
3998 for (i = 0; i < NUM_BELT_PARTS; i++)
4000 int element = belt_base_active_element[belt_nr] + i;
4001 int graphic = el2img(element);
4003 if (belt_dir == MV_LEFT)
4004 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4006 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4009 for (yy = 0; yy < lev_fieldy; yy++)
4011 for (xx = 0; xx < lev_fieldx; xx++)
4013 int element = Feld[xx][yy];
4015 if (IS_BELT_SWITCH(element))
4017 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4019 if (e_belt_nr == belt_nr)
4021 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4022 DrawLevelField(xx, yy);
4025 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4027 int e_belt_nr = getBeltNrFromBeltElement(element);
4029 if (e_belt_nr == belt_nr)
4031 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4033 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4034 DrawLevelField(xx, yy);
4037 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4039 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4041 if (e_belt_nr == belt_nr)
4043 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4045 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4046 DrawLevelField(xx, yy);
4053 static void ToggleSwitchgateSwitch(int x, int y)
4057 game.switchgate_pos = !game.switchgate_pos;
4059 for (yy = 0; yy < lev_fieldy; yy++)
4061 for (xx = 0; xx < lev_fieldx; xx++)
4063 int element = Feld[xx][yy];
4065 if (element == EL_SWITCHGATE_SWITCH_UP ||
4066 element == EL_SWITCHGATE_SWITCH_DOWN)
4068 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4069 DrawLevelField(xx, yy);
4071 else if (element == EL_SWITCHGATE_OPEN ||
4072 element == EL_SWITCHGATE_OPENING)
4074 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4076 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4078 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4081 else if (element == EL_SWITCHGATE_CLOSED ||
4082 element == EL_SWITCHGATE_CLOSING)
4084 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4086 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4088 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4095 static int getInvisibleActiveFromInvisibleElement(int element)
4097 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4098 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4099 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4103 static int getInvisibleFromInvisibleActiveElement(int element)
4105 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4106 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4107 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4111 static void RedrawAllLightSwitchesAndInvisibleElements()
4115 for (y = 0; y < lev_fieldy; y++)
4117 for (x = 0; x < lev_fieldx; x++)
4119 int element = Feld[x][y];
4121 if (element == EL_LIGHT_SWITCH &&
4122 game.light_time_left > 0)
4124 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4125 DrawLevelField(x, y);
4127 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4128 game.light_time_left == 0)
4130 Feld[x][y] = EL_LIGHT_SWITCH;
4131 DrawLevelField(x, y);
4133 else if (element == EL_INVISIBLE_STEELWALL ||
4134 element == EL_INVISIBLE_WALL ||
4135 element == EL_INVISIBLE_SAND)
4137 if (game.light_time_left > 0)
4138 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4140 DrawLevelField(x, y);
4142 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4143 element == EL_INVISIBLE_WALL_ACTIVE ||
4144 element == EL_INVISIBLE_SAND_ACTIVE)
4146 if (game.light_time_left == 0)
4147 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4149 DrawLevelField(x, y);
4155 static void ToggleLightSwitch(int x, int y)
4157 int element = Feld[x][y];
4159 game.light_time_left =
4160 (element == EL_LIGHT_SWITCH ?
4161 level.time_light * FRAMES_PER_SECOND : 0);
4163 RedrawAllLightSwitchesAndInvisibleElements();
4166 static void ActivateTimegateSwitch(int x, int y)
4170 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4172 for (yy = 0; yy < lev_fieldy; yy++)
4174 for (xx = 0; xx < lev_fieldx; xx++)
4176 int element = Feld[xx][yy];
4178 if (element == EL_TIMEGATE_CLOSED ||
4179 element == EL_TIMEGATE_CLOSING)
4181 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4182 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4186 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4188 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4189 DrawLevelField(xx, yy);
4196 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4199 inline static int getElementMoveStepsize(int x, int y)
4201 int element = Feld[x][y];
4202 int direction = MovDir[x][y];
4203 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4204 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4205 int horiz_move = (dx != 0);
4206 int sign = (horiz_move ? dx : dy);
4207 int step = sign * element_info[element].move_stepsize;
4209 /* special values for move stepsize for spring and things on conveyor belt */
4213 if (element == EL_SPRING)
4214 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4215 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4216 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4217 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4219 if (CAN_FALL(element) &&
4220 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4221 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4222 else if (element == EL_SPRING)
4223 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4230 void Impact(int x, int y)
4232 boolean lastline = (y == lev_fieldy-1);
4233 boolean object_hit = FALSE;
4234 boolean impact = (lastline || object_hit);
4235 int element = Feld[x][y];
4236 int smashed = EL_STEELWALL;
4238 if (!lastline) /* check if element below was hit */
4240 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4243 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4244 MovDir[x][y + 1] != MV_DOWN ||
4245 MovPos[x][y + 1] <= TILEY / 2));
4248 object_hit = !IS_FREE(x, y + 1);
4251 /* do not smash moving elements that left the smashed field in time */
4252 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4253 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4257 smashed = MovingOrBlocked2Element(x, y + 1);
4259 impact = (lastline || object_hit);
4262 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4264 SplashAcid(x, y + 1);
4268 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4269 /* only reset graphic animation if graphic really changes after impact */
4271 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4273 ResetGfxAnimation(x, y);
4274 DrawLevelField(x, y);
4277 if (impact && CAN_EXPLODE_IMPACT(element))
4282 else if (impact && element == EL_PEARL)
4284 ResetGfxAnimation(x, y);
4286 Feld[x][y] = EL_PEARL_BREAKING;
4287 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4290 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4292 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4297 if (impact && element == EL_AMOEBA_DROP)
4299 if (object_hit && IS_PLAYER(x, y + 1))
4300 KillHeroUnlessEnemyProtected(x, y + 1);
4301 else if (object_hit && smashed == EL_PENGUIN)
4305 Feld[x][y] = EL_AMOEBA_GROWING;
4306 Store[x][y] = EL_AMOEBA_WET;
4308 ResetRandomAnimationValue(x, y);
4313 if (object_hit) /* check which object was hit */
4315 if (CAN_PASS_MAGIC_WALL(element) &&
4316 (smashed == EL_MAGIC_WALL ||
4317 smashed == EL_BD_MAGIC_WALL))
4320 int activated_magic_wall =
4321 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4322 EL_BD_MAGIC_WALL_ACTIVE);
4324 /* activate magic wall / mill */
4325 for (yy = 0; yy < lev_fieldy; yy++)
4326 for (xx = 0; xx < lev_fieldx; xx++)
4327 if (Feld[xx][yy] == smashed)
4328 Feld[xx][yy] = activated_magic_wall;
4330 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4331 game.magic_wall_active = TRUE;
4333 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4334 SND_MAGIC_WALL_ACTIVATING :
4335 SND_BD_MAGIC_WALL_ACTIVATING));
4338 if (IS_PLAYER(x, y + 1))
4340 if (CAN_SMASH_PLAYER(element))
4342 KillHeroUnlessEnemyProtected(x, y + 1);
4346 else if (smashed == EL_PENGUIN)
4348 if (CAN_SMASH_PLAYER(element))
4354 else if (element == EL_BD_DIAMOND)
4356 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4362 else if (((element == EL_SP_INFOTRON ||
4363 element == EL_SP_ZONK) &&
4364 (smashed == EL_SP_SNIKSNAK ||
4365 smashed == EL_SP_ELECTRON ||
4366 smashed == EL_SP_DISK_ORANGE)) ||
4367 (element == EL_SP_INFOTRON &&
4368 smashed == EL_SP_DISK_YELLOW))
4374 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4380 else if (CAN_SMASH_EVERYTHING(element))
4382 if (IS_CLASSIC_ENEMY(smashed) ||
4383 CAN_EXPLODE_SMASHED(smashed))
4388 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4390 if (smashed == EL_LAMP ||
4391 smashed == EL_LAMP_ACTIVE)
4396 else if (smashed == EL_NUT)
4398 Feld[x][y + 1] = EL_NUT_BREAKING;
4399 PlayLevelSound(x, y, SND_NUT_BREAKING);
4400 RaiseScoreElement(EL_NUT);
4403 else if (smashed == EL_PEARL)
4405 ResetGfxAnimation(x, y);
4407 Feld[x][y + 1] = EL_PEARL_BREAKING;
4408 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4411 else if (smashed == EL_DIAMOND)
4413 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4414 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4417 else if (IS_BELT_SWITCH(smashed))
4419 ToggleBeltSwitch(x, y + 1);
4421 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4422 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4424 ToggleSwitchgateSwitch(x, y + 1);
4426 else if (smashed == EL_LIGHT_SWITCH ||
4427 smashed == EL_LIGHT_SWITCH_ACTIVE)
4429 ToggleLightSwitch(x, y + 1);
4434 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4437 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4440 /* !!! TEST ONLY !!! */
4441 CheckElementChangeBySide(x, y + 1, smashed, element,
4442 CE_SWITCHED, CH_SIDE_TOP);
4443 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4444 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4446 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4447 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4448 CheckElementChangeBySide(x, y + 1, smashed, element,
4449 CE_SWITCHED, CH_SIDE_TOP);
4455 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4460 /* play sound of magic wall / mill */
4462 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4463 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4465 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4466 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4467 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4468 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4473 /* play sound of object that hits the ground */
4474 if (lastline || object_hit)
4475 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4478 inline static void TurnRoundExt(int x, int y)
4490 { 0, 0 }, { 0, 0 }, { 0, 0 },
4495 int left, right, back;
4499 { MV_DOWN, MV_UP, MV_RIGHT },
4500 { MV_UP, MV_DOWN, MV_LEFT },
4502 { MV_LEFT, MV_RIGHT, MV_DOWN },
4506 { MV_RIGHT, MV_LEFT, MV_UP }
4509 int element = Feld[x][y];
4510 int move_pattern = element_info[element].move_pattern;
4512 int old_move_dir = MovDir[x][y];
4513 int left_dir = turn[old_move_dir].left;
4514 int right_dir = turn[old_move_dir].right;
4515 int back_dir = turn[old_move_dir].back;
4517 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4518 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4519 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4520 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4522 int left_x = x + left_dx, left_y = y + left_dy;
4523 int right_x = x + right_dx, right_y = y + right_dy;
4524 int move_x = x + move_dx, move_y = y + move_dy;
4528 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4530 TestIfBadThingTouchesOtherBadThing(x, y);
4532 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4533 MovDir[x][y] = right_dir;
4534 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4535 MovDir[x][y] = left_dir;
4537 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4539 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4543 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4544 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4546 TestIfBadThingTouchesOtherBadThing(x, y);
4548 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4549 MovDir[x][y] = left_dir;
4550 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4551 MovDir[x][y] = right_dir;
4553 if ((element == EL_SPACESHIP ||
4554 element == EL_SP_SNIKSNAK ||
4555 element == EL_SP_ELECTRON)
4556 && MovDir[x][y] != old_move_dir)
4558 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4562 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4564 TestIfBadThingTouchesOtherBadThing(x, y);
4566 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4567 MovDir[x][y] = left_dir;
4568 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4569 MovDir[x][y] = right_dir;
4571 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4573 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4576 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4578 TestIfBadThingTouchesOtherBadThing(x, y);
4580 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4581 MovDir[x][y] = left_dir;
4582 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4583 MovDir[x][y] = right_dir;
4585 if (MovDir[x][y] != old_move_dir)
4589 else if (element == EL_YAMYAM)
4591 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4592 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4594 if (can_turn_left && can_turn_right)
4595 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4596 else if (can_turn_left)
4597 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4598 else if (can_turn_right)
4599 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4601 MovDir[x][y] = back_dir;
4603 MovDelay[x][y] = 16 + 16 * RND(3);
4605 else if (element == EL_DARK_YAMYAM)
4607 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4609 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4612 if (can_turn_left && can_turn_right)
4613 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4614 else if (can_turn_left)
4615 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4616 else if (can_turn_right)
4617 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4619 MovDir[x][y] = back_dir;
4621 MovDelay[x][y] = 16 + 16 * RND(3);
4623 else if (element == EL_PACMAN)
4625 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4626 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4628 if (can_turn_left && can_turn_right)
4629 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4630 else if (can_turn_left)
4631 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4632 else if (can_turn_right)
4633 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4635 MovDir[x][y] = back_dir;
4637 MovDelay[x][y] = 6 + RND(40);
4639 else if (element == EL_PIG)
4641 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4642 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4643 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4644 boolean should_turn_left, should_turn_right, should_move_on;
4646 int rnd = RND(rnd_value);
4648 should_turn_left = (can_turn_left &&
4650 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4651 y + back_dy + left_dy)));
4652 should_turn_right = (can_turn_right &&
4654 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4655 y + back_dy + right_dy)));
4656 should_move_on = (can_move_on &&
4659 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4660 y + move_dy + left_dy) ||
4661 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4662 y + move_dy + right_dy)));
4664 if (should_turn_left || should_turn_right || should_move_on)
4666 if (should_turn_left && should_turn_right && should_move_on)
4667 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4668 rnd < 2 * rnd_value / 3 ? right_dir :
4670 else if (should_turn_left && should_turn_right)
4671 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4672 else if (should_turn_left && should_move_on)
4673 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4674 else if (should_turn_right && should_move_on)
4675 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4676 else if (should_turn_left)
4677 MovDir[x][y] = left_dir;
4678 else if (should_turn_right)
4679 MovDir[x][y] = right_dir;
4680 else if (should_move_on)
4681 MovDir[x][y] = old_move_dir;
4683 else if (can_move_on && rnd > rnd_value / 8)
4684 MovDir[x][y] = old_move_dir;
4685 else if (can_turn_left && can_turn_right)
4686 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4687 else if (can_turn_left && rnd > rnd_value / 8)
4688 MovDir[x][y] = left_dir;
4689 else if (can_turn_right && rnd > rnd_value/8)
4690 MovDir[x][y] = right_dir;
4692 MovDir[x][y] = back_dir;
4694 xx = x + move_xy[MovDir[x][y]].x;
4695 yy = y + move_xy[MovDir[x][y]].y;
4698 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4699 if (!IN_LEV_FIELD(xx, yy) ||
4700 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4701 MovDir[x][y] = old_move_dir;
4703 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4704 MovDir[x][y] = old_move_dir;
4709 else if (element == EL_DRAGON)
4711 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4712 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4713 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4715 int rnd = RND(rnd_value);
4718 if (FrameCounter < 1 && x == 0 && y == 29)
4719 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4722 if (can_move_on && rnd > rnd_value / 8)
4723 MovDir[x][y] = old_move_dir;
4724 else if (can_turn_left && can_turn_right)
4725 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4726 else if (can_turn_left && rnd > rnd_value / 8)
4727 MovDir[x][y] = left_dir;
4728 else if (can_turn_right && rnd > rnd_value / 8)
4729 MovDir[x][y] = right_dir;
4731 MovDir[x][y] = back_dir;
4733 xx = x + move_xy[MovDir[x][y]].x;
4734 yy = y + move_xy[MovDir[x][y]].y;
4737 if (FrameCounter < 1 && x == 0 && y == 29)
4738 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4739 xx, yy, Feld[xx][yy],
4744 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4745 MovDir[x][y] = old_move_dir;
4747 if (!IS_FREE(xx, yy))
4748 MovDir[x][y] = old_move_dir;
4752 if (FrameCounter < 1 && x == 0 && y == 29)
4753 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4758 else if (element == EL_MOLE)
4760 boolean can_move_on =
4761 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4762 IS_AMOEBOID(Feld[move_x][move_y]) ||
4763 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4766 boolean can_turn_left =
4767 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4768 IS_AMOEBOID(Feld[left_x][left_y])));
4770 boolean can_turn_right =
4771 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4772 IS_AMOEBOID(Feld[right_x][right_y])));
4774 if (can_turn_left && can_turn_right)
4775 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4776 else if (can_turn_left)
4777 MovDir[x][y] = left_dir;
4779 MovDir[x][y] = right_dir;
4782 if (MovDir[x][y] != old_move_dir)
4785 else if (element == EL_BALLOON)
4787 MovDir[x][y] = game.balloon_dir;
4790 else if (element == EL_SPRING)
4793 if (MovDir[x][y] & MV_HORIZONTAL &&
4794 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4795 MovDir[x][y] = MV_NO_MOVING;
4797 if (MovDir[x][y] & MV_HORIZONTAL &&
4798 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4799 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4800 MovDir[x][y] = MV_NO_MOVING;
4805 else if (element == EL_ROBOT ||
4806 element == EL_SATELLITE ||
4807 element == EL_PENGUIN)
4809 int attr_x = -1, attr_y = -1;
4820 for (i = 0; i < MAX_PLAYERS; i++)
4822 struct PlayerInfo *player = &stored_player[i];
4823 int jx = player->jx, jy = player->jy;
4825 if (!player->active)
4829 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4838 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4839 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4840 game.engine_version < VERSION_IDENT(3,1,0,0)))
4842 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4849 if (element == EL_PENGUIN)
4852 static int xy[4][2] =
4860 for (i = 0; i < NUM_DIRECTIONS; i++)
4862 int ex = x + xy[i][0];
4863 int ey = y + xy[i][1];
4865 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4874 MovDir[x][y] = MV_NO_MOVING;
4876 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4877 else if (attr_x > x)
4878 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4880 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4881 else if (attr_y > y)
4882 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4884 if (element == EL_ROBOT)
4888 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4889 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4890 Moving2Blocked(x, y, &newx, &newy);
4892 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4893 MovDelay[x][y] = 8 + 8 * !RND(3);
4895 MovDelay[x][y] = 16;
4897 else if (element == EL_PENGUIN)
4903 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4905 boolean first_horiz = RND(2);
4906 int new_move_dir = MovDir[x][y];
4909 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4910 Moving2Blocked(x, y, &newx, &newy);
4912 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4916 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4917 Moving2Blocked(x, y, &newx, &newy);
4919 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4922 MovDir[x][y] = old_move_dir;
4926 else /* (element == EL_SATELLITE) */
4932 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4934 boolean first_horiz = RND(2);
4935 int new_move_dir = MovDir[x][y];
4938 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4939 Moving2Blocked(x, y, &newx, &newy);
4941 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4945 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4946 Moving2Blocked(x, y, &newx, &newy);
4948 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4951 MovDir[x][y] = old_move_dir;
4956 else if (move_pattern == MV_TURNING_LEFT ||
4957 move_pattern == MV_TURNING_RIGHT ||
4958 move_pattern == MV_TURNING_LEFT_RIGHT ||
4959 move_pattern == MV_TURNING_RIGHT_LEFT ||
4960 move_pattern == MV_TURNING_RANDOM ||
4961 move_pattern == MV_ALL_DIRECTIONS)
4963 boolean can_turn_left =
4964 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4965 boolean can_turn_right =
4966 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4968 if (move_pattern == MV_TURNING_LEFT)
4969 MovDir[x][y] = left_dir;
4970 else if (move_pattern == MV_TURNING_RIGHT)
4971 MovDir[x][y] = right_dir;
4972 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4973 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4974 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4975 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4976 else if (move_pattern == MV_TURNING_RANDOM)
4977 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4978 can_turn_right && !can_turn_left ? right_dir :
4979 RND(2) ? left_dir : right_dir);
4980 else if (can_turn_left && can_turn_right)
4981 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4982 else if (can_turn_left)
4983 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4984 else if (can_turn_right)
4985 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4987 MovDir[x][y] = back_dir;
4989 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4991 else if (move_pattern == MV_HORIZONTAL ||
4992 move_pattern == MV_VERTICAL)
4994 if (move_pattern & old_move_dir)
4995 MovDir[x][y] = back_dir;
4996 else if (move_pattern == MV_HORIZONTAL)
4997 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4998 else if (move_pattern == MV_VERTICAL)
4999 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5001 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5003 else if (move_pattern & MV_ANY_DIRECTION)
5005 MovDir[x][y] = move_pattern;
5006 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5008 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5010 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5011 MovDir[x][y] = left_dir;
5012 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5013 MovDir[x][y] = right_dir;
5015 if (MovDir[x][y] != old_move_dir)
5016 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5018 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5020 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5021 MovDir[x][y] = right_dir;
5022 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5023 MovDir[x][y] = left_dir;
5025 if (MovDir[x][y] != old_move_dir)
5026 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5028 else if (move_pattern == MV_TOWARDS_PLAYER ||
5029 move_pattern == MV_AWAY_FROM_PLAYER)
5031 int attr_x = -1, attr_y = -1;
5033 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5044 for (i = 0; i < MAX_PLAYERS; i++)
5046 struct PlayerInfo *player = &stored_player[i];
5047 int jx = player->jx, jy = player->jy;
5049 if (!player->active)
5053 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5061 MovDir[x][y] = MV_NO_MOVING;
5063 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5064 else if (attr_x > x)
5065 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5067 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5068 else if (attr_y > y)
5069 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5071 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5073 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5075 boolean first_horiz = RND(2);
5076 int new_move_dir = MovDir[x][y];
5079 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5080 Moving2Blocked(x, y, &newx, &newy);
5082 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5086 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5087 Moving2Blocked(x, y, &newx, &newy);
5089 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5092 MovDir[x][y] = old_move_dir;
5095 else if (move_pattern == MV_WHEN_PUSHED ||
5096 move_pattern == MV_WHEN_DROPPED)
5098 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5099 MovDir[x][y] = MV_NO_MOVING;
5103 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5105 static int test_xy[7][2] =
5115 static int test_dir[7] =
5125 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5126 int move_preference = -1000000; /* start with very low preference */
5127 int new_move_dir = MV_NO_MOVING;
5128 int start_test = RND(4);
5131 for (i = 0; i < NUM_DIRECTIONS; i++)
5133 int move_dir = test_dir[start_test + i];
5134 int move_dir_preference;
5136 xx = x + test_xy[start_test + i][0];
5137 yy = y + test_xy[start_test + i][1];
5139 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5140 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5142 new_move_dir = move_dir;
5147 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5150 move_dir_preference = -1 * RunnerVisit[xx][yy];
5151 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5152 move_dir_preference = PlayerVisit[xx][yy];
5154 if (move_dir_preference > move_preference)
5156 /* prefer field that has not been visited for the longest time */
5157 move_preference = move_dir_preference;
5158 new_move_dir = move_dir;
5160 else if (move_dir_preference == move_preference &&
5161 move_dir == old_move_dir)
5163 /* prefer last direction when all directions are preferred equally */
5164 move_preference = move_dir_preference;
5165 new_move_dir = move_dir;
5169 MovDir[x][y] = new_move_dir;
5170 if (old_move_dir != new_move_dir)
5173 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5181 static void TurnRound(int x, int y)
5183 int direction = MovDir[x][y];
5186 GfxDir[x][y] = MovDir[x][y];
5192 GfxDir[x][y] = MovDir[x][y];
5195 if (direction != MovDir[x][y])
5200 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5203 GfxAction[x][y] = ACTION_WAITING;
5207 static boolean JustBeingPushed(int x, int y)
5211 for (i = 0; i < MAX_PLAYERS; i++)
5213 struct PlayerInfo *player = &stored_player[i];
5215 if (player->active && player->is_pushing && player->MovPos)
5217 int next_jx = player->jx + (player->jx - player->last_jx);
5218 int next_jy = player->jy + (player->jy - player->last_jy);
5220 if (x == next_jx && y == next_jy)
5228 void StartMoving(int x, int y)
5231 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5233 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5234 int element = Feld[x][y];
5240 if (MovDelay[x][y] == 0)
5241 GfxAction[x][y] = ACTION_DEFAULT;
5243 /* !!! this should be handled more generic (not only for mole) !!! */
5244 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5245 GfxAction[x][y] = ACTION_DEFAULT;
5248 if (CAN_FALL(element) && y < lev_fieldy - 1)
5250 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5251 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5252 if (JustBeingPushed(x, y))
5255 if (element == EL_QUICKSAND_FULL)
5257 if (IS_FREE(x, y + 1))
5259 InitMovingField(x, y, MV_DOWN);
5260 started_moving = TRUE;
5262 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5263 Store[x][y] = EL_ROCK;
5265 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5267 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5270 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5272 if (!MovDelay[x][y])
5273 MovDelay[x][y] = TILEY + 1;
5282 Feld[x][y] = EL_QUICKSAND_EMPTY;
5283 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5284 Store[x][y + 1] = Store[x][y];
5287 PlayLevelSoundAction(x, y, ACTION_FILLING);
5289 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5293 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5294 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5296 InitMovingField(x, y, MV_DOWN);
5297 started_moving = TRUE;
5299 Feld[x][y] = EL_QUICKSAND_FILLING;
5300 Store[x][y] = element;
5302 PlayLevelSoundAction(x, y, ACTION_FILLING);
5304 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5307 else if (element == EL_MAGIC_WALL_FULL)
5309 if (IS_FREE(x, y + 1))
5311 InitMovingField(x, y, MV_DOWN);
5312 started_moving = TRUE;
5314 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5315 Store[x][y] = EL_CHANGED(Store[x][y]);
5317 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5319 if (!MovDelay[x][y])
5320 MovDelay[x][y] = TILEY/4 + 1;
5329 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5330 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5331 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5335 else if (element == EL_BD_MAGIC_WALL_FULL)
5337 if (IS_FREE(x, y + 1))
5339 InitMovingField(x, y, MV_DOWN);
5340 started_moving = TRUE;
5342 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5343 Store[x][y] = EL_CHANGED2(Store[x][y]);
5345 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5347 if (!MovDelay[x][y])
5348 MovDelay[x][y] = TILEY/4 + 1;
5357 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5358 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5359 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5363 else if (CAN_PASS_MAGIC_WALL(element) &&
5364 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5365 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5367 InitMovingField(x, y, MV_DOWN);
5368 started_moving = TRUE;
5371 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5372 EL_BD_MAGIC_WALL_FILLING);
5373 Store[x][y] = element;
5376 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5378 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5381 SplashAcid(x, y + 1);
5383 InitMovingField(x, y, MV_DOWN);
5384 started_moving = TRUE;
5386 Store[x][y] = EL_ACID;
5388 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5389 GfxAction[x][y + 1] = ACTION_ACTIVE;
5393 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5394 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5396 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5397 CAN_SMASH(element) && WasJustFalling[x][y] &&
5398 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5400 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5401 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5402 (Feld[x][y + 1] == EL_BLOCKED)))
5406 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5407 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5408 WasJustMoving[x][y] && !Pushed[x][y + 1])
5410 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5411 WasJustMoving[x][y])
5416 /* this is needed for a special case not covered by calling "Impact()"
5417 from "ContinueMoving()": if an element moves to a tile directly below
5418 another element which was just falling on that tile (which was empty
5419 in the previous frame), the falling element above would just stop
5420 instead of smashing the element below (in previous version, the above
5421 element was just checked for "moving" instead of "falling", resulting
5422 in incorrect smashes caused by horizontal movement of the above
5423 element; also, the case of the player being the element to smash was
5424 simply not covered here... :-/ ) */
5427 WasJustMoving[x][y] = 0;
5428 WasJustFalling[x][y] = 0;
5431 CheckCollision[x][y] = 0;
5434 if (IS_PLAYER(x, y + 1))
5435 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5440 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5442 if (MovDir[x][y] == MV_NO_MOVING)
5444 InitMovingField(x, y, MV_DOWN);
5445 started_moving = TRUE;
5448 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5450 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5451 MovDir[x][y] = MV_DOWN;
5453 InitMovingField(x, y, MV_DOWN);
5454 started_moving = TRUE;
5456 else if (element == EL_AMOEBA_DROP)
5458 Feld[x][y] = EL_AMOEBA_GROWING;
5459 Store[x][y] = EL_AMOEBA_WET;
5461 /* Store[x][y + 1] must be zero, because:
5462 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5465 #if OLD_GAME_BEHAVIOUR
5466 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5468 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5469 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5470 element != EL_DX_SUPABOMB)
5473 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5474 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5475 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5476 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5479 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5480 (IS_FREE(x - 1, y + 1) ||
5481 Feld[x - 1][y + 1] == EL_ACID));
5482 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5483 (IS_FREE(x + 1, y + 1) ||
5484 Feld[x + 1][y + 1] == EL_ACID));
5485 boolean can_fall_any = (can_fall_left || can_fall_right);
5486 boolean can_fall_both = (can_fall_left && can_fall_right);
5488 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5490 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5492 if (slippery_type == SLIPPERY_ONLY_LEFT)
5493 can_fall_right = FALSE;
5494 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5495 can_fall_left = FALSE;
5496 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5497 can_fall_right = FALSE;
5498 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5499 can_fall_left = FALSE;
5501 can_fall_any = (can_fall_left || can_fall_right);
5502 can_fall_both = (can_fall_left && can_fall_right);
5505 #if USE_NEW_SP_SLIPPERY
5506 /* !!! better use the same properties as for custom elements here !!! */
5507 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5508 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5510 can_fall_right = FALSE; /* slip down on left side */
5511 can_fall_both = FALSE;
5518 if (game.emulation == EMU_BOULDERDASH ||
5519 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5520 can_fall_right = FALSE; /* slip down on left side */
5522 can_fall_left = !(can_fall_right = RND(2));
5524 can_fall_both = FALSE;
5531 if (can_fall_both &&
5532 (game.emulation != EMU_BOULDERDASH &&
5533 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5534 can_fall_left = !(can_fall_right = RND(2));
5537 /* if not determined otherwise, prefer left side for slipping down */
5538 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5539 started_moving = TRUE;
5543 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5545 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5548 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5549 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5550 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5551 int belt_dir = game.belt_dir[belt_nr];
5553 if ((belt_dir == MV_LEFT && left_is_free) ||
5554 (belt_dir == MV_RIGHT && right_is_free))
5557 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5560 InitMovingField(x, y, belt_dir);
5561 started_moving = TRUE;
5564 Pushed[x][y] = TRUE;
5565 Pushed[nextx][y] = TRUE;
5568 GfxAction[x][y] = ACTION_DEFAULT;
5572 MovDir[x][y] = 0; /* if element was moving, stop it */
5577 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5579 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5581 if (CAN_MOVE(element) && !started_moving)
5584 int move_pattern = element_info[element].move_pattern;
5589 if (MovDir[x][y] == MV_NO_MOVING)
5591 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5592 x, y, element, element_info[element].token_name);
5593 printf("StartMoving(): This should never happen!\n");
5598 Moving2Blocked(x, y, &newx, &newy);
5601 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5604 if ((element == EL_SATELLITE ||
5605 element == EL_BALLOON ||
5606 element == EL_SPRING)
5607 && JustBeingPushed(x, y))
5614 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5615 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5617 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5618 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5619 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5623 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5624 element, element_info[element].token_name,
5625 WasJustMoving[x][y],
5626 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5627 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5628 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5629 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5633 WasJustMoving[x][y] = 0;
5636 CheckCollision[x][y] = 0;
5638 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5641 if (Feld[x][y] != element) /* element has changed */
5643 element = Feld[x][y];
5644 move_pattern = element_info[element].move_pattern;
5646 if (!CAN_MOVE(element))
5650 if (Feld[x][y] != element) /* element has changed */
5658 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5659 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5661 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5663 Moving2Blocked(x, y, &newx, &newy);
5664 if (Feld[newx][newy] == EL_BLOCKED)
5665 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5671 if (FrameCounter < 1 && x == 0 && y == 29)
5672 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5675 if (!MovDelay[x][y]) /* start new movement phase */
5677 /* all objects that can change their move direction after each step
5678 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5680 if (element != EL_YAMYAM &&
5681 element != EL_DARK_YAMYAM &&
5682 element != EL_PACMAN &&
5683 !(move_pattern & MV_ANY_DIRECTION) &&
5684 move_pattern != MV_TURNING_LEFT &&
5685 move_pattern != MV_TURNING_RIGHT &&
5686 move_pattern != MV_TURNING_LEFT_RIGHT &&
5687 move_pattern != MV_TURNING_RIGHT_LEFT &&
5688 move_pattern != MV_TURNING_RANDOM)
5693 if (FrameCounter < 1 && x == 0 && y == 29)
5694 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5697 if (MovDelay[x][y] && (element == EL_BUG ||
5698 element == EL_SPACESHIP ||
5699 element == EL_SP_SNIKSNAK ||
5700 element == EL_SP_ELECTRON ||
5701 element == EL_MOLE))
5702 DrawLevelField(x, y);
5706 if (MovDelay[x][y]) /* wait some time before next movement */
5711 if (element == EL_YAMYAM)
5714 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5715 DrawLevelElementAnimation(x, y, element);
5719 if (MovDelay[x][y]) /* element still has to wait some time */
5722 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5723 ResetGfxAnimation(x, y);
5727 if (GfxAction[x][y] != ACTION_WAITING)
5728 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5730 GfxAction[x][y] = ACTION_WAITING;
5734 if (element == EL_ROBOT ||
5736 element == EL_PACMAN ||
5738 element == EL_YAMYAM ||
5739 element == EL_DARK_YAMYAM)
5742 DrawLevelElementAnimation(x, y, element);
5744 DrawLevelElementAnimationIfNeeded(x, y, element);
5746 PlayLevelSoundAction(x, y, ACTION_WAITING);
5748 else if (element == EL_SP_ELECTRON)
5749 DrawLevelElementAnimationIfNeeded(x, y, element);
5750 else if (element == EL_DRAGON)
5753 int dir = MovDir[x][y];
5754 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5755 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5756 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5757 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5758 dir == MV_UP ? IMG_FLAMES_1_UP :
5759 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5760 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5763 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5766 GfxAction[x][y] = ACTION_ATTACKING;
5768 if (IS_PLAYER(x, y))
5769 DrawPlayerField(x, y);
5771 DrawLevelField(x, y);
5773 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5775 for (i = 1; i <= 3; i++)
5777 int xx = x + i * dx;
5778 int yy = y + i * dy;
5779 int sx = SCREENX(xx);
5780 int sy = SCREENY(yy);
5781 int flame_graphic = graphic + (i - 1);
5783 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5788 int flamed = MovingOrBlocked2Element(xx, yy);
5792 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5794 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5795 RemoveMovingField(xx, yy);
5797 RemoveField(xx, yy);
5799 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5802 RemoveMovingField(xx, yy);
5806 if (ChangeDelay[xx][yy])
5807 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5808 Feld[xx][yy] == EL_BLOCKED));
5812 ChangeDelay[xx][yy] = 0;
5814 Feld[xx][yy] = EL_FLAMES;
5815 if (IN_SCR_FIELD(sx, sy))
5817 DrawLevelFieldCrumbledSand(xx, yy);
5818 DrawGraphic(sx, sy, flame_graphic, frame);
5823 if (Feld[xx][yy] == EL_FLAMES)
5824 Feld[xx][yy] = EL_EMPTY;
5825 DrawLevelField(xx, yy);
5830 if (MovDelay[x][y]) /* element still has to wait some time */
5832 PlayLevelSoundAction(x, y, ACTION_WAITING);
5838 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5839 for all other elements GfxAction will be set by InitMovingField() */
5840 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5841 GfxAction[x][y] = ACTION_MOVING;
5845 /* now make next step */
5847 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5849 if (DONT_COLLIDE_WITH(element) &&
5850 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5851 !PLAYER_ENEMY_PROTECTED(newx, newy))
5854 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5858 /* player killed by element which is deadly when colliding with */
5860 KillHero(PLAYERINFO(newx, newy));
5867 else if (CAN_MOVE_INTO_ACID(element) &&
5868 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5869 (MovDir[x][y] == MV_DOWN ||
5870 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5872 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5873 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5877 else if ((element == EL_PENGUIN ||
5878 element == EL_ROBOT ||
5879 element == EL_SATELLITE ||
5880 element == EL_BALLOON ||
5881 IS_CUSTOM_ELEMENT(element)) &&
5882 IN_LEV_FIELD(newx, newy) &&
5883 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5886 SplashAcid(newx, newy);
5887 Store[x][y] = EL_ACID;
5889 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5891 if (Feld[newx][newy] == EL_EXIT_OPEN)
5895 DrawLevelField(x, y);
5897 Feld[x][y] = EL_EMPTY;
5898 DrawLevelField(x, y);
5901 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5902 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5903 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5905 local_player->friends_still_needed--;
5906 if (!local_player->friends_still_needed &&
5907 !local_player->GameOver && AllPlayersGone)
5908 local_player->LevelSolved = local_player->GameOver = TRUE;
5912 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5914 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5915 DrawLevelField(newx, newy);
5917 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5919 else if (!IS_FREE(newx, newy))
5921 GfxAction[x][y] = ACTION_WAITING;
5923 if (IS_PLAYER(x, y))
5924 DrawPlayerField(x, y);
5926 DrawLevelField(x, y);
5931 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5933 if (IS_FOOD_PIG(Feld[newx][newy]))
5935 if (IS_MOVING(newx, newy))
5936 RemoveMovingField(newx, newy);
5939 Feld[newx][newy] = EL_EMPTY;
5940 DrawLevelField(newx, newy);
5943 PlayLevelSound(x, y, SND_PIG_DIGGING);
5945 else if (!IS_FREE(newx, newy))
5947 if (IS_PLAYER(x, y))
5948 DrawPlayerField(x, y);
5950 DrawLevelField(x, y);
5959 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5962 else if (IS_CUSTOM_ELEMENT(element) &&
5963 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5967 !IS_FREE(newx, newy)
5972 int new_element = Feld[newx][newy];
5975 printf("::: '%s' digs '%s' [%d]\n",
5976 element_info[element].token_name,
5977 element_info[Feld[newx][newy]].token_name,
5978 StorePlayer[newx][newy]);
5981 if (!IS_FREE(newx, newy))
5983 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5984 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5987 /* no element can dig solid indestructible elements */
5988 if (IS_INDESTRUCTIBLE(new_element) &&
5989 !IS_DIGGABLE(new_element) &&
5990 !IS_COLLECTIBLE(new_element))
5993 if (AmoebaNr[newx][newy] &&
5994 (new_element == EL_AMOEBA_FULL ||
5995 new_element == EL_BD_AMOEBA ||
5996 new_element == EL_AMOEBA_GROWING))
5998 AmoebaCnt[AmoebaNr[newx][newy]]--;
5999 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6002 if (IS_MOVING(newx, newy))
6003 RemoveMovingField(newx, newy);
6006 RemoveField(newx, newy);
6007 DrawLevelField(newx, newy);
6010 /* if digged element was about to explode, prevent the explosion */
6011 ExplodeField[newx][newy] = EX_TYPE_NONE;
6013 PlayLevelSoundAction(x, y, action);
6018 Store[newx][newy] = EL_EMPTY;
6019 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6020 Store[newx][newy] = element_info[element].move_leave_element;
6022 Store[newx][newy] = EL_EMPTY;
6023 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6024 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6025 Store[newx][newy] = element_info[element].move_leave_element;
6028 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6029 element_info[element].can_leave_element = TRUE;
6032 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6034 RunnerVisit[x][y] = FrameCounter;
6035 PlayerVisit[x][y] /= 8; /* expire player visit path */
6041 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6043 if (!IS_FREE(newx, newy))
6045 if (IS_PLAYER(x, y))
6046 DrawPlayerField(x, y);
6048 DrawLevelField(x, y);
6054 boolean wanna_flame = !RND(10);
6055 int dx = newx - x, dy = newy - y;
6056 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6057 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6058 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6059 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6060 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6061 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6064 IS_CLASSIC_ENEMY(element1) ||
6065 IS_CLASSIC_ENEMY(element2)) &&
6066 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6067 element1 != EL_FLAMES && element2 != EL_FLAMES)
6070 ResetGfxAnimation(x, y);
6071 GfxAction[x][y] = ACTION_ATTACKING;
6074 if (IS_PLAYER(x, y))
6075 DrawPlayerField(x, y);
6077 DrawLevelField(x, y);
6079 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6081 MovDelay[x][y] = 50;
6085 RemoveField(newx, newy);
6087 Feld[newx][newy] = EL_FLAMES;
6088 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6091 RemoveField(newx1, newy1);
6093 Feld[newx1][newy1] = EL_FLAMES;
6095 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6098 RemoveField(newx2, newy2);
6100 Feld[newx2][newy2] = EL_FLAMES;
6107 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6108 Feld[newx][newy] == EL_DIAMOND)
6110 if (IS_MOVING(newx, newy))
6111 RemoveMovingField(newx, newy);
6114 Feld[newx][newy] = EL_EMPTY;
6115 DrawLevelField(newx, newy);
6118 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6120 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6121 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6123 if (AmoebaNr[newx][newy])
6125 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6126 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6127 Feld[newx][newy] == EL_BD_AMOEBA)
6128 AmoebaCnt[AmoebaNr[newx][newy]]--;
6133 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6135 if (IS_MOVING(newx, newy))
6138 RemoveMovingField(newx, newy);
6142 Feld[newx][newy] = EL_EMPTY;
6143 DrawLevelField(newx, newy);
6146 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6148 else if ((element == EL_PACMAN || element == EL_MOLE)
6149 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6151 if (AmoebaNr[newx][newy])
6153 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6154 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6155 Feld[newx][newy] == EL_BD_AMOEBA)
6156 AmoebaCnt[AmoebaNr[newx][newy]]--;
6159 if (element == EL_MOLE)
6161 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6162 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6164 ResetGfxAnimation(x, y);
6165 GfxAction[x][y] = ACTION_DIGGING;
6166 DrawLevelField(x, y);
6168 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6170 return; /* wait for shrinking amoeba */
6172 else /* element == EL_PACMAN */
6174 Feld[newx][newy] = EL_EMPTY;
6175 DrawLevelField(newx, newy);
6176 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6179 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6180 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6181 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6183 /* wait for shrinking amoeba to completely disappear */
6186 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6188 /* object was running against a wall */
6193 if (move_pattern & MV_ANY_DIRECTION &&
6194 move_pattern == MovDir[x][y])
6196 int blocking_element =
6197 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6200 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6201 element_info[element].token_name,
6202 element_info[blocking_element].token_name,
6206 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6209 element = Feld[x][y]; /* element might have changed */
6214 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6215 DrawLevelElementAnimation(x, y, element);
6217 if (element == EL_BUG ||
6218 element == EL_SPACESHIP ||
6219 element == EL_SP_SNIKSNAK)
6220 DrawLevelField(x, y);
6221 else if (element == EL_MOLE)
6222 DrawLevelField(x, y);
6223 else if (element == EL_BD_BUTTERFLY ||
6224 element == EL_BD_FIREFLY)
6225 DrawLevelElementAnimationIfNeeded(x, y, element);
6226 else if (element == EL_SATELLITE)
6227 DrawLevelElementAnimationIfNeeded(x, y, element);
6228 else if (element == EL_SP_ELECTRON)
6229 DrawLevelElementAnimationIfNeeded(x, y, element);
6232 if (DONT_TOUCH(element))
6233 TestIfBadThingTouchesHero(x, y);
6236 PlayLevelSoundAction(x, y, ACTION_WAITING);
6242 InitMovingField(x, y, MovDir[x][y]);
6244 PlayLevelSoundAction(x, y, ACTION_MOVING);
6248 ContinueMoving(x, y);
6251 void ContinueMoving(int x, int y)
6253 int element = Feld[x][y];
6254 int stored = Store[x][y];
6255 struct ElementInfo *ei = &element_info[element];
6256 int direction = MovDir[x][y];
6257 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6258 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6259 int newx = x + dx, newy = y + dy;
6261 int nextx = newx + dx, nexty = newy + dy;
6264 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6265 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6267 boolean pushed_by_player = Pushed[x][y];
6270 MovPos[x][y] += getElementMoveStepsize(x, y);
6273 if (pushed_by_player && IS_PLAYER(x, y))
6275 /* special case: moving object pushed by player */
6276 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6279 if (pushed_by_player) /* special case: moving object pushed by player */
6280 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6283 if (ABS(MovPos[x][y]) < TILEX)
6285 DrawLevelField(x, y);
6287 return; /* element is still moving */
6290 /* element reached destination field */
6292 Feld[x][y] = EL_EMPTY;
6293 Feld[newx][newy] = element;
6294 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6297 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6299 element = Feld[newx][newy] = EL_ACID;
6302 else if (element == EL_MOLE)
6304 Feld[x][y] = EL_SAND;
6306 DrawLevelFieldCrumbledSandNeighbours(x, y);
6308 else if (element == EL_QUICKSAND_FILLING)
6310 element = Feld[newx][newy] = get_next_element(element);
6311 Store[newx][newy] = Store[x][y];
6313 else if (element == EL_QUICKSAND_EMPTYING)
6315 Feld[x][y] = get_next_element(element);
6316 element = Feld[newx][newy] = Store[x][y];
6318 else if (element == EL_MAGIC_WALL_FILLING)
6320 element = Feld[newx][newy] = get_next_element(element);
6321 if (!game.magic_wall_active)
6322 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6323 Store[newx][newy] = Store[x][y];
6325 else if (element == EL_MAGIC_WALL_EMPTYING)
6327 Feld[x][y] = get_next_element(element);
6328 if (!game.magic_wall_active)
6329 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6330 element = Feld[newx][newy] = Store[x][y];
6332 else if (element == EL_BD_MAGIC_WALL_FILLING)
6334 element = Feld[newx][newy] = get_next_element(element);
6335 if (!game.magic_wall_active)
6336 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6337 Store[newx][newy] = Store[x][y];
6339 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6341 Feld[x][y] = get_next_element(element);
6342 if (!game.magic_wall_active)
6343 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6344 element = Feld[newx][newy] = Store[x][y];
6346 else if (element == EL_AMOEBA_DROPPING)
6348 Feld[x][y] = get_next_element(element);
6349 element = Feld[newx][newy] = Store[x][y];
6351 else if (element == EL_SOKOBAN_OBJECT)
6354 Feld[x][y] = Back[x][y];
6356 if (Back[newx][newy])
6357 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6359 Back[x][y] = Back[newx][newy] = 0;
6362 else if (Store[x][y] == EL_ACID)
6364 element = Feld[newx][newy] = EL_ACID;
6368 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6369 ei->move_leave_element != EL_EMPTY &&
6370 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6371 Store[x][y] != EL_EMPTY))
6373 /* some elements can leave other elements behind after moving */
6375 Feld[x][y] = ei->move_leave_element;
6376 InitField(x, y, FALSE);
6378 if (GFX_CRUMBLED(Feld[x][y]))
6379 DrawLevelFieldCrumbledSandNeighbours(x, y);
6383 Store[x][y] = EL_EMPTY;
6384 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6385 MovDelay[newx][newy] = 0;
6387 if (CAN_CHANGE(element))
6389 /* copy element change control values to new field */
6390 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6391 ChangePage[newx][newy] = ChangePage[x][y];
6392 Changed[newx][newy] = Changed[x][y];
6393 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6396 ChangeDelay[x][y] = 0;
6397 ChangePage[x][y] = -1;
6398 Changed[x][y] = CE_BITMASK_DEFAULT;
6399 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6401 /* copy animation control values to new field */
6402 GfxFrame[newx][newy] = GfxFrame[x][y];
6403 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6404 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6405 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6407 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6409 ResetGfxAnimation(x, y); /* reset animation values for old field */
6412 /* some elements can leave other elements behind after moving */
6414 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6415 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6416 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6418 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6419 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6423 int move_leave_element = ei->move_leave_element;
6425 Feld[x][y] = move_leave_element;
6426 InitField(x, y, FALSE);
6428 if (GFX_CRUMBLED(Feld[x][y]))
6429 DrawLevelFieldCrumbledSandNeighbours(x, y);
6431 if (ELEM_IS_PLAYER(move_leave_element))
6432 RelocatePlayer(x, y, move_leave_element);
6437 /* some elements can leave other elements behind after moving */
6438 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6439 ei->move_leave_element != EL_EMPTY &&
6440 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6441 ei->can_leave_element_last))
6443 Feld[x][y] = ei->move_leave_element;
6444 InitField(x, y, FALSE);
6446 if (GFX_CRUMBLED(Feld[x][y]))
6447 DrawLevelFieldCrumbledSandNeighbours(x, y);
6450 ei->can_leave_element_last = ei->can_leave_element;
6451 ei->can_leave_element = FALSE;
6455 /* 2.1.1 (does not work correctly for spring) */
6456 if (!CAN_MOVE(element))
6457 MovDir[newx][newy] = 0;
6461 /* (does not work for falling objects that slide horizontally) */
6462 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6463 MovDir[newx][newy] = 0;
6466 if (!CAN_MOVE(element) ||
6467 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6468 MovDir[newx][newy] = 0;
6472 if (!CAN_MOVE(element) ||
6473 (CAN_FALL(element) && direction == MV_DOWN))
6474 GfxDir[x][y] = MovDir[newx][newy] = 0;
6476 if (!CAN_MOVE(element) ||
6477 (CAN_FALL(element) && direction == MV_DOWN &&
6478 (element == EL_SPRING ||
6479 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6480 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6481 GfxDir[x][y] = MovDir[newx][newy] = 0;
6487 DrawLevelField(x, y);
6488 DrawLevelField(newx, newy);
6490 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6492 /* prevent pushed element from moving on in pushed direction */
6493 if (pushed_by_player && CAN_MOVE(element) &&
6494 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6495 !(element_info[element].move_pattern & direction))
6496 TurnRound(newx, newy);
6499 /* prevent elements on conveyor belt from moving on in last direction */
6500 if (pushed_by_conveyor && CAN_FALL(element) &&
6501 direction & MV_HORIZONTAL)
6504 if (CAN_MOVE(element))
6505 InitMovDir(newx, newy);
6507 MovDir[newx][newy] = 0;
6509 MovDir[newx][newy] = 0;
6514 if (!pushed_by_player)
6516 int nextx = newx + dx, nexty = newy + dy;
6517 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6519 WasJustMoving[newx][newy] = 3;
6521 if (CAN_FALL(element) && direction == MV_DOWN)
6522 WasJustFalling[newx][newy] = 3;
6524 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6525 CheckCollision[newx][newy] = 2;
6528 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6530 TestIfBadThingTouchesHero(newx, newy);
6531 TestIfBadThingTouchesFriend(newx, newy);
6533 if (!IS_CUSTOM_ELEMENT(element))
6534 TestIfBadThingTouchesOtherBadThing(newx, newy);
6536 else if (element == EL_PENGUIN)
6537 TestIfFriendTouchesBadThing(newx, newy);
6539 #if USE_NEW_MOVE_STYLE
6541 if (CAN_FALL(element) && direction == MV_DOWN &&
6542 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6543 IS_PLAYER(x, newy + 1))
6544 printf("::: we would now kill the player [%d]\n", FrameCounter);
6547 /* give the player one last chance (one more frame) to move away */
6548 if (CAN_FALL(element) && direction == MV_DOWN &&
6549 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6550 (!IS_PLAYER(x, newy + 1) ||
6551 game.engine_version < VERSION_IDENT(3,1,1,0)))
6554 if (CAN_FALL(element) && direction == MV_DOWN &&
6555 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6563 if (pushed_by_player && !game.use_bug_change_when_pushing)
6565 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6568 if (pushed_by_player)
6573 int dig_side = MV_DIR_OPPOSITE(direction);
6575 static int trigger_sides[4] =
6577 CH_SIDE_RIGHT, /* moving left */
6578 CH_SIDE_LEFT, /* moving right */
6579 CH_SIDE_BOTTOM, /* moving up */
6580 CH_SIDE_TOP, /* moving down */
6582 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6584 struct PlayerInfo *player = PLAYERINFO(x, y);
6586 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6587 player->index_bit, dig_side);
6588 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6589 player->index_bit, dig_side);
6594 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6598 if (ChangePage[newx][newy] != -1) /* delayed change */
6599 ChangeElement(newx, newy, ChangePage[newx][newy]);
6604 TestIfElementHitsCustomElement(newx, newy, direction);
6608 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6610 int hitting_element = Feld[newx][newy];
6612 /* !!! fix side (direction) orientation here and elsewhere !!! */
6613 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6617 if (IN_LEV_FIELD(nextx, nexty))
6619 int opposite_direction = MV_DIR_OPPOSITE(direction);
6620 int hitting_side = direction;
6621 int touched_side = opposite_direction;
6622 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6623 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6624 MovDir[nextx][nexty] != direction ||
6625 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6631 CheckElementChangeBySide(nextx, nexty, touched_element,
6632 CE_HIT_BY_SOMETHING, opposite_direction);
6634 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6635 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6637 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6639 struct ElementChangeInfo *change =
6640 &element_info[hitting_element].change_page[i];
6642 if (change->can_change &&
6643 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6644 change->trigger_side & touched_side &&
6645 change->trigger_element == touched_element)
6647 CheckElementChangeByPage(newx, newy, hitting_element,
6648 touched_element, CE_OTHER_IS_HITTING,i);
6654 if (IS_CUSTOM_ELEMENT(touched_element) &&
6655 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6657 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6659 struct ElementChangeInfo *change =
6660 &element_info[touched_element].change_page[i];
6662 if (change->can_change &&
6663 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6664 change->trigger_side & hitting_side &&
6665 change->trigger_element == hitting_element)
6667 CheckElementChangeByPage(nextx, nexty, touched_element,
6668 hitting_element, CE_OTHER_GETS_HIT, i);
6679 TestIfPlayerTouchesCustomElement(newx, newy);
6680 TestIfElementTouchesCustomElement(newx, newy);
6683 int AmoebeNachbarNr(int ax, int ay)
6686 int element = Feld[ax][ay];
6688 static int xy[4][2] =
6696 for (i = 0; i < NUM_DIRECTIONS; i++)
6698 int x = ax + xy[i][0];
6699 int y = ay + xy[i][1];
6701 if (!IN_LEV_FIELD(x, y))
6704 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6705 group_nr = AmoebaNr[x][y];
6711 void AmoebenVereinigen(int ax, int ay)
6713 int i, x, y, xx, yy;
6714 int new_group_nr = AmoebaNr[ax][ay];
6715 static int xy[4][2] =
6723 if (new_group_nr == 0)
6726 for (i = 0; i < NUM_DIRECTIONS; i++)
6731 if (!IN_LEV_FIELD(x, y))
6734 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6735 Feld[x][y] == EL_BD_AMOEBA ||
6736 Feld[x][y] == EL_AMOEBA_DEAD) &&
6737 AmoebaNr[x][y] != new_group_nr)
6739 int old_group_nr = AmoebaNr[x][y];
6741 if (old_group_nr == 0)
6744 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6745 AmoebaCnt[old_group_nr] = 0;
6746 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6747 AmoebaCnt2[old_group_nr] = 0;
6749 for (yy = 0; yy < lev_fieldy; yy++)
6751 for (xx = 0; xx < lev_fieldx; xx++)
6753 if (AmoebaNr[xx][yy] == old_group_nr)
6754 AmoebaNr[xx][yy] = new_group_nr;
6761 void AmoebeUmwandeln(int ax, int ay)
6765 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6767 int group_nr = AmoebaNr[ax][ay];
6772 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6773 printf("AmoebeUmwandeln(): This should never happen!\n");
6778 for (y = 0; y < lev_fieldy; y++)
6780 for (x = 0; x < lev_fieldx; x++)
6782 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6785 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6789 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6790 SND_AMOEBA_TURNING_TO_GEM :
6791 SND_AMOEBA_TURNING_TO_ROCK));
6796 static int xy[4][2] =
6804 for (i = 0; i < NUM_DIRECTIONS; i++)
6809 if (!IN_LEV_FIELD(x, y))
6812 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6814 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6815 SND_AMOEBA_TURNING_TO_GEM :
6816 SND_AMOEBA_TURNING_TO_ROCK));
6823 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6826 int group_nr = AmoebaNr[ax][ay];
6827 boolean done = FALSE;
6832 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6833 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6838 for (y = 0; y < lev_fieldy; y++)
6840 for (x = 0; x < lev_fieldx; x++)
6842 if (AmoebaNr[x][y] == group_nr &&
6843 (Feld[x][y] == EL_AMOEBA_DEAD ||
6844 Feld[x][y] == EL_BD_AMOEBA ||
6845 Feld[x][y] == EL_AMOEBA_GROWING))
6848 Feld[x][y] = new_element;
6849 InitField(x, y, FALSE);
6850 DrawLevelField(x, y);
6857 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6858 SND_BD_AMOEBA_TURNING_TO_ROCK :
6859 SND_BD_AMOEBA_TURNING_TO_GEM));
6862 void AmoebeWaechst(int x, int y)
6864 static unsigned long sound_delay = 0;
6865 static unsigned long sound_delay_value = 0;
6867 if (!MovDelay[x][y]) /* start new growing cycle */
6871 if (DelayReached(&sound_delay, sound_delay_value))
6874 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6876 if (Store[x][y] == EL_BD_AMOEBA)
6877 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6879 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6881 sound_delay_value = 30;
6885 if (MovDelay[x][y]) /* wait some time before growing bigger */
6888 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6890 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6891 6 - MovDelay[x][y]);
6893 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6896 if (!MovDelay[x][y])
6898 Feld[x][y] = Store[x][y];
6900 DrawLevelField(x, y);
6905 void AmoebaDisappearing(int x, int y)
6907 static unsigned long sound_delay = 0;
6908 static unsigned long sound_delay_value = 0;
6910 if (!MovDelay[x][y]) /* start new shrinking cycle */
6914 if (DelayReached(&sound_delay, sound_delay_value))
6915 sound_delay_value = 30;
6918 if (MovDelay[x][y]) /* wait some time before shrinking */
6921 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6923 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6924 6 - MovDelay[x][y]);
6926 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6929 if (!MovDelay[x][y])
6931 Feld[x][y] = EL_EMPTY;
6932 DrawLevelField(x, y);
6934 /* don't let mole enter this field in this cycle;
6935 (give priority to objects falling to this field from above) */
6941 void AmoebeAbleger(int ax, int ay)
6944 int element = Feld[ax][ay];
6945 int graphic = el2img(element);
6946 int newax = ax, neway = ay;
6947 static int xy[4][2] =
6955 if (!level.amoeba_speed)
6957 Feld[ax][ay] = EL_AMOEBA_DEAD;
6958 DrawLevelField(ax, ay);
6962 if (IS_ANIMATED(graphic))
6963 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6965 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6966 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6968 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6971 if (MovDelay[ax][ay])
6975 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6978 int x = ax + xy[start][0];
6979 int y = ay + xy[start][1];
6981 if (!IN_LEV_FIELD(x, y))
6985 if (IS_FREE(x, y) ||
6986 CAN_GROW_INTO(Feld[x][y]) ||
6987 Feld[x][y] == EL_QUICKSAND_EMPTY)
6993 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6994 if (IS_FREE(x, y) ||
6995 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7002 if (newax == ax && neway == ay)
7005 else /* normal or "filled" (BD style) amoeba */
7008 boolean waiting_for_player = FALSE;
7010 for (i = 0; i < NUM_DIRECTIONS; i++)
7012 int j = (start + i) % 4;
7013 int x = ax + xy[j][0];
7014 int y = ay + xy[j][1];
7016 if (!IN_LEV_FIELD(x, y))
7020 if (IS_FREE(x, y) ||
7021 CAN_GROW_INTO(Feld[x][y]) ||
7022 Feld[x][y] == EL_QUICKSAND_EMPTY)
7029 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7030 if (IS_FREE(x, y) ||
7031 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7038 else if (IS_PLAYER(x, y))
7039 waiting_for_player = TRUE;
7042 if (newax == ax && neway == ay) /* amoeba cannot grow */
7045 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7047 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7050 Feld[ax][ay] = EL_AMOEBA_DEAD;
7051 DrawLevelField(ax, ay);
7052 AmoebaCnt[AmoebaNr[ax][ay]]--;
7054 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7056 if (element == EL_AMOEBA_FULL)
7057 AmoebeUmwandeln(ax, ay);
7058 else if (element == EL_BD_AMOEBA)
7059 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7064 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7066 /* amoeba gets larger by growing in some direction */
7068 int new_group_nr = AmoebaNr[ax][ay];
7071 if (new_group_nr == 0)
7073 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7074 printf("AmoebeAbleger(): This should never happen!\n");
7079 AmoebaNr[newax][neway] = new_group_nr;
7080 AmoebaCnt[new_group_nr]++;
7081 AmoebaCnt2[new_group_nr]++;
7083 /* if amoeba touches other amoeba(s) after growing, unify them */
7084 AmoebenVereinigen(newax, neway);
7086 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7088 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7094 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7095 (neway == lev_fieldy - 1 && newax != ax))
7097 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7098 Store[newax][neway] = element;
7100 else if (neway == ay)
7102 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7104 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7106 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7111 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7112 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7113 Store[ax][ay] = EL_AMOEBA_DROP;
7114 ContinueMoving(ax, ay);
7118 DrawLevelField(newax, neway);
7121 void Life(int ax, int ay)
7124 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7126 int element = Feld[ax][ay];
7127 int graphic = el2img(element);
7128 boolean changed = FALSE;
7130 if (IS_ANIMATED(graphic))
7131 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7136 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7137 MovDelay[ax][ay] = life_time;
7139 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7142 if (MovDelay[ax][ay])
7146 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7148 int xx = ax+x1, yy = ay+y1;
7151 if (!IN_LEV_FIELD(xx, yy))
7154 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7156 int x = xx+x2, y = yy+y2;
7158 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7161 if (((Feld[x][y] == element ||
7162 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7164 (IS_FREE(x, y) && Stop[x][y]))
7168 if (xx == ax && yy == ay) /* field in the middle */
7170 if (nachbarn < life[0] || nachbarn > life[1])
7172 Feld[xx][yy] = EL_EMPTY;
7174 DrawLevelField(xx, yy);
7175 Stop[xx][yy] = TRUE;
7180 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7181 { /* free border field */
7182 if (nachbarn >= life[2] && nachbarn <= life[3])
7184 Feld[xx][yy] = element;
7185 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7187 DrawLevelField(xx, yy);
7188 Stop[xx][yy] = TRUE;
7193 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7194 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7195 { /* free border field */
7196 if (nachbarn >= life[2] && nachbarn <= life[3])
7198 Feld[xx][yy] = element;
7199 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7201 DrawLevelField(xx, yy);
7202 Stop[xx][yy] = TRUE;
7210 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7211 SND_GAME_OF_LIFE_GROWING);
7214 static void InitRobotWheel(int x, int y)
7216 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7219 static void RunRobotWheel(int x, int y)
7221 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7224 static void StopRobotWheel(int x, int y)
7226 if (ZX == x && ZY == y)
7230 static void InitTimegateWheel(int x, int y)
7233 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7235 /* another brainless, "type style" bug ... :-( */
7236 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7240 static void RunTimegateWheel(int x, int y)
7242 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7245 void CheckExit(int x, int y)
7247 if (local_player->gems_still_needed > 0 ||
7248 local_player->sokobanfields_still_needed > 0 ||
7249 local_player->lights_still_needed > 0)
7251 int element = Feld[x][y];
7252 int graphic = el2img(element);
7254 if (IS_ANIMATED(graphic))
7255 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7260 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7263 Feld[x][y] = EL_EXIT_OPENING;
7265 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7268 void CheckExitSP(int x, int y)
7270 if (local_player->gems_still_needed > 0)
7272 int element = Feld[x][y];
7273 int graphic = el2img(element);
7275 if (IS_ANIMATED(graphic))
7276 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7281 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7284 Feld[x][y] = EL_SP_EXIT_OPENING;
7286 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7289 static void CloseAllOpenTimegates()
7293 for (y = 0; y < lev_fieldy; y++)
7295 for (x = 0; x < lev_fieldx; x++)
7297 int element = Feld[x][y];
7299 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7301 Feld[x][y] = EL_TIMEGATE_CLOSING;
7303 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7305 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7312 void EdelsteinFunkeln(int x, int y)
7314 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7317 if (Feld[x][y] == EL_BD_DIAMOND)
7320 if (MovDelay[x][y] == 0) /* next animation frame */
7321 MovDelay[x][y] = 11 * !SimpleRND(500);
7323 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7327 if (setup.direct_draw && MovDelay[x][y])
7328 SetDrawtoField(DRAW_BUFFERED);
7330 DrawLevelElementAnimation(x, y, Feld[x][y]);
7332 if (MovDelay[x][y] != 0)
7334 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7335 10 - MovDelay[x][y]);
7337 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7339 if (setup.direct_draw)
7343 dest_x = FX + SCREENX(x) * TILEX;
7344 dest_y = FY + SCREENY(y) * TILEY;
7346 BlitBitmap(drawto_field, window,
7347 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7348 SetDrawtoField(DRAW_DIRECT);
7354 void MauerWaechst(int x, int y)
7358 if (!MovDelay[x][y]) /* next animation frame */
7359 MovDelay[x][y] = 3 * delay;
7361 if (MovDelay[x][y]) /* wait some time before next frame */
7365 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7367 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7368 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7370 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7373 if (!MovDelay[x][y])
7375 if (MovDir[x][y] == MV_LEFT)
7377 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7378 DrawLevelField(x - 1, y);
7380 else if (MovDir[x][y] == MV_RIGHT)
7382 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7383 DrawLevelField(x + 1, y);
7385 else if (MovDir[x][y] == MV_UP)
7387 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7388 DrawLevelField(x, y - 1);
7392 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7393 DrawLevelField(x, y + 1);
7396 Feld[x][y] = Store[x][y];
7398 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7399 DrawLevelField(x, y);
7404 void MauerAbleger(int ax, int ay)
7406 int element = Feld[ax][ay];
7407 int graphic = el2img(element);
7408 boolean oben_frei = FALSE, unten_frei = FALSE;
7409 boolean links_frei = FALSE, rechts_frei = FALSE;
7410 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7411 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7412 boolean new_wall = FALSE;
7414 if (IS_ANIMATED(graphic))
7415 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7417 if (!MovDelay[ax][ay]) /* start building new wall */
7418 MovDelay[ax][ay] = 6;
7420 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7423 if (MovDelay[ax][ay])
7427 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7429 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7431 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7433 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7436 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7437 element == EL_EXPANDABLE_WALL_ANY)
7441 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7442 Store[ax][ay-1] = element;
7443 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7444 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7445 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7446 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7451 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7452 Store[ax][ay+1] = element;
7453 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7454 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7455 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7456 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7461 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7462 element == EL_EXPANDABLE_WALL_ANY ||
7463 element == EL_EXPANDABLE_WALL)
7467 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7468 Store[ax-1][ay] = element;
7469 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7470 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7471 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7472 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7478 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7479 Store[ax+1][ay] = element;
7480 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7481 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7482 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7483 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7488 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7489 DrawLevelField(ax, ay);
7491 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7493 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7494 unten_massiv = TRUE;
7495 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7496 links_massiv = TRUE;
7497 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7498 rechts_massiv = TRUE;
7500 if (((oben_massiv && unten_massiv) ||
7501 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7502 element == EL_EXPANDABLE_WALL) &&
7503 ((links_massiv && rechts_massiv) ||
7504 element == EL_EXPANDABLE_WALL_VERTICAL))
7505 Feld[ax][ay] = EL_WALL;
7509 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7511 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7515 void CheckForDragon(int x, int y)
7518 boolean dragon_found = FALSE;
7519 static int xy[4][2] =
7527 for (i = 0; i < NUM_DIRECTIONS; i++)
7529 for (j = 0; j < 4; j++)
7531 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7533 if (IN_LEV_FIELD(xx, yy) &&
7534 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7536 if (Feld[xx][yy] == EL_DRAGON)
7537 dragon_found = TRUE;
7546 for (i = 0; i < NUM_DIRECTIONS; i++)
7548 for (j = 0; j < 3; j++)
7550 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7552 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7554 Feld[xx][yy] = EL_EMPTY;
7555 DrawLevelField(xx, yy);
7564 static void InitBuggyBase(int x, int y)
7566 int element = Feld[x][y];
7567 int activating_delay = FRAMES_PER_SECOND / 4;
7570 (element == EL_SP_BUGGY_BASE ?
7571 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7572 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7574 element == EL_SP_BUGGY_BASE_ACTIVE ?
7575 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7578 static void WarnBuggyBase(int x, int y)
7581 static int xy[4][2] =
7589 for (i = 0; i < NUM_DIRECTIONS; i++)
7591 int xx = x + xy[i][0], yy = y + xy[i][1];
7593 if (IS_PLAYER(xx, yy))
7595 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7602 static void InitTrap(int x, int y)
7604 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7607 static void ActivateTrap(int x, int y)
7609 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7612 static void ChangeActiveTrap(int x, int y)
7614 int graphic = IMG_TRAP_ACTIVE;
7616 /* if new animation frame was drawn, correct crumbled sand border */
7617 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7618 DrawLevelFieldCrumbledSand(x, y);
7621 static void ChangeElementNowExt(int x, int y, int target_element)
7623 int previous_move_direction = MovDir[x][y];
7625 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7626 IS_WALKABLE(Feld[x][y]));
7628 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7629 IS_WALKABLE(Feld[x][y]) &&
7633 /* check if element under player changes from accessible to unaccessible
7634 (needed for special case of dropping element which then changes) */
7635 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7636 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7639 printf("::: BOOOM! [%d, '%s']\n", target_element,
7640 element_info[target_element].token_name);
7652 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7653 RemoveMovingField(x, y);
7657 Feld[x][y] = target_element;
7660 Feld[x][y] = target_element;
7663 ResetGfxAnimation(x, y);
7664 ResetRandomAnimationValue(x, y);
7666 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7667 MovDir[x][y] = previous_move_direction;
7670 InitField_WithBug1(x, y, FALSE);
7672 InitField(x, y, FALSE);
7673 if (CAN_MOVE(Feld[x][y]))
7677 DrawLevelField(x, y);
7679 if (GFX_CRUMBLED(Feld[x][y]))
7680 DrawLevelFieldCrumbledSandNeighbours(x, y);
7684 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7688 TestIfBadThingTouchesHero(x, y);
7689 TestIfPlayerTouchesCustomElement(x, y);
7690 TestIfElementTouchesCustomElement(x, y);
7693 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7694 if (ELEM_IS_PLAYER(target_element))
7695 RelocatePlayer(x, y, target_element);
7698 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7702 TestIfBadThingTouchesHero(x, y);
7703 TestIfPlayerTouchesCustomElement(x, y);
7704 TestIfElementTouchesCustomElement(x, y);
7708 static boolean ChangeElementNow(int x, int y, int element, int page)
7710 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7712 int old_element = Feld[x][y];
7714 /* always use default change event to prevent running into a loop */
7715 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7716 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7718 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7720 /* reset actual trigger element and player */
7721 change->actual_trigger_element = EL_EMPTY;
7722 change->actual_trigger_player = EL_PLAYER_1;
7725 /* do not change already changed elements with same change event */
7727 if (Changed[x][y] & ChangeEvent[x][y])
7734 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7737 /* !!! indirect change before direct change !!! */
7738 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7741 if (change->explode)
7748 if (change->use_target_content)
7750 boolean complete_replace = TRUE;
7751 boolean can_replace[3][3];
7754 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7757 boolean is_walkable;
7758 boolean is_diggable;
7759 boolean is_collectible;
7760 boolean is_removable;
7761 boolean is_destructible;
7762 int ex = x + xx - 1;
7763 int ey = y + yy - 1;
7764 int content_element = change->target_content[xx][yy];
7767 can_replace[xx][yy] = TRUE;
7769 if (ex == x && ey == y) /* do not check changing element itself */
7772 if (content_element == EL_EMPTY_SPACE)
7774 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7779 if (!IN_LEV_FIELD(ex, ey))
7781 can_replace[xx][yy] = FALSE;
7782 complete_replace = FALSE;
7788 if (Changed[ex][ey]) /* do not change already changed elements */
7790 can_replace[xx][yy] = FALSE;
7791 complete_replace = FALSE;
7799 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7800 e = MovingOrBlocked2Element(ex, ey);
7805 is_empty = (IS_FREE(ex, ey) ||
7806 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7807 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7808 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7812 is_empty = (IS_FREE(ex, ey) ||
7813 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7815 is_empty = (IS_FREE(ex, ey) ||
7816 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7821 is_walkable = (is_empty || IS_WALKABLE(e));
7822 is_diggable = (is_empty || IS_DIGGABLE(e));
7823 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7824 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7825 is_removable = (is_diggable || is_collectible);
7827 can_replace[xx][yy] =
7828 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7829 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7830 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7831 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7832 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7833 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7834 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7836 if (!can_replace[xx][yy])
7837 complete_replace = FALSE;
7839 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7840 IS_WALKABLE(content_element)));
7842 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7844 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7847 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7848 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7849 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7851 can_replace[xx][yy] = FALSE;
7852 complete_replace = FALSE;
7857 if (!change->only_if_complete || complete_replace)
7859 boolean something_has_changed = FALSE;
7861 if (change->only_if_complete && change->use_random_replace &&
7862 RND(100) < change->random_percentage)
7865 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7867 int ex = x + xx - 1;
7868 int ey = y + yy - 1;
7869 int content_element;
7871 if (can_replace[xx][yy] && (!change->use_random_replace ||
7872 RND(100) < change->random_percentage))
7874 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7875 RemoveMovingField(ex, ey);
7877 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7879 content_element = change->target_content[xx][yy];
7880 target_element = GET_TARGET_ELEMENT(content_element, change);
7882 ChangeElementNowExt(ex, ey, target_element);
7884 something_has_changed = TRUE;
7886 /* for symmetry reasons, freeze newly created border elements */
7887 if (ex != x || ey != y)
7888 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7892 if (something_has_changed)
7893 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7898 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7900 ChangeElementNowExt(x, y, target_element);
7902 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7906 /* this uses direct change before indirect change */
7907 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7913 static void ChangeElement(int x, int y, int page)
7915 int element = MovingOrBlocked2Element(x, y);
7916 struct ElementInfo *ei = &element_info[element];
7917 struct ElementChangeInfo *change = &ei->change_page[page];
7920 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7923 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7924 x, y, element, element_info[element].token_name);
7925 printf("ChangeElement(): This should never happen!\n");
7930 /* this can happen with classic bombs on walkable, changing elements */
7931 if (!CAN_CHANGE(element))
7934 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7935 ChangeDelay[x][y] = 0;
7941 if (ChangeDelay[x][y] == 0) /* initialize element change */
7943 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7944 RND(change->delay_random * change->delay_frames)) + 1;
7946 ResetGfxAnimation(x, y);
7947 ResetRandomAnimationValue(x, y);
7949 if (change->pre_change_function)
7950 change->pre_change_function(x, y);
7953 ChangeDelay[x][y]--;
7955 if (ChangeDelay[x][y] != 0) /* continue element change */
7957 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7959 if (IS_ANIMATED(graphic))
7960 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7962 if (change->change_function)
7963 change->change_function(x, y);
7965 else /* finish element change */
7967 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7969 page = ChangePage[x][y];
7970 ChangePage[x][y] = -1;
7972 change = &ei->change_page[page];
7976 if (IS_MOVING(x, y) && !change->explode)
7978 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7981 ChangeDelay[x][y] = 1; /* try change after next move step */
7982 ChangePage[x][y] = page; /* remember page to use for change */
7987 if (ChangeElementNow(x, y, element, page))
7989 if (change->post_change_function)
7990 change->post_change_function(x, y);
7995 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7996 int trigger_element,
8003 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8005 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8010 int element = EL_CUSTOM_START + i;
8012 boolean change_element = FALSE;
8015 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8018 for (j = 0; j < element_info[element].num_change_pages; j++)
8020 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8022 if (change->can_change &&
8023 change->events & CH_EVENT_BIT(trigger_event) &&
8024 change->trigger_side & trigger_side &&
8025 change->trigger_player & trigger_player &&
8026 change->trigger_page & trigger_page_bits &&
8027 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8030 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8031 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8032 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8035 change_element = TRUE;
8038 change->actual_trigger_element = trigger_element;
8039 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8045 if (!change_element)
8048 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8051 if (x == lx && y == ly) /* do not change trigger element itself */
8055 if (Feld[x][y] == element)
8057 ChangeDelay[x][y] = 1;
8058 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8059 ChangeElement(x, y, page);
8067 static boolean CheckElementChangeExt(int x, int y,
8069 int trigger_element,
8075 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8078 if (Feld[x][y] == EL_BLOCKED)
8080 Blocked2Moving(x, y, &x, &y);
8081 element = Feld[x][y];
8085 if (Feld[x][y] != element) /* check if element has already changed */
8088 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8089 Feld[x][y], element_info[Feld[x][y]].token_name,
8090 element, element_info[element].token_name,
8099 if (trigger_page < 0)
8101 boolean change_element = FALSE;
8104 for (i = 0; i < element_info[element].num_change_pages; i++)
8106 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8108 if (change->can_change &&
8109 change->events & CH_EVENT_BIT(trigger_event) &&
8110 change->trigger_side & trigger_side &&
8111 change->trigger_player & trigger_player)
8113 change_element = TRUE;
8116 change->actual_trigger_element = trigger_element;
8117 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8123 if (!change_element)
8128 struct ElementInfo *ei = &element_info[element];
8129 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8131 change->actual_trigger_element = trigger_element;
8132 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8137 /* !!! this check misses pages with same event, but different side !!! */
8139 if (trigger_page < 0)
8140 trigger_page = element_info[element].event_page_nr[trigger_event];
8142 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8146 ChangeDelay[x][y] = 1;
8147 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8148 ChangeElement(x, y, trigger_page);
8153 static void PlayPlayerSound(struct PlayerInfo *player)
8155 int jx = player->jx, jy = player->jy;
8156 int element = player->element_nr;
8157 int last_action = player->last_action_waiting;
8158 int action = player->action_waiting;
8160 if (player->is_waiting)
8162 if (action != last_action)
8163 PlayLevelSoundElementAction(jx, jy, element, action);
8165 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8169 if (action != last_action)
8170 StopSound(element_info[element].sound[last_action]);
8172 if (last_action == ACTION_SLEEPING)
8173 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8177 static void PlayAllPlayersSound()
8181 for (i = 0; i < MAX_PLAYERS; i++)
8182 if (stored_player[i].active)
8183 PlayPlayerSound(&stored_player[i]);
8186 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8188 boolean last_waiting = player->is_waiting;
8189 int move_dir = player->MovDir;
8191 player->last_action_waiting = player->action_waiting;
8195 if (!last_waiting) /* not waiting -> waiting */
8197 player->is_waiting = TRUE;
8199 player->frame_counter_bored =
8201 game.player_boring_delay_fixed +
8202 SimpleRND(game.player_boring_delay_random);
8203 player->frame_counter_sleeping =
8205 game.player_sleeping_delay_fixed +
8206 SimpleRND(game.player_sleeping_delay_random);
8208 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8211 if (game.player_sleeping_delay_fixed +
8212 game.player_sleeping_delay_random > 0 &&
8213 player->anim_delay_counter == 0 &&
8214 player->post_delay_counter == 0 &&
8215 FrameCounter >= player->frame_counter_sleeping)
8216 player->is_sleeping = TRUE;
8217 else if (game.player_boring_delay_fixed +
8218 game.player_boring_delay_random > 0 &&
8219 FrameCounter >= player->frame_counter_bored)
8220 player->is_bored = TRUE;
8222 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8223 player->is_bored ? ACTION_BORING :
8226 if (player->is_sleeping)
8228 if (player->num_special_action_sleeping > 0)
8230 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8232 int last_special_action = player->special_action_sleeping;
8233 int num_special_action = player->num_special_action_sleeping;
8234 int special_action =
8235 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8236 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8237 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8238 last_special_action + 1 : ACTION_SLEEPING);
8239 int special_graphic =
8240 el_act_dir2img(player->element_nr, special_action, move_dir);
8242 player->anim_delay_counter =
8243 graphic_info[special_graphic].anim_delay_fixed +
8244 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8245 player->post_delay_counter =
8246 graphic_info[special_graphic].post_delay_fixed +
8247 SimpleRND(graphic_info[special_graphic].post_delay_random);
8249 player->special_action_sleeping = special_action;
8252 if (player->anim_delay_counter > 0)
8254 player->action_waiting = player->special_action_sleeping;
8255 player->anim_delay_counter--;
8257 else if (player->post_delay_counter > 0)
8259 player->post_delay_counter--;
8263 else if (player->is_bored)
8265 if (player->num_special_action_bored > 0)
8267 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8269 int special_action =
8270 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8271 int special_graphic =
8272 el_act_dir2img(player->element_nr, special_action, move_dir);
8274 player->anim_delay_counter =
8275 graphic_info[special_graphic].anim_delay_fixed +
8276 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8277 player->post_delay_counter =
8278 graphic_info[special_graphic].post_delay_fixed +
8279 SimpleRND(graphic_info[special_graphic].post_delay_random);
8281 player->special_action_bored = special_action;
8284 if (player->anim_delay_counter > 0)
8286 player->action_waiting = player->special_action_bored;
8287 player->anim_delay_counter--;
8289 else if (player->post_delay_counter > 0)
8291 player->post_delay_counter--;
8296 else if (last_waiting) /* waiting -> not waiting */
8298 player->is_waiting = FALSE;
8299 player->is_bored = FALSE;
8300 player->is_sleeping = FALSE;
8302 player->frame_counter_bored = -1;
8303 player->frame_counter_sleeping = -1;
8305 player->anim_delay_counter = 0;
8306 player->post_delay_counter = 0;
8308 player->action_waiting = ACTION_DEFAULT;
8310 player->special_action_bored = ACTION_DEFAULT;
8311 player->special_action_sleeping = ACTION_DEFAULT;
8316 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8319 static byte stored_player_action[MAX_PLAYERS];
8320 static int num_stored_actions = 0;
8322 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8323 int left = player_action & JOY_LEFT;
8324 int right = player_action & JOY_RIGHT;
8325 int up = player_action & JOY_UP;
8326 int down = player_action & JOY_DOWN;
8327 int button1 = player_action & JOY_BUTTON_1;
8328 int button2 = player_action & JOY_BUTTON_2;
8329 int dx = (left ? -1 : right ? 1 : 0);
8330 int dy = (up ? -1 : down ? 1 : 0);
8333 stored_player_action[player->index_nr] = 0;
8334 num_stored_actions++;
8338 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8341 if (!player->active || tape.pausing)
8345 printf("::: [%d %d %d %d] [%d %d]\n",
8346 left, right, up, down, button1, button2);
8352 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8357 if (player->MovPos == 0)
8358 CheckGravityMovement(player);
8361 snapped = SnapField(player, dx, dy);
8365 dropped = DropElement(player);
8367 moved = MovePlayer(player, dx, dy);
8370 if (tape.single_step && tape.recording && !tape.pausing)
8372 if (button1 || (dropped && !moved))
8374 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8375 SnapField(player, 0, 0); /* stop snapping */
8379 SetPlayerWaiting(player, FALSE);
8382 return player_action;
8384 stored_player_action[player->index_nr] = player_action;
8390 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8393 /* no actions for this player (no input at player's configured device) */
8395 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8396 SnapField(player, 0, 0);
8397 CheckGravityMovementWhenNotMoving(player);
8399 if (player->MovPos == 0)
8400 SetPlayerWaiting(player, TRUE);
8402 if (player->MovPos == 0) /* needed for tape.playing */
8403 player->is_moving = FALSE;
8405 player->is_dropping = FALSE;
8411 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8413 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8415 TapeRecordAction(stored_player_action);
8416 num_stored_actions = 0;
8423 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8425 static byte stored_player_action[MAX_PLAYERS];
8426 static int num_stored_actions = 0;
8427 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8428 int left = player_action & JOY_LEFT;
8429 int right = player_action & JOY_RIGHT;
8430 int up = player_action & JOY_UP;
8431 int down = player_action & JOY_DOWN;
8432 int button1 = player_action & JOY_BUTTON_1;
8433 int button2 = player_action & JOY_BUTTON_2;
8434 int dx = (left ? -1 : right ? 1 : 0);
8435 int dy = (up ? -1 : down ? 1 : 0);
8437 stored_player_action[player->index_nr] = 0;
8438 num_stored_actions++;
8440 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8442 if (!player->active || tape.pausing)
8447 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8450 snapped = SnapField(player, dx, dy);
8454 dropped = DropElement(player);
8456 moved = MovePlayer(player, dx, dy);
8459 if (tape.single_step && tape.recording && !tape.pausing)
8461 if (button1 || (dropped && !moved))
8463 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8464 SnapField(player, 0, 0); /* stop snapping */
8468 stored_player_action[player->index_nr] = player_action;
8472 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8474 /* no actions for this player (no input at player's configured device) */
8476 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8477 SnapField(player, 0, 0);
8478 CheckGravityMovementWhenNotMoving(player);
8480 if (player->MovPos == 0)
8481 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8483 if (player->MovPos == 0) /* needed for tape.playing */
8484 player->is_moving = FALSE;
8487 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8489 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8491 TapeRecordAction(stored_player_action);
8492 num_stored_actions = 0;
8497 void AdvanceFrameAndPlayerCounters(int player_nr)
8501 /* advance frame counters (global frame counter and time frame counter) */
8505 /* advance player counters (counters for move delay, move animation etc.) */
8506 for (i = 0; i < MAX_PLAYERS; i++)
8508 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8510 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8512 if (!advance_player_counters) /* not all players may be affected */
8515 stored_player[i].Frame += move_frames;
8517 if (stored_player[i].MovPos != 0)
8518 stored_player[i].StepFrame += move_frames;
8520 #if USE_NEW_MOVE_DELAY
8521 if (stored_player[i].move_delay > 0)
8522 stored_player[i].move_delay--;
8525 #if USE_NEW_PUSH_DELAY
8526 /* due to bugs in previous versions, counter must count up, not down */
8527 if (stored_player[i].push_delay != -1)
8528 stored_player[i].push_delay++;
8531 if (stored_player[i].drop_delay > 0)
8532 stored_player[i].drop_delay--;
8538 static unsigned long action_delay = 0;
8539 unsigned long action_delay_value;
8540 int magic_wall_x = 0, magic_wall_y = 0;
8541 int i, x, y, element, graphic;
8542 byte *recorded_player_action;
8543 byte summarized_player_action = 0;
8545 byte tape_action[MAX_PLAYERS];
8548 if (game_status != GAME_MODE_PLAYING)
8551 action_delay_value =
8552 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8554 if (tape.playing && tape.warp_forward && !tape.pausing)
8555 action_delay_value = 0;
8557 /* ---------- main game synchronization point ---------- */
8559 WaitUntilDelayReached(&action_delay, action_delay_value);
8561 if (network_playing && !network_player_action_received)
8565 printf("DEBUG: try to get network player actions in time\n");
8569 #if defined(NETWORK_AVALIABLE)
8570 /* last chance to get network player actions without main loop delay */
8574 if (game_status != GAME_MODE_PLAYING)
8577 if (!network_player_action_received)
8581 printf("DEBUG: failed to get network player actions in time\n");
8592 printf("::: getting new tape action [%d]\n", FrameCounter);
8595 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8598 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8599 if (recorded_player_action == NULL && tape.pausing)
8604 printf("::: %d\n", stored_player[0].action);
8608 if (recorded_player_action != NULL)
8609 for (i = 0; i < MAX_PLAYERS; i++)
8610 stored_player[i].action = recorded_player_action[i];
8613 for (i = 0; i < MAX_PLAYERS; i++)
8615 summarized_player_action |= stored_player[i].action;
8617 if (!network_playing)
8618 stored_player[i].effective_action = stored_player[i].action;
8621 #if defined(NETWORK_AVALIABLE)
8622 if (network_playing)
8623 SendToServer_MovePlayer(summarized_player_action);
8626 if (!options.network && !setup.team_mode)
8627 local_player->effective_action = summarized_player_action;
8630 if (recorded_player_action != NULL)
8631 for (i = 0; i < MAX_PLAYERS; i++)
8632 stored_player[i].effective_action = recorded_player_action[i];
8636 for (i = 0; i < MAX_PLAYERS; i++)
8638 tape_action[i] = stored_player[i].effective_action;
8640 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8641 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8644 /* only save actions from input devices, but not programmed actions */
8646 TapeRecordAction(tape_action);
8649 for (i = 0; i < MAX_PLAYERS; i++)
8651 int actual_player_action = stored_player[i].effective_action;
8654 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8655 - rnd_equinox_tetrachloride 048
8656 - rnd_equinox_tetrachloride_ii 096
8657 - rnd_emanuel_schmieg 002
8658 - doctor_sloan_ww 001, 020
8660 if (stored_player[i].MovPos == 0)
8661 CheckGravityMovement(&stored_player[i]);
8665 /* overwrite programmed action with tape action */
8666 if (stored_player[i].programmed_action)
8667 actual_player_action = stored_player[i].programmed_action;
8671 if (stored_player[i].programmed_action)
8672 printf("::: %d\n", stored_player[i].programmed_action);
8675 if (recorded_player_action)
8678 if (stored_player[i].programmed_action &&
8679 stored_player[i].programmed_action != recorded_player_action[i])
8680 printf("::: %d: %d <-> %d\n", i,
8681 stored_player[i].programmed_action, recorded_player_action[i]);
8685 actual_player_action = recorded_player_action[i];
8690 /* overwrite tape action with programmed action */
8691 if (stored_player[i].programmed_action)
8692 actual_player_action = stored_player[i].programmed_action;
8697 printf("::: action: %d: %x [%d]\n",
8698 stored_player[i].MovPos, actual_player_action, FrameCounter);
8702 PlayerActions(&stored_player[i], actual_player_action);
8704 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8706 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8707 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8710 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8715 TapeRecordAction(tape_action);
8718 network_player_action_received = FALSE;
8720 ScrollScreen(NULL, SCROLL_GO_ON);
8726 for (i = 0; i < MAX_PLAYERS; i++)
8727 stored_player[i].Frame++;
8731 /* for backwards compatibility, the following code emulates a fixed bug that
8732 occured when pushing elements (causing elements that just made their last
8733 pushing step to already (if possible) make their first falling step in the
8734 same game frame, which is bad); this code is also needed to use the famous
8735 "spring push bug" which is used in older levels and might be wanted to be
8736 used also in newer levels, but in this case the buggy pushing code is only
8737 affecting the "spring" element and no other elements */
8740 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8742 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8745 for (i = 0; i < MAX_PLAYERS; i++)
8747 struct PlayerInfo *player = &stored_player[i];
8752 if (player->active && player->is_pushing && player->is_moving &&
8754 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8755 Feld[x][y] == EL_SPRING))
8757 if (player->active && player->is_pushing && player->is_moving &&
8761 ContinueMoving(x, y);
8763 /* continue moving after pushing (this is actually a bug) */
8764 if (!IS_MOVING(x, y))
8773 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8775 Changed[x][y] = CE_BITMASK_DEFAULT;
8776 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8778 #if USE_NEW_BLOCK_STYLE
8779 /* this must be handled before main playfield loop */
8780 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8783 if (MovDelay[x][y] <= 0)
8789 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8791 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8792 printf("GameActions(): This should never happen!\n");
8794 ChangePage[x][y] = -1;
8799 if (WasJustMoving[x][y] > 0)
8800 WasJustMoving[x][y]--;
8801 if (WasJustFalling[x][y] > 0)
8802 WasJustFalling[x][y]--;
8803 if (CheckCollision[x][y] > 0)
8804 CheckCollision[x][y]--;
8809 /* reset finished pushing action (not done in ContinueMoving() to allow
8810 continous pushing animation for elements with zero push delay) */
8811 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8813 ResetGfxAnimation(x, y);
8814 DrawLevelField(x, y);
8819 if (IS_BLOCKED(x, y))
8823 Blocked2Moving(x, y, &oldx, &oldy);
8824 if (!IS_MOVING(oldx, oldy))
8826 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8827 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8828 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8829 printf("GameActions(): This should never happen!\n");
8835 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8837 element = Feld[x][y];
8839 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8841 graphic = el2img(element);
8847 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8849 element = graphic = 0;
8853 if (graphic_info[graphic].anim_global_sync)
8854 GfxFrame[x][y] = FrameCounter;
8856 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8857 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8858 ResetRandomAnimationValue(x, y);
8860 SetRandomAnimationValue(x, y);
8863 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8866 if (IS_INACTIVE(element))
8868 if (IS_ANIMATED(graphic))
8869 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8875 /* this may take place after moving, so 'element' may have changed */
8877 if (IS_CHANGING(x, y))
8879 if (IS_CHANGING(x, y) &&
8880 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8884 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8885 element_info[element].event_page_nr[CE_DELAY]);
8887 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8890 element = Feld[x][y];
8891 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8895 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8900 element = Feld[x][y];
8901 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8903 if (element == EL_MOLE)
8904 printf("::: %d, %d, %d [%d]\n",
8905 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8909 if (element == EL_YAMYAM)
8910 printf("::: %d, %d, %d\n",
8911 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8915 if (IS_ANIMATED(graphic) &&
8919 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8922 if (element == EL_BUG)
8923 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8927 if (element == EL_MOLE)
8928 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8932 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8933 EdelsteinFunkeln(x, y);
8935 else if ((element == EL_ACID ||
8936 element == EL_EXIT_OPEN ||
8937 element == EL_SP_EXIT_OPEN ||
8938 element == EL_SP_TERMINAL ||
8939 element == EL_SP_TERMINAL_ACTIVE ||
8940 element == EL_EXTRA_TIME ||
8941 element == EL_SHIELD_NORMAL ||
8942 element == EL_SHIELD_DEADLY) &&
8943 IS_ANIMATED(graphic))
8944 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8945 else if (IS_MOVING(x, y))
8946 ContinueMoving(x, y);
8947 else if (IS_ACTIVE_BOMB(element))
8948 CheckDynamite(x, y);
8950 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8951 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8953 else if (element == EL_AMOEBA_GROWING)
8954 AmoebeWaechst(x, y);
8955 else if (element == EL_AMOEBA_SHRINKING)
8956 AmoebaDisappearing(x, y);
8958 #if !USE_NEW_AMOEBA_CODE
8959 else if (IS_AMOEBALIVE(element))
8960 AmoebeAbleger(x, y);
8963 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8965 else if (element == EL_EXIT_CLOSED)
8967 else if (element == EL_SP_EXIT_CLOSED)
8969 else if (element == EL_EXPANDABLE_WALL_GROWING)
8971 else if (element == EL_EXPANDABLE_WALL ||
8972 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8973 element == EL_EXPANDABLE_WALL_VERTICAL ||
8974 element == EL_EXPANDABLE_WALL_ANY)
8976 else if (element == EL_FLAMES)
8977 CheckForDragon(x, y);
8979 else if (IS_AUTO_CHANGING(element))
8980 ChangeElement(x, y);
8982 else if (element == EL_EXPLOSION)
8983 ; /* drawing of correct explosion animation is handled separately */
8984 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8985 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8988 /* this may take place after moving, so 'element' may have changed */
8989 if (IS_AUTO_CHANGING(Feld[x][y]))
8990 ChangeElement(x, y);
8993 if (IS_BELT_ACTIVE(element))
8994 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8996 if (game.magic_wall_active)
8998 int jx = local_player->jx, jy = local_player->jy;
9000 /* play the element sound at the position nearest to the player */
9001 if ((element == EL_MAGIC_WALL_FULL ||
9002 element == EL_MAGIC_WALL_ACTIVE ||
9003 element == EL_MAGIC_WALL_EMPTYING ||
9004 element == EL_BD_MAGIC_WALL_FULL ||
9005 element == EL_BD_MAGIC_WALL_ACTIVE ||
9006 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9007 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9015 #if USE_NEW_AMOEBA_CODE
9016 /* new experimental amoeba growth stuff */
9018 if (!(FrameCounter % 8))
9021 static unsigned long random = 1684108901;
9023 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9026 x = (random >> 10) % lev_fieldx;
9027 y = (random >> 20) % lev_fieldy;
9029 x = RND(lev_fieldx);
9030 y = RND(lev_fieldy);
9032 element = Feld[x][y];
9035 if (!IS_PLAYER(x,y) &&
9036 (element == EL_EMPTY ||
9037 CAN_GROW_INTO(element) ||
9038 element == EL_QUICKSAND_EMPTY ||
9039 element == EL_ACID_SPLASH_LEFT ||
9040 element == EL_ACID_SPLASH_RIGHT))
9042 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9043 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9044 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9045 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9046 Feld[x][y] = EL_AMOEBA_DROP;
9049 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9050 if (!IS_PLAYER(x,y) &&
9051 (element == EL_EMPTY ||
9052 element == EL_SAND ||
9053 element == EL_QUICKSAND_EMPTY ||
9054 element == EL_ACID_SPLASH_LEFT ||
9055 element == EL_ACID_SPLASH_RIGHT))
9057 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9058 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9059 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9060 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9061 Feld[x][y] = EL_AMOEBA_DROP;
9065 random = random * 129 + 1;
9071 if (game.explosions_delayed)
9074 game.explosions_delayed = FALSE;
9076 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9078 element = Feld[x][y];
9080 if (ExplodeField[x][y])
9081 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9082 else if (element == EL_EXPLOSION)
9083 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9085 ExplodeField[x][y] = EX_TYPE_NONE;
9088 game.explosions_delayed = TRUE;
9091 if (game.magic_wall_active)
9093 if (!(game.magic_wall_time_left % 4))
9095 int element = Feld[magic_wall_x][magic_wall_y];
9097 if (element == EL_BD_MAGIC_WALL_FULL ||
9098 element == EL_BD_MAGIC_WALL_ACTIVE ||
9099 element == EL_BD_MAGIC_WALL_EMPTYING)
9100 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9102 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9105 if (game.magic_wall_time_left > 0)
9107 game.magic_wall_time_left--;
9108 if (!game.magic_wall_time_left)
9110 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9112 element = Feld[x][y];
9114 if (element == EL_MAGIC_WALL_ACTIVE ||
9115 element == EL_MAGIC_WALL_FULL)
9117 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9118 DrawLevelField(x, y);
9120 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9121 element == EL_BD_MAGIC_WALL_FULL)
9123 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9124 DrawLevelField(x, y);
9128 game.magic_wall_active = FALSE;
9133 if (game.light_time_left > 0)
9135 game.light_time_left--;
9137 if (game.light_time_left == 0)
9138 RedrawAllLightSwitchesAndInvisibleElements();
9141 if (game.timegate_time_left > 0)
9143 game.timegate_time_left--;
9145 if (game.timegate_time_left == 0)
9146 CloseAllOpenTimegates();
9149 for (i = 0; i < MAX_PLAYERS; i++)
9151 struct PlayerInfo *player = &stored_player[i];
9153 if (SHIELD_ON(player))
9155 if (player->shield_deadly_time_left)
9156 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9157 else if (player->shield_normal_time_left)
9158 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9162 if (TimeFrames >= FRAMES_PER_SECOND)
9167 for (i = 0; i < MAX_PLAYERS; i++)
9169 struct PlayerInfo *player = &stored_player[i];
9171 if (SHIELD_ON(player))
9173 player->shield_normal_time_left--;
9175 if (player->shield_deadly_time_left > 0)
9176 player->shield_deadly_time_left--;
9180 if (!level.use_step_counter)
9188 if (TimeLeft <= 10 && setup.time_limit)
9189 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9191 DrawGameValue_Time(TimeLeft);
9193 if (!TimeLeft && setup.time_limit)
9194 for (i = 0; i < MAX_PLAYERS; i++)
9195 KillHero(&stored_player[i]);
9197 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9198 DrawGameValue_Time(TimePlayed);
9201 if (tape.recording || tape.playing)
9202 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9206 PlayAllPlayersSound();
9208 if (options.debug) /* calculate frames per second */
9210 static unsigned long fps_counter = 0;
9211 static int fps_frames = 0;
9212 unsigned long fps_delay_ms = Counter() - fps_counter;
9216 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9218 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9221 fps_counter = Counter();
9224 redraw_mask |= REDRAW_FPS;
9228 if (stored_player[0].jx != stored_player[0].last_jx ||
9229 stored_player[0].jy != stored_player[0].last_jy)
9230 printf("::: %d, %d, %d, %d, %d\n",
9231 stored_player[0].MovDir,
9232 stored_player[0].MovPos,
9233 stored_player[0].GfxPos,
9234 stored_player[0].Frame,
9235 stored_player[0].StepFrame);
9238 #if USE_NEW_MOVE_DELAY
9239 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9244 for (i = 0; i < MAX_PLAYERS; i++)
9247 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9249 stored_player[i].Frame += move_frames;
9251 if (stored_player[i].MovPos != 0)
9252 stored_player[i].StepFrame += move_frames;
9254 #if USE_NEW_MOVE_DELAY
9255 if (stored_player[i].move_delay > 0)
9256 stored_player[i].move_delay--;
9259 if (stored_player[i].drop_delay > 0)
9260 stored_player[i].drop_delay--;
9265 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9267 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9269 local_player->show_envelope = 0;
9273 #if USE_NEW_RANDOMIZE
9274 /* use random number generator in every frame to make it less predictable */
9275 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9280 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9282 int min_x = x, min_y = y, max_x = x, max_y = y;
9285 for (i = 0; i < MAX_PLAYERS; i++)
9287 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9289 if (!stored_player[i].active || &stored_player[i] == player)
9292 min_x = MIN(min_x, jx);
9293 min_y = MIN(min_y, jy);
9294 max_x = MAX(max_x, jx);
9295 max_y = MAX(max_y, jy);
9298 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9301 static boolean AllPlayersInVisibleScreen()
9305 for (i = 0; i < MAX_PLAYERS; i++)
9307 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9309 if (!stored_player[i].active)
9312 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9319 void ScrollLevel(int dx, int dy)
9321 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9324 BlitBitmap(drawto_field, drawto_field,
9325 FX + TILEX * (dx == -1) - softscroll_offset,
9326 FY + TILEY * (dy == -1) - softscroll_offset,
9327 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9328 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9329 FX + TILEX * (dx == 1) - softscroll_offset,
9330 FY + TILEY * (dy == 1) - softscroll_offset);
9334 x = (dx == 1 ? BX1 : BX2);
9335 for (y = BY1; y <= BY2; y++)
9336 DrawScreenField(x, y);
9341 y = (dy == 1 ? BY1 : BY2);
9342 for (x = BX1; x <= BX2; x++)
9343 DrawScreenField(x, y);
9346 redraw_mask |= REDRAW_FIELD;
9350 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9352 int nextx = x + dx, nexty = y + dy;
9353 int element = Feld[x][y];
9356 element != EL_SP_PORT_LEFT &&
9357 element != EL_SP_GRAVITY_PORT_LEFT &&
9358 element != EL_SP_PORT_HORIZONTAL &&
9359 element != EL_SP_PORT_ANY) ||
9361 element != EL_SP_PORT_RIGHT &&
9362 element != EL_SP_GRAVITY_PORT_RIGHT &&
9363 element != EL_SP_PORT_HORIZONTAL &&
9364 element != EL_SP_PORT_ANY) ||
9366 element != EL_SP_PORT_UP &&
9367 element != EL_SP_GRAVITY_PORT_UP &&
9368 element != EL_SP_PORT_VERTICAL &&
9369 element != EL_SP_PORT_ANY) ||
9371 element != EL_SP_PORT_DOWN &&
9372 element != EL_SP_GRAVITY_PORT_DOWN &&
9373 element != EL_SP_PORT_VERTICAL &&
9374 element != EL_SP_PORT_ANY) ||
9375 !IN_LEV_FIELD(nextx, nexty) ||
9376 !IS_FREE(nextx, nexty))
9383 static boolean canFallDown(struct PlayerInfo *player)
9385 int jx = player->jx, jy = player->jy;
9387 return (IN_LEV_FIELD(jx, jy + 1) &&
9388 (IS_FREE(jx, jy + 1) ||
9389 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9390 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9391 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9394 static boolean canPassField(int x, int y, int move_dir)
9396 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9397 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9398 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9401 int element = Feld[x][y];
9403 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9404 !CAN_MOVE(element) &&
9405 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9406 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9407 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9410 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9412 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9413 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9414 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9418 int nextx = newx + dx;
9419 int nexty = newy + dy;
9423 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9424 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9426 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9428 (IS_DIGGABLE(Feld[newx][newy]) ||
9429 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9430 canPassField(newx, newy, move_dir)));
9433 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9434 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9435 (IS_DIGGABLE(Feld[newx][newy]) ||
9436 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9437 canPassField(newx, newy, move_dir)));
9440 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9441 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9442 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9443 canPassField(newx, newy, move_dir)));
9445 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9446 (IS_DIGGABLE(Feld[newx][newy]) ||
9447 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9448 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9449 !CAN_MOVE(Feld[newx][newy]) &&
9450 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9451 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9452 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9458 static void CheckGravityMovement(struct PlayerInfo *player)
9460 if (game.gravity && !player->programmed_action)
9463 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9464 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9466 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9467 int move_dir_vertical = player->action & MV_VERTICAL;
9471 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9473 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9476 int jx = player->jx, jy = player->jy;
9478 boolean player_is_moving_to_valid_field =
9479 (!player_is_snapping &&
9480 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9481 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9485 (player->last_move_dir & MV_HORIZONTAL ?
9486 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9487 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9491 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9492 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9493 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9494 int new_jx = jx + dx, new_jy = jy + dy;
9495 int nextx = new_jx + dx, nexty = new_jy + dy;
9501 boolean player_can_fall_down = canFallDown(player);
9503 boolean player_can_fall_down =
9504 (IN_LEV_FIELD(jx, jy + 1) &&
9505 (IS_FREE(jx, jy + 1) ||
9506 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9510 boolean player_can_fall_down =
9511 (IN_LEV_FIELD(jx, jy + 1) &&
9512 (IS_FREE(jx, jy + 1)));
9516 boolean player_is_moving_to_valid_field =
9519 !player_is_snapping &&
9523 IN_LEV_FIELD(new_jx, new_jy) &&
9524 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9525 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9526 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9527 IN_LEV_FIELD(nextx, nexty) &&
9528 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9530 IN_LEV_FIELD(new_jx, new_jy) &&
9531 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9532 Feld[new_jx][new_jy] == EL_SAND ||
9533 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9534 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9535 /* !!! extend EL_SAND to anything diggable !!! */
9541 boolean player_is_standing_on_valid_field =
9542 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9543 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9547 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9548 player_can_fall_down,
9549 player_is_standing_on_valid_field,
9550 player_is_moving_to_valid_field,
9551 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9552 player->effective_action,
9553 player->can_fall_into_acid);
9556 if (player_can_fall_down &&
9558 !player_is_standing_on_valid_field &&
9560 !player_is_moving_to_valid_field)
9563 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9564 jx, jy, FrameCounter);
9567 player->programmed_action = MV_DOWN;
9572 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9575 return CheckGravityMovement(player);
9578 if (game.gravity && !player->programmed_action)
9580 int jx = player->jx, jy = player->jy;
9581 boolean field_under_player_is_free =
9582 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9583 boolean player_is_standing_on_valid_field =
9584 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9585 (IS_WALKABLE(Feld[jx][jy]) &&
9586 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9588 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9589 player->programmed_action = MV_DOWN;
9595 -----------------------------------------------------------------------------
9596 dx, dy: direction (non-diagonal) to try to move the player to
9597 real_dx, real_dy: direction as read from input device (can be diagonal)
9600 boolean MovePlayerOneStep(struct PlayerInfo *player,
9601 int dx, int dy, int real_dx, int real_dy)
9604 static int trigger_sides[4][2] =
9606 /* enter side leave side */
9607 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9608 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9609 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9610 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9612 int move_direction = (dx == -1 ? MV_LEFT :
9613 dx == +1 ? MV_RIGHT :
9615 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9616 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9617 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9619 int jx = player->jx, jy = player->jy;
9620 int new_jx = jx + dx, new_jy = jy + dy;
9624 if (!player->active || (!dx && !dy))
9625 return MF_NO_ACTION;
9627 player->MovDir = (dx < 0 ? MV_LEFT :
9630 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9632 if (!IN_LEV_FIELD(new_jx, new_jy))
9633 return MF_NO_ACTION;
9635 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9636 return MF_NO_ACTION;
9639 element = MovingOrBlocked2Element(new_jx, new_jy);
9641 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9644 if (DONT_RUN_INTO(element))
9646 if (element == EL_ACID && dx == 0 && dy == 1)
9648 SplashAcid(new_jx, new_jy);
9649 Feld[jx][jy] = EL_PLAYER_1;
9650 InitMovingField(jx, jy, MV_DOWN);
9651 Store[jx][jy] = EL_ACID;
9652 ContinueMoving(jx, jy);
9656 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9661 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9662 if (can_move != MF_MOVING)
9665 /* check if DigField() has caused relocation of the player */
9666 if (player->jx != jx || player->jy != jy)
9667 return MF_NO_ACTION;
9669 StorePlayer[jx][jy] = 0;
9670 player->last_jx = jx;
9671 player->last_jy = jy;
9672 player->jx = new_jx;
9673 player->jy = new_jy;
9674 StorePlayer[new_jx][new_jy] = player->element_nr;
9677 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9679 player->step_counter++;
9682 player->drop_delay = 0;
9685 PlayerVisit[jx][jy] = FrameCounter;
9687 ScrollPlayer(player, SCROLL_INIT);
9690 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9692 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9694 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9697 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9699 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9700 CE_OTHER_GETS_ENTERED, enter_side);
9701 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9702 CE_ENTERED_BY_PLAYER, enter_side);
9709 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9711 int jx = player->jx, jy = player->jy;
9712 int old_jx = jx, old_jy = jy;
9713 int moved = MF_NO_ACTION;
9716 if (!player->active)
9721 if (player->MovPos == 0)
9723 player->is_moving = FALSE;
9724 player->is_digging = FALSE;
9725 player->is_collecting = FALSE;
9726 player->is_snapping = FALSE;
9727 player->is_pushing = FALSE;
9733 if (!player->active || (!dx && !dy))
9738 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9746 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9747 player->move_delay + player->move_delay_value);
9750 #if USE_NEW_MOVE_DELAY
9751 if (player->move_delay > 0)
9753 if (!FrameReached(&player->move_delay, player->move_delay_value))
9757 printf("::: can NOT move\n");
9763 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9764 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9771 printf("::: COULD move now\n");
9774 #if USE_NEW_MOVE_DELAY
9775 player->move_delay = -1; /* set to "uninitialized" value */
9778 /* store if player is automatically moved to next field */
9779 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9781 /* remove the last programmed player action */
9782 player->programmed_action = 0;
9786 /* should only happen if pre-1.2 tape recordings are played */
9787 /* this is only for backward compatibility */
9789 int original_move_delay_value = player->move_delay_value;
9792 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9796 /* scroll remaining steps with finest movement resolution */
9797 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9799 while (player->MovPos)
9801 ScrollPlayer(player, SCROLL_GO_ON);
9802 ScrollScreen(NULL, SCROLL_GO_ON);
9804 #if USE_NEW_MOVE_DELAY
9805 AdvanceFrameAndPlayerCounters(player->index_nr);
9814 player->move_delay_value = original_move_delay_value;
9817 if (player->last_move_dir & MV_HORIZONTAL)
9819 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9820 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9824 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9825 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9831 if (moved & MF_MOVING && !ScreenMovPos &&
9832 (player == local_player || !options.network))
9834 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9835 int offset = (setup.scroll_delay ? 3 : 0);
9837 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9839 /* actual player has left the screen -- scroll in that direction */
9840 if (jx != old_jx) /* player has moved horizontally */
9841 scroll_x += (jx - old_jx);
9842 else /* player has moved vertically */
9843 scroll_y += (jy - old_jy);
9847 if (jx != old_jx) /* player has moved horizontally */
9849 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9850 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9851 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9853 /* don't scroll over playfield boundaries */
9854 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9855 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9857 /* don't scroll more than one field at a time */
9858 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9860 /* don't scroll against the player's moving direction */
9861 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9862 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9863 scroll_x = old_scroll_x;
9865 else /* player has moved vertically */
9867 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9868 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9869 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9871 /* don't scroll over playfield boundaries */
9872 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9873 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9875 /* don't scroll more than one field at a time */
9876 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9878 /* don't scroll against the player's moving direction */
9879 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9880 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9881 scroll_y = old_scroll_y;
9885 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9887 if (!options.network && !AllPlayersInVisibleScreen())
9889 scroll_x = old_scroll_x;
9890 scroll_y = old_scroll_y;
9894 ScrollScreen(player, SCROLL_INIT);
9895 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9902 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9904 if (!(moved & MF_MOVING) && !player->is_pushing)
9909 player->StepFrame = 0;
9911 if (moved & MF_MOVING)
9914 printf("::: REALLY moves now\n");
9917 if (old_jx != jx && old_jy == jy)
9918 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9919 else if (old_jx == jx && old_jy != jy)
9920 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9922 DrawLevelField(jx, jy); /* for "crumbled sand" */
9924 player->last_move_dir = player->MovDir;
9925 player->is_moving = TRUE;
9927 player->is_snapping = FALSE;
9931 player->is_switching = FALSE;
9934 player->is_dropping = FALSE;
9938 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9941 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9944 int move_direction = player->MovDir;
9946 int enter_side = MV_DIR_OPPOSITE(move_direction);
9947 int leave_side = move_direction;
9949 static int trigger_sides[4][2] =
9951 /* enter side leave side */
9952 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9953 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9954 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9955 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9957 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9958 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9960 int old_element = Feld[old_jx][old_jy];
9961 int new_element = Feld[jx][jy];
9964 /* !!! TEST ONLY !!! */
9965 if (IS_CUSTOM_ELEMENT(old_element))
9966 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9968 player->index_bit, leave_side);
9970 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9972 player->index_bit, leave_side);
9974 if (IS_CUSTOM_ELEMENT(new_element))
9975 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9976 player->index_bit, enter_side);
9978 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9979 CE_OTHER_GETS_ENTERED,
9980 player->index_bit, enter_side);
9990 CheckGravityMovementWhenNotMoving(player);
9993 player->last_move_dir = MV_NO_MOVING;
9995 player->is_moving = FALSE;
9997 #if USE_NEW_MOVE_STYLE
9998 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9999 /* ensure that the player is also allowed to move in the next frame */
10000 /* (currently, the player is forced to wait eight frames before he can try
10003 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10004 player->move_delay = 0; /* allow direct movement in the next frame */
10008 #if USE_NEW_MOVE_DELAY
10009 if (player->move_delay == -1) /* not yet initialized by DigField() */
10010 player->move_delay = player->move_delay_value;
10013 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10015 TestIfHeroTouchesBadThing(jx, jy);
10016 TestIfPlayerTouchesCustomElement(jx, jy);
10019 if (!player->active)
10020 RemoveHero(player);
10025 void ScrollPlayer(struct PlayerInfo *player, int mode)
10027 int jx = player->jx, jy = player->jy;
10028 int last_jx = player->last_jx, last_jy = player->last_jy;
10029 int move_stepsize = TILEX / player->move_delay_value;
10031 if (!player->active || !player->MovPos)
10034 if (mode == SCROLL_INIT)
10036 player->actual_frame_counter = FrameCounter;
10037 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10040 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10042 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10043 player->block_delay);
10046 #if USE_NEW_BLOCK_STYLE
10049 if (player->block_delay <= 0)
10050 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10053 if (player->block_delay > 0 &&
10054 Feld[last_jx][last_jy] == EL_EMPTY)
10056 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10057 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10060 #if USE_NEW_MOVE_STYLE
10061 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10062 player->block_last_field) &&
10063 Feld[last_jx][last_jy] == EL_EMPTY)
10064 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10066 if (Feld[last_jx][last_jy] == EL_EMPTY)
10067 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10072 DrawPlayer(player);
10077 else if (!FrameReached(&player->actual_frame_counter, 1))
10080 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10081 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10083 #if USE_NEW_BLOCK_STYLE
10085 if (!player->block_last_field &&
10086 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10088 RemoveField(last_jx, last_jy);
10090 Feld[last_jx][last_jy] = EL_EMPTY;
10094 /* before DrawPlayer() to draw correct player graphic for this case */
10095 if (player->MovPos == 0)
10096 CheckGravityMovement(player);
10099 DrawPlayer(player); /* needed here only to cleanup last field */
10102 if (player->MovPos == 0) /* player reached destination field */
10105 if (player->move_delay_reset_counter > 0)
10107 player->move_delay_reset_counter--;
10109 if (player->move_delay_reset_counter == 0)
10111 /* continue with normal speed after quickly moving through gate */
10112 HALVE_PLAYER_SPEED(player);
10114 /* be able to make the next move without delay */
10115 player->move_delay = 0;
10119 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10121 /* continue with normal speed after quickly moving through gate */
10122 HALVE_PLAYER_SPEED(player);
10124 /* be able to make the next move without delay */
10125 player->move_delay = 0;
10129 #if USE_NEW_BLOCK_STYLE
10131 if (player->block_last_field &&
10132 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10134 RemoveField(last_jx, last_jy);
10136 Feld[last_jx][last_jy] = EL_EMPTY;
10140 player->last_jx = jx;
10141 player->last_jy = jy;
10143 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10144 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10145 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10147 DrawPlayer(player); /* needed here only to cleanup last field */
10148 RemoveHero(player);
10150 if (local_player->friends_still_needed == 0 ||
10151 IS_SP_ELEMENT(Feld[jx][jy]))
10152 player->LevelSolved = player->GameOver = TRUE;
10156 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10157 /* this breaks one level: "machine", level 000 */
10159 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10162 int move_direction = player->MovDir;
10164 int enter_side = MV_DIR_OPPOSITE(move_direction);
10165 int leave_side = move_direction;
10167 static int trigger_sides[4][2] =
10169 /* enter side leave side */
10170 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10171 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10172 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10173 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10175 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10176 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10178 int old_jx = last_jx;
10179 int old_jy = last_jy;
10180 int old_element = Feld[old_jx][old_jy];
10181 int new_element = Feld[jx][jy];
10184 /* !!! TEST ONLY !!! */
10185 if (IS_CUSTOM_ELEMENT(old_element))
10186 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10188 player->index_bit, leave_side);
10190 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10191 CE_OTHER_GETS_LEFT,
10192 player->index_bit, leave_side);
10194 if (IS_CUSTOM_ELEMENT(new_element))
10195 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10196 player->index_bit, enter_side);
10198 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10199 CE_OTHER_GETS_ENTERED,
10200 player->index_bit, enter_side);
10206 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10208 TestIfHeroTouchesBadThing(jx, jy);
10209 TestIfPlayerTouchesCustomElement(jx, jy);
10212 /* needed because pushed element has not yet reached its destination,
10213 so it would trigger a change event at its previous field location */
10214 if (!player->is_pushing)
10216 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10219 if (!player->active)
10220 RemoveHero(player);
10223 if (level.use_step_counter)
10233 if (TimeLeft <= 10 && setup.time_limit)
10234 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10236 DrawGameValue_Time(TimeLeft);
10238 if (!TimeLeft && setup.time_limit)
10239 for (i = 0; i < MAX_PLAYERS; i++)
10240 KillHero(&stored_player[i]);
10242 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10243 DrawGameValue_Time(TimePlayed);
10246 if (tape.single_step && tape.recording && !tape.pausing &&
10247 !player->programmed_action)
10248 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10252 void ScrollScreen(struct PlayerInfo *player, int mode)
10254 static unsigned long screen_frame_counter = 0;
10256 if (mode == SCROLL_INIT)
10258 /* set scrolling step size according to actual player's moving speed */
10259 ScrollStepSize = TILEX / player->move_delay_value;
10261 screen_frame_counter = FrameCounter;
10262 ScreenMovDir = player->MovDir;
10263 ScreenMovPos = player->MovPos;
10264 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10267 else if (!FrameReached(&screen_frame_counter, 1))
10272 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10273 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10274 redraw_mask |= REDRAW_FIELD;
10277 ScreenMovDir = MV_NO_MOVING;
10280 void TestIfPlayerTouchesCustomElement(int x, int y)
10282 static int xy[4][2] =
10289 static int trigger_sides[4][2] =
10291 /* center side border side */
10292 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10293 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10294 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10295 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10297 static int touch_dir[4] =
10299 MV_LEFT | MV_RIGHT,
10304 int center_element = Feld[x][y]; /* should always be non-moving! */
10307 for (i = 0; i < NUM_DIRECTIONS; i++)
10309 int xx = x + xy[i][0];
10310 int yy = y + xy[i][1];
10311 int center_side = trigger_sides[i][0];
10312 int border_side = trigger_sides[i][1];
10313 int border_element;
10315 if (!IN_LEV_FIELD(xx, yy))
10318 if (IS_PLAYER(x, y))
10320 struct PlayerInfo *player = PLAYERINFO(x, y);
10322 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10323 border_element = Feld[xx][yy]; /* may be moving! */
10324 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10325 border_element = Feld[xx][yy];
10326 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10327 border_element = MovingOrBlocked2Element(xx, yy);
10329 continue; /* center and border element do not touch */
10332 /* !!! TEST ONLY !!! */
10333 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10334 player->index_bit, border_side);
10335 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10336 CE_OTHER_GETS_TOUCHED,
10337 player->index_bit, border_side);
10339 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10340 CE_OTHER_GETS_TOUCHED,
10341 player->index_bit, border_side);
10342 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10343 player->index_bit, border_side);
10346 else if (IS_PLAYER(xx, yy))
10348 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10350 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10352 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10353 continue; /* center and border element do not touch */
10357 /* !!! TEST ONLY !!! */
10358 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10359 player->index_bit, center_side);
10360 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10361 CE_OTHER_GETS_TOUCHED,
10362 player->index_bit, center_side);
10364 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10365 CE_OTHER_GETS_TOUCHED,
10366 player->index_bit, center_side);
10367 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10368 player->index_bit, center_side);
10376 void TestIfElementTouchesCustomElement(int x, int y)
10378 static int xy[4][2] =
10385 static int trigger_sides[4][2] =
10387 /* center side border side */
10388 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10389 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10390 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10391 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10393 static int touch_dir[4] =
10395 MV_LEFT | MV_RIGHT,
10400 boolean change_center_element = FALSE;
10401 int center_element_change_page = 0;
10402 int center_element = Feld[x][y]; /* should always be non-moving! */
10403 int border_trigger_element = EL_UNDEFINED;
10406 for (i = 0; i < NUM_DIRECTIONS; i++)
10408 int xx = x + xy[i][0];
10409 int yy = y + xy[i][1];
10410 int center_side = trigger_sides[i][0];
10411 int border_side = trigger_sides[i][1];
10412 int border_element;
10414 if (!IN_LEV_FIELD(xx, yy))
10417 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10418 border_element = Feld[xx][yy]; /* may be moving! */
10419 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10420 border_element = Feld[xx][yy];
10421 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10422 border_element = MovingOrBlocked2Element(xx, yy);
10424 continue; /* center and border element do not touch */
10426 /* check for change of center element (but change it only once) */
10427 if (IS_CUSTOM_ELEMENT(center_element) &&
10428 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10429 !change_center_element)
10431 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10433 struct ElementChangeInfo *change =
10434 &element_info[center_element].change_page[j];
10436 if (change->can_change &&
10437 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10438 change->trigger_side & border_side &&
10440 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10442 change->trigger_element == border_element
10446 change_center_element = TRUE;
10447 center_element_change_page = j;
10448 border_trigger_element = border_element;
10455 /* check for change of border element */
10456 if (IS_CUSTOM_ELEMENT(border_element) &&
10457 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10459 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10461 struct ElementChangeInfo *change =
10462 &element_info[border_element].change_page[j];
10464 if (change->can_change &&
10465 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10466 change->trigger_side & center_side &&
10468 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10470 change->trigger_element == center_element
10475 printf("::: border_element %d, %d\n", x, y);
10478 CheckElementChangeByPage(xx, yy, border_element, center_element,
10479 CE_OTHER_IS_TOUCHING, j);
10486 if (change_center_element)
10489 printf("::: center_element %d, %d\n", x, y);
10492 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10493 CE_OTHER_IS_TOUCHING, center_element_change_page);
10497 void TestIfElementHitsCustomElement(int x, int y, int direction)
10499 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10500 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10501 int hitx = x + dx, hity = y + dy;
10502 int hitting_element = Feld[x][y];
10503 int touched_element;
10505 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10506 !IS_FREE(hitx, hity) &&
10507 (!IS_MOVING(hitx, hity) ||
10508 MovDir[hitx][hity] != direction ||
10509 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10512 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10516 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10520 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10521 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10523 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10524 CE_HITTING_SOMETHING, direction);
10526 if (IN_LEV_FIELD(hitx, hity))
10528 int opposite_direction = MV_DIR_OPPOSITE(direction);
10529 int hitting_side = direction;
10530 int touched_side = opposite_direction;
10532 int touched_element = MovingOrBlocked2Element(hitx, hity);
10535 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10536 MovDir[hitx][hity] != direction ||
10537 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10546 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10547 CE_HIT_BY_SOMETHING, opposite_direction);
10549 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10550 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10552 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10554 struct ElementChangeInfo *change =
10555 &element_info[hitting_element].change_page[i];
10557 if (change->can_change &&
10558 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10559 change->trigger_side & touched_side &&
10562 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10564 change->trigger_element == touched_element
10568 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10569 CE_OTHER_IS_HITTING, i);
10575 if (IS_CUSTOM_ELEMENT(touched_element) &&
10576 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10578 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10580 struct ElementChangeInfo *change =
10581 &element_info[touched_element].change_page[i];
10583 if (change->can_change &&
10584 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10585 change->trigger_side & hitting_side &&
10587 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10589 change->trigger_element == hitting_element
10593 CheckElementChangeByPage(hitx, hity, touched_element,
10594 hitting_element, CE_OTHER_GETS_HIT, i);
10604 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10606 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10607 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10608 int hitx = x + dx, hity = y + dy;
10609 int hitting_element = Feld[x][y];
10610 int touched_element;
10612 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10613 !IS_FREE(hitx, hity) &&
10614 (!IS_MOVING(hitx, hity) ||
10615 MovDir[hitx][hity] != direction ||
10616 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10619 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10623 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10627 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10628 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10630 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10631 EP_CAN_SMASH_EVERYTHING, direction);
10633 if (IN_LEV_FIELD(hitx, hity))
10635 int opposite_direction = MV_DIR_OPPOSITE(direction);
10636 int hitting_side = direction;
10637 int touched_side = opposite_direction;
10639 int touched_element = MovingOrBlocked2Element(hitx, hity);
10642 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10643 MovDir[hitx][hity] != direction ||
10644 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10653 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10654 CE_SMASHED_BY_SOMETHING, opposite_direction);
10656 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10657 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10659 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10661 struct ElementChangeInfo *change =
10662 &element_info[hitting_element].change_page[i];
10664 if (change->can_change &&
10665 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10666 change->trigger_side & touched_side &&
10669 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10671 change->trigger_element == touched_element
10675 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10676 CE_OTHER_IS_SMASHING, i);
10682 if (IS_CUSTOM_ELEMENT(touched_element) &&
10683 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10685 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10687 struct ElementChangeInfo *change =
10688 &element_info[touched_element].change_page[i];
10690 if (change->can_change &&
10691 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10692 change->trigger_side & hitting_side &&
10694 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10696 change->trigger_element == hitting_element
10700 CheckElementChangeByPage(hitx, hity, touched_element,
10701 hitting_element, CE_OTHER_GETS_SMASHED,i);
10711 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10713 int i, kill_x = -1, kill_y = -1;
10714 int bad_element = -1;
10715 static int test_xy[4][2] =
10722 static int test_dir[4] =
10730 for (i = 0; i < NUM_DIRECTIONS; i++)
10732 int test_x, test_y, test_move_dir, test_element;
10734 test_x = good_x + test_xy[i][0];
10735 test_y = good_y + test_xy[i][1];
10737 if (!IN_LEV_FIELD(test_x, test_y))
10741 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10744 test_element = Feld[test_x][test_y];
10746 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10749 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10750 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10752 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10753 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10757 bad_element = test_element;
10763 if (kill_x != -1 || kill_y != -1)
10765 if (IS_PLAYER(good_x, good_y))
10767 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10770 if (player->shield_deadly_time_left > 0 &&
10771 !IS_INDESTRUCTIBLE(bad_element))
10772 Bang(kill_x, kill_y);
10773 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10776 if (player->shield_deadly_time_left > 0)
10777 Bang(kill_x, kill_y);
10778 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10783 Bang(good_x, good_y);
10787 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10789 int i, kill_x = -1, kill_y = -1;
10790 int bad_element = Feld[bad_x][bad_y];
10791 static int test_xy[4][2] =
10798 static int touch_dir[4] =
10800 MV_LEFT | MV_RIGHT,
10805 static int test_dir[4] =
10813 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10816 for (i = 0; i < NUM_DIRECTIONS; i++)
10818 int test_x, test_y, test_move_dir, test_element;
10820 test_x = bad_x + test_xy[i][0];
10821 test_y = bad_y + test_xy[i][1];
10822 if (!IN_LEV_FIELD(test_x, test_y))
10826 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10828 test_element = Feld[test_x][test_y];
10830 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10831 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10833 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10834 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10836 /* good thing is player or penguin that does not move away */
10837 if (IS_PLAYER(test_x, test_y))
10839 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10841 if (bad_element == EL_ROBOT && player->is_moving)
10842 continue; /* robot does not kill player if he is moving */
10844 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10846 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10847 continue; /* center and border element do not touch */
10854 else if (test_element == EL_PENGUIN)
10863 if (kill_x != -1 || kill_y != -1)
10865 if (IS_PLAYER(kill_x, kill_y))
10867 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10870 if (player->shield_deadly_time_left > 0 &&
10871 !IS_INDESTRUCTIBLE(bad_element))
10872 Bang(bad_x, bad_y);
10873 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10876 if (player->shield_deadly_time_left > 0)
10877 Bang(bad_x, bad_y);
10878 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10883 Bang(kill_x, kill_y);
10887 void TestIfHeroTouchesBadThing(int x, int y)
10889 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10892 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10894 TestIfGoodThingHitsBadThing(x, y, move_dir);
10897 void TestIfBadThingTouchesHero(int x, int y)
10899 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10902 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10904 TestIfBadThingHitsGoodThing(x, y, move_dir);
10907 void TestIfFriendTouchesBadThing(int x, int y)
10909 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10912 void TestIfBadThingTouchesFriend(int x, int y)
10914 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10917 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10919 int i, kill_x = bad_x, kill_y = bad_y;
10920 static int xy[4][2] =
10928 for (i = 0; i < NUM_DIRECTIONS; i++)
10932 x = bad_x + xy[i][0];
10933 y = bad_y + xy[i][1];
10934 if (!IN_LEV_FIELD(x, y))
10937 element = Feld[x][y];
10938 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10939 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10947 if (kill_x != bad_x || kill_y != bad_y)
10948 Bang(bad_x, bad_y);
10951 void KillHero(struct PlayerInfo *player)
10953 int jx = player->jx, jy = player->jy;
10955 if (!player->active)
10958 /* remove accessible field at the player's position */
10959 Feld[jx][jy] = EL_EMPTY;
10961 /* deactivate shield (else Bang()/Explode() would not work right) */
10962 player->shield_normal_time_left = 0;
10963 player->shield_deadly_time_left = 0;
10969 static void KillHeroUnlessEnemyProtected(int x, int y)
10971 if (!PLAYER_ENEMY_PROTECTED(x, y))
10972 KillHero(PLAYERINFO(x, y));
10975 static void KillHeroUnlessExplosionProtected(int x, int y)
10977 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10978 KillHero(PLAYERINFO(x, y));
10981 void BuryHero(struct PlayerInfo *player)
10983 int jx = player->jx, jy = player->jy;
10985 if (!player->active)
10989 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10991 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10993 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10995 player->GameOver = TRUE;
10996 RemoveHero(player);
10999 void RemoveHero(struct PlayerInfo *player)
11001 int jx = player->jx, jy = player->jy;
11002 int i, found = FALSE;
11004 player->present = FALSE;
11005 player->active = FALSE;
11007 if (!ExplodeField[jx][jy])
11008 StorePlayer[jx][jy] = 0;
11010 for (i = 0; i < MAX_PLAYERS; i++)
11011 if (stored_player[i].active)
11015 AllPlayersGone = TRUE;
11022 =============================================================================
11023 checkDiagonalPushing()
11024 -----------------------------------------------------------------------------
11025 check if diagonal input device direction results in pushing of object
11026 (by checking if the alternative direction is walkable, diggable, ...)
11027 =============================================================================
11030 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11031 int x, int y, int real_dx, int real_dy)
11033 int jx, jy, dx, dy, xx, yy;
11035 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11038 /* diagonal direction: check alternative direction */
11043 xx = jx + (dx == 0 ? real_dx : 0);
11044 yy = jy + (dy == 0 ? real_dy : 0);
11046 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11050 =============================================================================
11052 -----------------------------------------------------------------------------
11053 x, y: field next to player (non-diagonal) to try to dig to
11054 real_dx, real_dy: direction as read from input device (can be diagonal)
11055 =============================================================================
11058 int DigField(struct PlayerInfo *player,
11059 int oldx, int oldy, int x, int y,
11060 int real_dx, int real_dy, int mode)
11063 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11065 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11066 boolean player_was_pushing = player->is_pushing;
11067 int jx = oldx, jy = oldy;
11068 int dx = x - jx, dy = y - jy;
11069 int nextx = x + dx, nexty = y + dy;
11070 int move_direction = (dx == -1 ? MV_LEFT :
11071 dx == +1 ? MV_RIGHT :
11073 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11074 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11076 int dig_side = MV_DIR_OPPOSITE(move_direction);
11078 static int trigger_sides[4] =
11080 CH_SIDE_RIGHT, /* moving left */
11081 CH_SIDE_LEFT, /* moving right */
11082 CH_SIDE_BOTTOM, /* moving up */
11083 CH_SIDE_TOP, /* moving down */
11085 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11087 int old_element = Feld[jx][jy];
11090 if (is_player) /* function can also be called by EL_PENGUIN */
11092 if (player->MovPos == 0)
11094 player->is_digging = FALSE;
11095 player->is_collecting = FALSE;
11098 if (player->MovPos == 0) /* last pushing move finished */
11099 player->is_pushing = FALSE;
11101 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11103 player->is_switching = FALSE;
11104 #if USE_NEW_PUSH_DELAY
11105 player->push_delay = -1;
11107 player->push_delay = 0;
11110 return MF_NO_ACTION;
11114 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11115 return MF_NO_ACTION;
11120 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11122 if (IS_TUBE(Feld[jx][jy]) ||
11123 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11127 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11128 int tube_leave_directions[][2] =
11130 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11131 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11132 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11133 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11134 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11135 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11136 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11137 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11138 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11139 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11140 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11141 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11144 while (tube_leave_directions[i][0] != tube_element)
11147 if (tube_leave_directions[i][0] == -1) /* should not happen */
11151 if (!(tube_leave_directions[i][1] & move_direction))
11152 return MF_NO_ACTION; /* tube has no opening in this direction */
11157 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11158 old_element = Back[jx][jy];
11162 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11163 return MF_NO_ACTION; /* field has no opening in this direction */
11165 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11166 return MF_NO_ACTION; /* field has no opening in this direction */
11168 element = Feld[x][y];
11170 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11171 return MF_NO_ACTION;
11173 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11174 game.engine_version >= VERSION_IDENT(2,2,0,0))
11175 return MF_NO_ACTION;
11178 if (game.gravity && is_player && !player->is_auto_moving &&
11179 canFallDown(player) && move_direction != MV_DOWN &&
11180 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11181 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11185 if (element == EL_EMPTY_SPACE &&
11186 game.gravity && !player->is_auto_moving &&
11187 canFallDown(player) && move_direction != MV_DOWN)
11188 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11194 case EL_SP_PORT_LEFT:
11195 case EL_SP_PORT_RIGHT:
11196 case EL_SP_PORT_UP:
11197 case EL_SP_PORT_DOWN:
11198 case EL_SP_PORT_HORIZONTAL:
11199 case EL_SP_PORT_VERTICAL:
11200 case EL_SP_PORT_ANY:
11201 case EL_SP_GRAVITY_PORT_LEFT:
11202 case EL_SP_GRAVITY_PORT_RIGHT:
11203 case EL_SP_GRAVITY_PORT_UP:
11204 case EL_SP_GRAVITY_PORT_DOWN:
11206 if (!canEnterSupaplexPort(x, y, dx, dy))
11207 return MF_NO_ACTION;
11210 element != EL_SP_PORT_LEFT &&
11211 element != EL_SP_GRAVITY_PORT_LEFT &&
11212 element != EL_SP_PORT_HORIZONTAL &&
11213 element != EL_SP_PORT_ANY) ||
11215 element != EL_SP_PORT_RIGHT &&
11216 element != EL_SP_GRAVITY_PORT_RIGHT &&
11217 element != EL_SP_PORT_HORIZONTAL &&
11218 element != EL_SP_PORT_ANY) ||
11220 element != EL_SP_PORT_UP &&
11221 element != EL_SP_GRAVITY_PORT_UP &&
11222 element != EL_SP_PORT_VERTICAL &&
11223 element != EL_SP_PORT_ANY) ||
11225 element != EL_SP_PORT_DOWN &&
11226 element != EL_SP_GRAVITY_PORT_DOWN &&
11227 element != EL_SP_PORT_VERTICAL &&
11228 element != EL_SP_PORT_ANY) ||
11229 !IN_LEV_FIELD(nextx, nexty) ||
11230 !IS_FREE(nextx, nexty))
11231 return MF_NO_ACTION;
11234 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11235 element == EL_SP_GRAVITY_PORT_RIGHT ||
11236 element == EL_SP_GRAVITY_PORT_UP ||
11237 element == EL_SP_GRAVITY_PORT_DOWN)
11238 game.gravity = !game.gravity;
11240 /* automatically move to the next field with double speed */
11241 player->programmed_action = move_direction;
11243 if (player->move_delay_reset_counter == 0)
11245 player->move_delay_reset_counter = 2; /* two double speed steps */
11247 DOUBLE_PLAYER_SPEED(player);
11250 player->move_delay_reset_counter = 2;
11252 DOUBLE_PLAYER_SPEED(player);
11256 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11259 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11265 case EL_TUBE_VERTICAL:
11266 case EL_TUBE_HORIZONTAL:
11267 case EL_TUBE_VERTICAL_LEFT:
11268 case EL_TUBE_VERTICAL_RIGHT:
11269 case EL_TUBE_HORIZONTAL_UP:
11270 case EL_TUBE_HORIZONTAL_DOWN:
11271 case EL_TUBE_LEFT_UP:
11272 case EL_TUBE_LEFT_DOWN:
11273 case EL_TUBE_RIGHT_UP:
11274 case EL_TUBE_RIGHT_DOWN:
11277 int tube_enter_directions[][2] =
11279 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11280 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11281 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11282 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11283 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11284 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11285 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11286 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11287 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11288 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11289 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11290 { -1, MV_NO_MOVING }
11293 while (tube_enter_directions[i][0] != element)
11296 if (tube_enter_directions[i][0] == -1) /* should not happen */
11300 if (!(tube_enter_directions[i][1] & move_direction))
11301 return MF_NO_ACTION; /* tube has no opening in this direction */
11303 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11311 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11313 if (IS_WALKABLE(element))
11316 int sound_element = SND_ELEMENT(element);
11317 int sound_action = ACTION_WALKING;
11320 if (!ACCESS_FROM(element, opposite_direction))
11321 return MF_NO_ACTION; /* field not accessible from this direction */
11325 if (element == EL_EMPTY_SPACE &&
11326 game.gravity && !player->is_auto_moving &&
11327 canFallDown(player) && move_direction != MV_DOWN)
11328 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11331 if (IS_GATE(element))
11333 if (!player->key[element - EL_GATE_1])
11334 return MF_NO_ACTION;
11336 else if (IS_GATE_GRAY(element))
11338 if (!player->key[element - EL_GATE_1_GRAY])
11339 return MF_NO_ACTION;
11341 else if (element == EL_EXIT_OPEN ||
11342 element == EL_SP_EXIT_OPEN ||
11343 element == EL_SP_EXIT_OPENING)
11345 sound_action = ACTION_PASSING; /* player is passing exit */
11347 else if (element == EL_EMPTY)
11349 sound_action = ACTION_MOVING; /* nothing to walk on */
11352 /* play sound from background or player, whatever is available */
11353 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11354 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11356 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11361 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11363 else if (IS_PASSABLE(element))
11367 if (!canPassField(x, y, move_direction))
11368 return MF_NO_ACTION;
11373 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11374 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11375 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11376 return MF_NO_ACTION;
11378 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11379 return MF_NO_ACTION;
11384 if (!ACCESS_FROM(element, opposite_direction))
11385 return MF_NO_ACTION; /* field not accessible from this direction */
11387 if (IS_CUSTOM_ELEMENT(element) &&
11388 !ACCESS_FROM(element, opposite_direction))
11389 return MF_NO_ACTION; /* field not accessible from this direction */
11393 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11394 return MF_NO_ACTION;
11399 if (IS_EM_GATE(element))
11401 if (!player->key[element - EL_EM_GATE_1])
11402 return MF_NO_ACTION;
11404 else if (IS_EM_GATE_GRAY(element))
11406 if (!player->key[element - EL_EM_GATE_1_GRAY])
11407 return MF_NO_ACTION;
11409 else if (IS_SP_PORT(element))
11411 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11412 element == EL_SP_GRAVITY_PORT_RIGHT ||
11413 element == EL_SP_GRAVITY_PORT_UP ||
11414 element == EL_SP_GRAVITY_PORT_DOWN)
11415 game.gravity = !game.gravity;
11416 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11417 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11418 element == EL_SP_GRAVITY_ON_PORT_UP ||
11419 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11420 game.gravity = TRUE;
11421 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11422 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11423 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11424 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11425 game.gravity = FALSE;
11428 /* automatically move to the next field with double speed */
11429 player->programmed_action = move_direction;
11431 if (player->move_delay_reset_counter == 0)
11433 player->move_delay_reset_counter = 2; /* two double speed steps */
11435 DOUBLE_PLAYER_SPEED(player);
11438 player->move_delay_reset_counter = 2;
11440 DOUBLE_PLAYER_SPEED(player);
11443 PlayLevelSoundAction(x, y, ACTION_PASSING);
11447 else if (IS_DIGGABLE(element))
11451 if (mode != DF_SNAP)
11454 GfxElement[x][y] = GFX_ELEMENT(element);
11457 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11459 player->is_digging = TRUE;
11462 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11464 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11465 player->index_bit, dig_side);
11468 if (mode == DF_SNAP)
11469 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11474 else if (IS_COLLECTIBLE(element))
11478 if (is_player && mode != DF_SNAP)
11480 GfxElement[x][y] = element;
11481 player->is_collecting = TRUE;
11484 if (element == EL_SPEED_PILL)
11485 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11486 else if (element == EL_EXTRA_TIME && level.time > 0)
11489 DrawGameValue_Time(TimeLeft);
11491 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11493 player->shield_normal_time_left += 10;
11494 if (element == EL_SHIELD_DEADLY)
11495 player->shield_deadly_time_left += 10;
11497 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11499 if (player->inventory_size < MAX_INVENTORY_SIZE)
11500 player->inventory_element[player->inventory_size++] = element;
11502 DrawGameValue_Dynamite(local_player->inventory_size);
11504 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11506 player->dynabomb_count++;
11507 player->dynabombs_left++;
11509 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11511 player->dynabomb_size++;
11513 else if (element == EL_DYNABOMB_INCREASE_POWER)
11515 player->dynabomb_xl = TRUE;
11517 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11518 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11520 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11521 element - EL_KEY_1 : element - EL_EM_KEY_1);
11523 player->key[key_nr] = TRUE;
11525 DrawGameValue_Keys(player);
11527 redraw_mask |= REDRAW_DOOR_1;
11529 else if (IS_ENVELOPE(element))
11532 player->show_envelope = element;
11534 ShowEnvelope(element - EL_ENVELOPE_1);
11537 else if (IS_DROPPABLE(element) ||
11538 IS_THROWABLE(element)) /* can be collected and dropped */
11542 if (element_info[element].collect_count == 0)
11543 player->inventory_infinite_element = element;
11545 for (i = 0; i < element_info[element].collect_count; i++)
11546 if (player->inventory_size < MAX_INVENTORY_SIZE)
11547 player->inventory_element[player->inventory_size++] = element;
11549 DrawGameValue_Dynamite(local_player->inventory_size);
11551 else if (element_info[element].collect_count > 0)
11553 local_player->gems_still_needed -=
11554 element_info[element].collect_count;
11555 if (local_player->gems_still_needed < 0)
11556 local_player->gems_still_needed = 0;
11558 DrawGameValue_Emeralds(local_player->gems_still_needed);
11561 RaiseScoreElement(element);
11562 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11565 CheckTriggeredElementChangeByPlayer(x, y, element,
11566 CE_OTHER_GETS_COLLECTED,
11567 player->index_bit, dig_side);
11570 if (mode == DF_SNAP)
11571 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11576 else if (IS_PUSHABLE(element))
11578 if (mode == DF_SNAP && element != EL_BD_ROCK)
11579 return MF_NO_ACTION;
11581 if (CAN_FALL(element) && dy)
11582 return MF_NO_ACTION;
11584 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11585 !(element == EL_SPRING && level.use_spring_bug))
11586 return MF_NO_ACTION;
11589 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11590 ((move_direction & MV_VERTICAL &&
11591 ((element_info[element].move_pattern & MV_LEFT &&
11592 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11593 (element_info[element].move_pattern & MV_RIGHT &&
11594 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11595 (move_direction & MV_HORIZONTAL &&
11596 ((element_info[element].move_pattern & MV_UP &&
11597 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11598 (element_info[element].move_pattern & MV_DOWN &&
11599 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11600 return MF_NO_ACTION;
11604 /* do not push elements already moving away faster than player */
11605 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11606 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11607 return MF_NO_ACTION;
11609 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11610 return MF_NO_ACTION;
11616 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11618 if (player->push_delay_value == -1 || !player_was_pushing)
11619 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11621 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11623 if (player->push_delay_value == -1)
11624 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11627 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11629 if (player->push_delay_value == -1 || !player_was_pushing)
11630 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11633 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11635 if (!player->is_pushing)
11636 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11640 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11641 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11642 !player_is_pushing))
11643 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11646 if (!player->is_pushing &&
11647 game.engine_version >= VERSION_IDENT(2,2,0,7))
11648 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11652 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11653 player->push_delay, player->push_delay_value,
11654 FrameCounter, game.engine_version,
11655 player_was_pushing, player->is_pushing,
11656 element, element_info[element].token_name,
11657 GET_NEW_PUSH_DELAY(element));
11660 player->is_pushing = TRUE;
11662 if (!(IN_LEV_FIELD(nextx, nexty) &&
11663 (IS_FREE(nextx, nexty) ||
11664 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11665 IS_SB_ELEMENT(element)))))
11666 return MF_NO_ACTION;
11668 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11669 return MF_NO_ACTION;
11671 #if USE_NEW_PUSH_DELAY
11674 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11675 printf("::: ALERT: %d, %d [%d / %d]\n",
11676 player->push_delay, player->push_delay2,
11677 FrameCounter, FrameCounter / 50);
11680 if (player->push_delay == -1) /* new pushing; restart delay */
11681 player->push_delay = 0;
11683 if (player->push_delay == 0) /* new pushing; restart delay */
11684 player->push_delay = FrameCounter;
11687 #if USE_NEW_PUSH_DELAY
11689 if ( (player->push_delay > 0) != (!xxx_fr) )
11690 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11691 player->push_delay,
11692 xxx_pdv2, player->push_delay2, player->push_delay_value,
11693 FrameCounter, FrameCounter / 50);
11697 if (player->push_delay > 0 &&
11698 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11699 element != EL_SPRING && element != EL_BALLOON)
11702 if (player->push_delay < player->push_delay_value &&
11703 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11704 element != EL_SPRING && element != EL_BALLOON)
11708 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11709 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11710 element != EL_SPRING && element != EL_BALLOON)
11713 /* make sure that there is no move delay before next try to push */
11714 #if USE_NEW_MOVE_DELAY
11715 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11716 player->move_delay = 0;
11718 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11719 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11722 return MF_NO_ACTION;
11726 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11729 if (IS_SB_ELEMENT(element))
11731 if (element == EL_SOKOBAN_FIELD_FULL)
11733 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11734 local_player->sokobanfields_still_needed++;
11737 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11739 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11740 local_player->sokobanfields_still_needed--;
11743 Feld[x][y] = EL_SOKOBAN_OBJECT;
11745 if (Back[x][y] == Back[nextx][nexty])
11746 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11747 else if (Back[x][y] != 0)
11748 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11751 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11754 if (local_player->sokobanfields_still_needed == 0 &&
11755 game.emulation == EMU_SOKOBAN)
11757 player->LevelSolved = player->GameOver = TRUE;
11758 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11762 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11764 InitMovingField(x, y, move_direction);
11765 GfxAction[x][y] = ACTION_PUSHING;
11767 if (mode == DF_SNAP)
11768 ContinueMoving(x, y);
11770 MovPos[x][y] = (dx != 0 ? dx : dy);
11772 Pushed[x][y] = TRUE;
11773 Pushed[nextx][nexty] = TRUE;
11775 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11776 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11778 player->push_delay_value = -1; /* get new value later */
11780 #if USE_PUSH_BUGFIX
11781 /* now: check for element change _after_ element has been pushed! */
11783 if (game.use_bug_change_when_pushing)
11785 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11788 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11789 player->index_bit, dig_side);
11790 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11791 player->index_bit, dig_side);
11797 /* check for element change _after_ element has been pushed! */
11801 /* !!! TEST ONLY !!! */
11802 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11803 player->index_bit, dig_side);
11804 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11805 player->index_bit, dig_side);
11807 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11808 player->index_bit, dig_side);
11809 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11810 player->index_bit, dig_side);
11818 else if (IS_SWITCHABLE(element))
11820 if (PLAYER_SWITCHING(player, x, y))
11822 CheckTriggeredElementChangeByPlayer(x,y, element,
11823 CE_OTHER_GETS_PRESSED,
11824 player->index_bit, dig_side);
11829 player->is_switching = TRUE;
11830 player->switch_x = x;
11831 player->switch_y = y;
11833 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11835 if (element == EL_ROBOT_WHEEL)
11837 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11841 DrawLevelField(x, y);
11843 else if (element == EL_SP_TERMINAL)
11847 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11849 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11851 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11852 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11855 else if (IS_BELT_SWITCH(element))
11857 ToggleBeltSwitch(x, y);
11859 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11860 element == EL_SWITCHGATE_SWITCH_DOWN)
11862 ToggleSwitchgateSwitch(x, y);
11864 else if (element == EL_LIGHT_SWITCH ||
11865 element == EL_LIGHT_SWITCH_ACTIVE)
11867 ToggleLightSwitch(x, y);
11870 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11871 SND_LIGHT_SWITCH_ACTIVATING :
11872 SND_LIGHT_SWITCH_DEACTIVATING);
11875 else if (element == EL_TIMEGATE_SWITCH)
11877 ActivateTimegateSwitch(x, y);
11879 else if (element == EL_BALLOON_SWITCH_LEFT ||
11880 element == EL_BALLOON_SWITCH_RIGHT ||
11881 element == EL_BALLOON_SWITCH_UP ||
11882 element == EL_BALLOON_SWITCH_DOWN ||
11883 element == EL_BALLOON_SWITCH_ANY)
11885 if (element == EL_BALLOON_SWITCH_ANY)
11886 game.balloon_dir = move_direction;
11888 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11889 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11890 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11891 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11894 else if (element == EL_LAMP)
11896 Feld[x][y] = EL_LAMP_ACTIVE;
11897 local_player->lights_still_needed--;
11899 DrawLevelField(x, y);
11901 else if (element == EL_TIME_ORB_FULL)
11903 Feld[x][y] = EL_TIME_ORB_EMPTY;
11905 DrawGameValue_Time(TimeLeft);
11907 DrawLevelField(x, y);
11910 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11914 CheckTriggeredElementChangeByPlayer(x, y, element,
11915 CE_OTHER_IS_SWITCHING,
11916 player->index_bit, dig_side);
11918 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11919 player->index_bit, dig_side);
11925 if (!PLAYER_SWITCHING(player, x, y))
11927 player->is_switching = TRUE;
11928 player->switch_x = x;
11929 player->switch_y = y;
11932 /* !!! TEST ONLY !!! */
11933 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11934 player->index_bit, dig_side);
11935 CheckTriggeredElementChangeByPlayer(x, y, element,
11936 CE_OTHER_IS_SWITCHING,
11937 player->index_bit, dig_side);
11939 CheckTriggeredElementChangeByPlayer(x, y, element,
11940 CE_OTHER_IS_SWITCHING,
11941 player->index_bit, dig_side);
11942 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11943 player->index_bit, dig_side);
11948 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11949 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11950 player->index_bit, dig_side);
11951 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11952 player->index_bit, dig_side);
11954 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11955 player->index_bit, dig_side);
11956 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11957 player->index_bit, dig_side);
11961 return MF_NO_ACTION;
11964 #if USE_NEW_PUSH_DELAY
11965 player->push_delay = -1;
11967 player->push_delay = 0;
11970 if (Feld[x][y] != element) /* really digged/collected something */
11971 player->is_collecting = !player->is_digging;
11976 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11978 int jx = player->jx, jy = player->jy;
11979 int x = jx + dx, y = jy + dy;
11980 int snap_direction = (dx == -1 ? MV_LEFT :
11981 dx == +1 ? MV_RIGHT :
11983 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11986 if (player->MovPos != 0)
11989 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11993 if (!player->active || !IN_LEV_FIELD(x, y))
12001 if (player->MovPos == 0)
12002 player->is_pushing = FALSE;
12004 player->is_snapping = FALSE;
12006 if (player->MovPos == 0)
12008 player->is_moving = FALSE;
12009 player->is_digging = FALSE;
12010 player->is_collecting = FALSE;
12016 if (player->is_snapping)
12019 player->MovDir = snap_direction;
12022 if (player->MovPos == 0)
12025 player->is_moving = FALSE;
12026 player->is_digging = FALSE;
12027 player->is_collecting = FALSE;
12030 player->is_dropping = FALSE;
12032 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12035 player->is_snapping = TRUE;
12038 if (player->MovPos == 0)
12041 player->is_moving = FALSE;
12042 player->is_digging = FALSE;
12043 player->is_collecting = FALSE;
12047 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12048 DrawLevelField(player->last_jx, player->last_jy);
12051 DrawLevelField(x, y);
12060 boolean DropElement(struct PlayerInfo *player)
12062 int old_element, new_element;
12063 int dropx = player->jx, dropy = player->jy;
12064 int drop_direction = player->MovDir;
12066 int drop_side = drop_direction;
12068 static int trigger_sides[4] =
12070 CH_SIDE_LEFT, /* dropping left */
12071 CH_SIDE_RIGHT, /* dropping right */
12072 CH_SIDE_TOP, /* dropping up */
12073 CH_SIDE_BOTTOM, /* dropping down */
12075 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12077 int drop_element = (player->inventory_size > 0 ?
12078 player->inventory_element[player->inventory_size - 1] :
12079 player->inventory_infinite_element != EL_UNDEFINED ?
12080 player->inventory_infinite_element :
12081 player->dynabombs_left > 0 ?
12082 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12085 if (IS_THROWABLE(drop_element))
12087 dropx += GET_DX_FROM_DIR(drop_direction);
12088 dropy += GET_DY_FROM_DIR(drop_direction);
12090 if (!IN_LEV_FIELD(dropx, dropy))
12094 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12095 new_element = drop_element; /* default: no change when dropping */
12097 /* check if player is active, not moving and ready to drop */
12098 if (!player->active || player->MovPos || player->drop_delay > 0)
12101 /* check if player has anything that can be dropped */
12103 if (new_element == EL_UNDEFINED)
12106 if (player->inventory_size == 0 &&
12107 player->inventory_infinite_element == EL_UNDEFINED &&
12108 player->dynabombs_left == 0)
12112 /* check if anything can be dropped at the current position */
12113 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12116 /* collected custom elements can only be dropped on empty fields */
12118 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12121 if (player->inventory_size > 0 &&
12122 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12123 && old_element != EL_EMPTY)
12127 if (old_element != EL_EMPTY)
12128 Back[dropx][dropy] = old_element; /* store old element on this field */
12130 ResetGfxAnimation(dropx, dropy);
12131 ResetRandomAnimationValue(dropx, dropy);
12133 if (player->inventory_size > 0 ||
12134 player->inventory_infinite_element != EL_UNDEFINED)
12136 if (player->inventory_size > 0)
12138 player->inventory_size--;
12141 new_element = player->inventory_element[player->inventory_size];
12144 DrawGameValue_Dynamite(local_player->inventory_size);
12146 if (new_element == EL_DYNAMITE)
12147 new_element = EL_DYNAMITE_ACTIVE;
12148 else if (new_element == EL_SP_DISK_RED)
12149 new_element = EL_SP_DISK_RED_ACTIVE;
12152 Feld[dropx][dropy] = new_element;
12154 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12155 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12156 el2img(Feld[dropx][dropy]), 0);
12158 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12161 /* needed if previous element just changed to "empty" in the last frame */
12162 Changed[dropx][dropy] = 0; /* allow another change */
12166 /* !!! TEST ONLY !!! */
12167 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12168 player->index_bit, drop_side);
12169 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12170 CE_OTHER_GETS_DROPPED,
12171 player->index_bit, drop_side);
12173 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12174 CE_OTHER_GETS_DROPPED,
12175 player->index_bit, drop_side);
12176 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12177 player->index_bit, drop_side);
12180 TestIfElementTouchesCustomElement(dropx, dropy);
12182 else /* player is dropping a dyna bomb */
12184 player->dynabombs_left--;
12187 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12190 Feld[dropx][dropy] = new_element;
12192 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12193 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12194 el2img(Feld[dropx][dropy]), 0);
12196 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12203 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12206 InitField_WithBug1(dropx, dropy, FALSE);
12208 InitField(dropx, dropy, FALSE);
12209 if (CAN_MOVE(Feld[dropx][dropy]))
12210 InitMovDir(dropx, dropy);
12214 new_element = Feld[dropx][dropy]; /* element might have changed */
12216 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12217 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12220 int move_stepsize = element_info[new_element].move_stepsize;
12222 int move_direction, nextx, nexty;
12224 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12225 MovDir[dropx][dropy] = drop_direction;
12227 move_direction = MovDir[dropx][dropy];
12228 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12229 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12232 Changed[dropx][dropy] = 0; /* allow another change */
12233 CheckCollision[dropx][dropy] = 2;
12236 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12239 WasJustMoving[dropx][dropy] = 3;
12242 InitMovingField(dropx, dropy, move_direction);
12243 ContinueMoving(dropx, dropy);
12248 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12251 Changed[dropx][dropy] = 0; /* allow another change */
12254 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12256 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12257 CE_HITTING_SOMETHING, move_direction);
12265 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12270 player->drop_delay = 8 + 8 + 8;
12274 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12279 player->is_dropping = TRUE;
12285 /* ------------------------------------------------------------------------- */
12286 /* game sound playing functions */
12287 /* ------------------------------------------------------------------------- */
12289 static int *loop_sound_frame = NULL;
12290 static int *loop_sound_volume = NULL;
12292 void InitPlayLevelSound()
12294 int num_sounds = getSoundListSize();
12296 checked_free(loop_sound_frame);
12297 checked_free(loop_sound_volume);
12299 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12300 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12303 static void PlayLevelSound(int x, int y, int nr)
12305 int sx = SCREENX(x), sy = SCREENY(y);
12306 int volume, stereo_position;
12307 int max_distance = 8;
12308 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12310 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12311 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12314 if (!IN_LEV_FIELD(x, y) ||
12315 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12316 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12319 volume = SOUND_MAX_VOLUME;
12321 if (!IN_SCR_FIELD(sx, sy))
12323 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12324 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12326 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12329 stereo_position = (SOUND_MAX_LEFT +
12330 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12331 (SCR_FIELDX + 2 * max_distance));
12333 if (IS_LOOP_SOUND(nr))
12335 /* This assures that quieter loop sounds do not overwrite louder ones,
12336 while restarting sound volume comparison with each new game frame. */
12338 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12341 loop_sound_volume[nr] = volume;
12342 loop_sound_frame[nr] = FrameCounter;
12345 PlaySoundExt(nr, volume, stereo_position, type);
12348 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12350 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12351 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12352 y < LEVELY(BY1) ? LEVELY(BY1) :
12353 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12357 static void PlayLevelSoundAction(int x, int y, int action)
12359 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12362 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12364 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12366 if (sound_effect != SND_UNDEFINED)
12367 PlayLevelSound(x, y, sound_effect);
12370 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12373 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12375 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12376 PlayLevelSound(x, y, sound_effect);
12379 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12381 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12383 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12384 PlayLevelSound(x, y, sound_effect);
12387 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12389 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12391 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12392 StopSound(sound_effect);
12395 static void PlayLevelMusic()
12397 if (levelset.music[level_nr] != MUS_UNDEFINED)
12398 PlayMusic(levelset.music[level_nr]); /* from config file */
12400 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12403 void RaiseScore(int value)
12405 local_player->score += value;
12407 DrawGameValue_Score(local_player->score);
12410 void RaiseScoreElement(int element)
12415 case EL_BD_DIAMOND:
12416 case EL_EMERALD_YELLOW:
12417 case EL_EMERALD_RED:
12418 case EL_EMERALD_PURPLE:
12419 case EL_SP_INFOTRON:
12420 RaiseScore(level.score[SC_EMERALD]);
12423 RaiseScore(level.score[SC_DIAMOND]);
12426 RaiseScore(level.score[SC_CRYSTAL]);
12429 RaiseScore(level.score[SC_PEARL]);
12432 case EL_BD_BUTTERFLY:
12433 case EL_SP_ELECTRON:
12434 RaiseScore(level.score[SC_BUG]);
12437 case EL_BD_FIREFLY:
12438 case EL_SP_SNIKSNAK:
12439 RaiseScore(level.score[SC_SPACESHIP]);
12442 case EL_DARK_YAMYAM:
12443 RaiseScore(level.score[SC_YAMYAM]);
12446 RaiseScore(level.score[SC_ROBOT]);
12449 RaiseScore(level.score[SC_PACMAN]);
12452 RaiseScore(level.score[SC_NUT]);
12455 case EL_SP_DISK_RED:
12456 case EL_DYNABOMB_INCREASE_NUMBER:
12457 case EL_DYNABOMB_INCREASE_SIZE:
12458 case EL_DYNABOMB_INCREASE_POWER:
12459 RaiseScore(level.score[SC_DYNAMITE]);
12461 case EL_SHIELD_NORMAL:
12462 case EL_SHIELD_DEADLY:
12463 RaiseScore(level.score[SC_SHIELD]);
12465 case EL_EXTRA_TIME:
12466 RaiseScore(level.score[SC_TIME_BONUS]);
12472 RaiseScore(level.score[SC_KEY]);
12475 RaiseScore(element_info[element].collect_score);
12480 void RequestQuitGame(boolean ask_if_really_quit)
12482 if (AllPlayersGone ||
12483 !ask_if_really_quit ||
12484 level_editor_test_game ||
12485 Request("Do you really want to quit the game ?",
12486 REQ_ASK | REQ_STAY_CLOSED))
12488 #if defined(NETWORK_AVALIABLE)
12489 if (options.network)
12490 SendToServer_StopPlaying();
12494 game_status = GAME_MODE_MAIN;
12502 if (tape.playing && tape.deactivate_display)
12503 TapeDeactivateDisplayOff(TRUE);
12506 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12509 if (tape.playing && tape.deactivate_display)
12510 TapeDeactivateDisplayOn();
12517 /* ---------- new game button stuff ---------------------------------------- */
12519 /* graphic position values for game buttons */
12520 #define GAME_BUTTON_XSIZE 30
12521 #define GAME_BUTTON_YSIZE 30
12522 #define GAME_BUTTON_XPOS 5
12523 #define GAME_BUTTON_YPOS 215
12524 #define SOUND_BUTTON_XPOS 5
12525 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12527 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12528 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12529 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12530 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12531 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12532 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12539 } gamebutton_info[NUM_GAME_BUTTONS] =
12542 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12547 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12548 GAME_CTRL_ID_PAUSE,
12552 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12557 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12558 SOUND_CTRL_ID_MUSIC,
12559 "background music on/off"
12562 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12563 SOUND_CTRL_ID_LOOPS,
12564 "sound loops on/off"
12567 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12568 SOUND_CTRL_ID_SIMPLE,
12569 "normal sounds on/off"
12573 void CreateGameButtons()
12577 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12579 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12580 struct GadgetInfo *gi;
12583 unsigned long event_mask;
12584 int gd_xoffset, gd_yoffset;
12585 int gd_x1, gd_x2, gd_y1, gd_y2;
12588 gd_xoffset = gamebutton_info[i].x;
12589 gd_yoffset = gamebutton_info[i].y;
12590 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12591 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12593 if (id == GAME_CTRL_ID_STOP ||
12594 id == GAME_CTRL_ID_PAUSE ||
12595 id == GAME_CTRL_ID_PLAY)
12597 button_type = GD_TYPE_NORMAL_BUTTON;
12599 event_mask = GD_EVENT_RELEASED;
12600 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12601 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12605 button_type = GD_TYPE_CHECK_BUTTON;
12607 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12608 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12609 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12610 event_mask = GD_EVENT_PRESSED;
12611 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12612 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12615 gi = CreateGadget(GDI_CUSTOM_ID, id,
12616 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12617 GDI_X, DX + gd_xoffset,
12618 GDI_Y, DY + gd_yoffset,
12619 GDI_WIDTH, GAME_BUTTON_XSIZE,
12620 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12621 GDI_TYPE, button_type,
12622 GDI_STATE, GD_BUTTON_UNPRESSED,
12623 GDI_CHECKED, checked,
12624 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12625 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12626 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12627 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12628 GDI_EVENT_MASK, event_mask,
12629 GDI_CALLBACK_ACTION, HandleGameButtons,
12633 Error(ERR_EXIT, "cannot create gadget");
12635 game_gadget[id] = gi;
12639 void FreeGameButtons()
12643 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12644 FreeGadget(game_gadget[i]);
12647 static void MapGameButtons()
12651 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12652 MapGadget(game_gadget[i]);
12655 void UnmapGameButtons()
12659 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12660 UnmapGadget(game_gadget[i]);
12663 static void HandleGameButtons(struct GadgetInfo *gi)
12665 int id = gi->custom_id;
12667 if (game_status != GAME_MODE_PLAYING)
12672 case GAME_CTRL_ID_STOP:
12673 RequestQuitGame(TRUE);
12676 case GAME_CTRL_ID_PAUSE:
12677 if (options.network)
12679 #if defined(NETWORK_AVALIABLE)
12681 SendToServer_ContinuePlaying();
12683 SendToServer_PausePlaying();
12687 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12690 case GAME_CTRL_ID_PLAY:
12693 #if defined(NETWORK_AVALIABLE)
12694 if (options.network)
12695 SendToServer_ContinuePlaying();
12699 tape.pausing = FALSE;
12700 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12705 case SOUND_CTRL_ID_MUSIC:
12706 if (setup.sound_music)
12708 setup.sound_music = FALSE;
12711 else if (audio.music_available)
12713 setup.sound = setup.sound_music = TRUE;
12715 SetAudioMode(setup.sound);
12721 case SOUND_CTRL_ID_LOOPS:
12722 if (setup.sound_loops)
12723 setup.sound_loops = FALSE;
12724 else if (audio.loops_available)
12726 setup.sound = setup.sound_loops = TRUE;
12727 SetAudioMode(setup.sound);
12731 case SOUND_CTRL_ID_SIMPLE:
12732 if (setup.sound_simple)
12733 setup.sound_simple = FALSE;
12734 else if (audio.sound_available)
12736 setup.sound = setup.sound_simple = TRUE;
12737 SetAudioMode(setup.sound);