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 InitGameEngine_EM();
2073 /* after drawing the level, correct some elements */
2074 if (game.timegate_time_left == 0)
2075 CloseAllOpenTimegates();
2077 if (setup.soft_scrolling)
2078 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2080 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2083 /* !!! FIX THIS (END) !!! */
2085 /* copy default game door content to main double buffer */
2086 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2087 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2089 DrawGameDoorValues();
2093 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2094 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2095 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2099 /* copy actual game door content to door double buffer for OpenDoor() */
2100 BlitBitmap(drawto, bitmap_db_door,
2101 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2103 OpenDoor(DOOR_OPEN_ALL);
2105 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2107 if (setup.sound_music)
2110 KeyboardAutoRepeatOffUnlessAutoplay();
2114 for (i = 0; i < MAX_PLAYERS; i++)
2115 printf("Player %d %sactive.\n",
2116 i + 1, (stored_player[i].active ? "" : "not "));
2120 printf("::: starting game [%d]\n", FrameCounter);
2124 void InitMovDir(int x, int y)
2126 int i, element = Feld[x][y];
2127 static int xy[4][2] =
2134 static int direction[3][4] =
2136 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2137 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2138 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2147 Feld[x][y] = EL_BUG;
2148 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2151 case EL_SPACESHIP_RIGHT:
2152 case EL_SPACESHIP_UP:
2153 case EL_SPACESHIP_LEFT:
2154 case EL_SPACESHIP_DOWN:
2155 Feld[x][y] = EL_SPACESHIP;
2156 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2159 case EL_BD_BUTTERFLY_RIGHT:
2160 case EL_BD_BUTTERFLY_UP:
2161 case EL_BD_BUTTERFLY_LEFT:
2162 case EL_BD_BUTTERFLY_DOWN:
2163 Feld[x][y] = EL_BD_BUTTERFLY;
2164 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2167 case EL_BD_FIREFLY_RIGHT:
2168 case EL_BD_FIREFLY_UP:
2169 case EL_BD_FIREFLY_LEFT:
2170 case EL_BD_FIREFLY_DOWN:
2171 Feld[x][y] = EL_BD_FIREFLY;
2172 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2175 case EL_PACMAN_RIGHT:
2177 case EL_PACMAN_LEFT:
2178 case EL_PACMAN_DOWN:
2179 Feld[x][y] = EL_PACMAN;
2180 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2183 case EL_SP_SNIKSNAK:
2184 MovDir[x][y] = MV_UP;
2187 case EL_SP_ELECTRON:
2188 MovDir[x][y] = MV_LEFT;
2195 Feld[x][y] = EL_MOLE;
2196 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2200 if (IS_CUSTOM_ELEMENT(element))
2202 struct ElementInfo *ei = &element_info[element];
2203 int move_direction_initial = ei->move_direction_initial;
2204 int move_pattern = ei->move_pattern;
2206 if (move_direction_initial == MV_START_PREVIOUS)
2208 if (MovDir[x][y] != MV_NO_MOVING)
2211 move_direction_initial = MV_START_AUTOMATIC;
2214 if (move_direction_initial == MV_START_RANDOM)
2215 MovDir[x][y] = 1 << RND(4);
2216 else if (move_direction_initial & MV_ANY_DIRECTION)
2217 MovDir[x][y] = move_direction_initial;
2218 else if (move_pattern == MV_ALL_DIRECTIONS ||
2219 move_pattern == MV_TURNING_LEFT ||
2220 move_pattern == MV_TURNING_RIGHT ||
2221 move_pattern == MV_TURNING_LEFT_RIGHT ||
2222 move_pattern == MV_TURNING_RIGHT_LEFT ||
2223 move_pattern == MV_TURNING_RANDOM)
2224 MovDir[x][y] = 1 << RND(4);
2225 else if (move_pattern == MV_HORIZONTAL)
2226 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2227 else if (move_pattern == MV_VERTICAL)
2228 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2229 else if (move_pattern & MV_ANY_DIRECTION)
2230 MovDir[x][y] = element_info[element].move_pattern;
2231 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2232 move_pattern == MV_ALONG_RIGHT_SIDE)
2235 /* use random direction as default start direction */
2236 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2237 MovDir[x][y] = 1 << RND(4);
2240 for (i = 0; i < NUM_DIRECTIONS; i++)
2242 int x1 = x + xy[i][0];
2243 int y1 = y + xy[i][1];
2245 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2247 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2248 MovDir[x][y] = direction[0][i];
2250 MovDir[x][y] = direction[1][i];
2259 MovDir[x][y] = 1 << RND(4);
2261 if (element != EL_BUG &&
2262 element != EL_SPACESHIP &&
2263 element != EL_BD_BUTTERFLY &&
2264 element != EL_BD_FIREFLY)
2267 for (i = 0; i < NUM_DIRECTIONS; i++)
2269 int x1 = x + xy[i][0];
2270 int y1 = y + xy[i][1];
2272 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2274 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2276 MovDir[x][y] = direction[0][i];
2279 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2280 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2282 MovDir[x][y] = direction[1][i];
2291 GfxDir[x][y] = MovDir[x][y];
2294 void InitAmoebaNr(int x, int y)
2297 int group_nr = AmoebeNachbarNr(x, y);
2301 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2303 if (AmoebaCnt[i] == 0)
2311 AmoebaNr[x][y] = group_nr;
2312 AmoebaCnt[group_nr]++;
2313 AmoebaCnt2[group_nr]++;
2319 boolean raise_level = FALSE;
2321 if (local_player->MovPos)
2325 if (tape.auto_play) /* tape might already be stopped here */
2326 tape.auto_play_level_solved = TRUE;
2328 if (tape.playing && tape.auto_play)
2329 tape.auto_play_level_solved = TRUE;
2332 local_player->LevelSolved = FALSE;
2334 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2338 if (!tape.playing && setup.sound_loops)
2339 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2340 SND_CTRL_PLAY_LOOP);
2342 while (TimeLeft > 0)
2344 if (!tape.playing && !setup.sound_loops)
2345 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2346 if (TimeLeft > 0 && !(TimeLeft % 10))
2347 RaiseScore(level.score[SC_TIME_BONUS]);
2348 if (TimeLeft > 100 && !(TimeLeft % 10))
2353 DrawGameValue_Time(TimeLeft);
2361 if (!tape.playing && setup.sound_loops)
2362 StopSound(SND_GAME_LEVELTIME_BONUS);
2364 else if (level.time == 0) /* level without time limit */
2366 if (!tape.playing && setup.sound_loops)
2367 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2368 SND_CTRL_PLAY_LOOP);
2370 while (TimePlayed < 999)
2372 if (!tape.playing && !setup.sound_loops)
2373 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2374 if (TimePlayed < 999 && !(TimePlayed % 10))
2375 RaiseScore(level.score[SC_TIME_BONUS]);
2376 if (TimePlayed < 900 && !(TimePlayed % 10))
2381 DrawGameValue_Time(TimePlayed);
2389 if (!tape.playing && setup.sound_loops)
2390 StopSound(SND_GAME_LEVELTIME_BONUS);
2393 /* close exit door after last player */
2394 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2395 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2397 int element = Feld[ExitX][ExitY];
2399 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2400 EL_SP_EXIT_CLOSING);
2402 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2405 /* Hero disappears */
2406 DrawLevelField(ExitX, ExitY);
2412 CloseDoor(DOOR_CLOSE_1);
2417 SaveTape(tape.level_nr); /* Ask to save tape */
2420 if (level_nr == leveldir_current->handicap_level)
2422 leveldir_current->handicap_level++;
2423 SaveLevelSetup_SeriesInfo();
2426 if (level_editor_test_game)
2427 local_player->score = -1; /* no highscore when playing from editor */
2428 else if (level_nr < leveldir_current->last_level)
2429 raise_level = TRUE; /* advance to next level */
2431 if ((hi_pos = NewHiScore()) >= 0)
2433 game_status = GAME_MODE_SCORES;
2434 DrawHallOfFame(hi_pos);
2443 game_status = GAME_MODE_MAIN;
2460 LoadScore(level_nr);
2462 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2463 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2466 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2468 if (local_player->score > highscore[k].Score)
2470 /* player has made it to the hall of fame */
2472 if (k < MAX_SCORE_ENTRIES - 1)
2474 int m = MAX_SCORE_ENTRIES - 1;
2477 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2478 if (!strcmp(setup.player_name, highscore[l].Name))
2480 if (m == k) /* player's new highscore overwrites his old one */
2484 for (l = m; l > k; l--)
2486 strcpy(highscore[l].Name, highscore[l - 1].Name);
2487 highscore[l].Score = highscore[l - 1].Score;
2494 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2495 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2496 highscore[k].Score = local_player->score;
2502 else if (!strncmp(setup.player_name, highscore[k].Name,
2503 MAX_PLAYER_NAME_LEN))
2504 break; /* player already there with a higher score */
2510 SaveScore(level_nr);
2515 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2517 if (player->GfxAction != action || player->GfxDir != dir)
2520 printf("Player frame reset! (%d => %d, %d => %d)\n",
2521 player->GfxAction, action, player->GfxDir, dir);
2524 player->GfxAction = action;
2525 player->GfxDir = dir;
2527 player->StepFrame = 0;
2531 static void ResetRandomAnimationValue(int x, int y)
2533 GfxRandom[x][y] = INIT_GFX_RANDOM();
2536 static void ResetGfxAnimation(int x, int y)
2539 GfxAction[x][y] = ACTION_DEFAULT;
2540 GfxDir[x][y] = MovDir[x][y];
2543 void InitMovingField(int x, int y, int direction)
2545 int element = Feld[x][y];
2546 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2547 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2551 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2552 ResetGfxAnimation(x, y);
2554 MovDir[newx][newy] = MovDir[x][y] = direction;
2555 GfxDir[x][y] = direction;
2557 if (Feld[newx][newy] == EL_EMPTY)
2558 Feld[newx][newy] = EL_BLOCKED;
2560 if (direction == MV_DOWN && CAN_FALL(element))
2561 GfxAction[x][y] = ACTION_FALLING;
2563 GfxAction[x][y] = ACTION_MOVING;
2565 GfxFrame[newx][newy] = GfxFrame[x][y];
2566 GfxRandom[newx][newy] = GfxRandom[x][y];
2567 GfxAction[newx][newy] = GfxAction[x][y];
2568 GfxDir[newx][newy] = GfxDir[x][y];
2571 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2573 int direction = MovDir[x][y];
2574 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2575 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2581 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2583 int oldx = x, oldy = y;
2584 int direction = MovDir[x][y];
2586 if (direction == MV_LEFT)
2588 else if (direction == MV_RIGHT)
2590 else if (direction == MV_UP)
2592 else if (direction == MV_DOWN)
2595 *comes_from_x = oldx;
2596 *comes_from_y = oldy;
2599 int MovingOrBlocked2Element(int x, int y)
2601 int element = Feld[x][y];
2603 if (element == EL_BLOCKED)
2607 Blocked2Moving(x, y, &oldx, &oldy);
2608 return Feld[oldx][oldy];
2614 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2616 /* like MovingOrBlocked2Element(), but if element is moving
2617 and (x,y) is the field the moving element is just leaving,
2618 return EL_BLOCKED instead of the element value */
2619 int element = Feld[x][y];
2621 if (IS_MOVING(x, y))
2623 if (element == EL_BLOCKED)
2627 Blocked2Moving(x, y, &oldx, &oldy);
2628 return Feld[oldx][oldy];
2637 static void RemoveField(int x, int y)
2639 Feld[x][y] = EL_EMPTY;
2646 ChangeDelay[x][y] = 0;
2647 ChangePage[x][y] = -1;
2648 Pushed[x][y] = FALSE;
2651 ExplodeField[x][y] = EX_TYPE_NONE;
2654 GfxElement[x][y] = EL_UNDEFINED;
2655 GfxAction[x][y] = ACTION_DEFAULT;
2656 GfxDir[x][y] = MV_NO_MOVING;
2659 void RemoveMovingField(int x, int y)
2661 int oldx = x, oldy = y, newx = x, newy = y;
2662 int element = Feld[x][y];
2663 int next_element = EL_UNDEFINED;
2665 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2668 if (IS_MOVING(x, y))
2670 Moving2Blocked(x, y, &newx, &newy);
2672 if (Feld[newx][newy] != EL_BLOCKED)
2675 if (Feld[newx][newy] != EL_BLOCKED)
2677 /* element is moving, but target field is not free (blocked), but
2678 already occupied by something different (example: acid pool);
2679 in this case, only remove the moving field, but not the target */
2681 RemoveField(oldx, oldy);
2683 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2685 DrawLevelField(oldx, oldy);
2691 else if (element == EL_BLOCKED)
2693 Blocked2Moving(x, y, &oldx, &oldy);
2694 if (!IS_MOVING(oldx, oldy))
2698 if (element == EL_BLOCKED &&
2699 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2700 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2701 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2702 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2703 next_element = get_next_element(Feld[oldx][oldy]);
2705 RemoveField(oldx, oldy);
2706 RemoveField(newx, newy);
2708 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2710 if (next_element != EL_UNDEFINED)
2711 Feld[oldx][oldy] = next_element;
2713 DrawLevelField(oldx, oldy);
2714 DrawLevelField(newx, newy);
2717 void DrawDynamite(int x, int y)
2719 int sx = SCREENX(x), sy = SCREENY(y);
2720 int graphic = el2img(Feld[x][y]);
2723 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2726 if (IS_WALKABLE_INSIDE(Back[x][y]))
2730 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2731 else if (Store[x][y])
2732 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2734 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2737 if (Back[x][y] || Store[x][y])
2738 DrawGraphicThruMask(sx, sy, graphic, frame);
2740 DrawGraphic(sx, sy, graphic, frame);
2742 if (game.emulation == EMU_SUPAPLEX)
2743 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2744 else if (Store[x][y])
2745 DrawGraphicThruMask(sx, sy, graphic, frame);
2747 DrawGraphic(sx, sy, graphic, frame);
2751 void CheckDynamite(int x, int y)
2753 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2757 if (MovDelay[x][y] != 0)
2760 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2767 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2769 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2770 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2771 StopSound(SND_DYNAMITE_ACTIVE);
2773 StopSound(SND_DYNABOMB_ACTIVE);
2779 void DrawRelocatePlayer(struct PlayerInfo *player)
2781 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2782 boolean no_delay = (tape.warp_forward);
2783 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2784 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2785 int jx = player->jx;
2786 int jy = player->jy;
2788 if (level.instant_relocation)
2791 int offset = (setup.scroll_delay ? 3 : 0);
2793 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2795 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2796 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2797 local_player->jx - MIDPOSX);
2799 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2800 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2801 local_player->jy - MIDPOSY);
2805 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2806 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2807 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2809 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2810 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2811 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2813 /* don't scroll over playfield boundaries */
2814 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2815 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2817 /* don't scroll over playfield boundaries */
2818 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2819 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2822 scroll_x += (local_player->jx - old_jx);
2823 scroll_y += (local_player->jy - old_jy);
2825 /* don't scroll over playfield boundaries */
2826 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2827 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2829 /* don't scroll over playfield boundaries */
2830 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2831 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2834 RedrawPlayfield(TRUE, 0,0,0,0);
2840 int offset = (setup.scroll_delay ? 3 : 0);
2842 int scroll_xx = -999, scroll_yy = -999;
2844 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2846 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2849 int fx = FX, fy = FY;
2851 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2852 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2853 local_player->jx - MIDPOSX);
2855 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2856 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2857 local_player->jy - MIDPOSY);
2859 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2860 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2863 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2866 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2873 fx += dx * TILEX / 2;
2874 fy += dy * TILEY / 2;
2876 ScrollLevel(dx, dy);
2879 /* scroll in two steps of half tile size to make things smoother */
2880 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2882 Delay(wait_delay_value);
2884 /* scroll second step to align at full tile size */
2886 Delay(wait_delay_value);
2889 int scroll_xx = -999, scroll_yy = -999;
2891 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2893 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2896 int fx = FX, fy = FY;
2898 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2899 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2900 local_player->jx - MIDPOSX);
2902 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2903 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2904 local_player->jy - MIDPOSY);
2906 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2907 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2910 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2913 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2920 fx += dx * TILEX / 2;
2921 fy += dy * TILEY / 2;
2923 ScrollLevel(dx, dy);
2926 /* scroll in two steps of half tile size to make things smoother */
2927 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2929 Delay(wait_delay_value);
2931 /* scroll second step to align at full tile size */
2933 Delay(wait_delay_value);
2939 Delay(wait_delay_value);
2943 void RelocatePlayer(int jx, int jy, int el_player_raw)
2946 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2948 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2950 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2951 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2952 boolean no_delay = (tape.warp_forward);
2953 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2954 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2955 int old_jx = player->jx;
2956 int old_jy = player->jy;
2957 int old_element = Feld[old_jx][old_jy];
2958 int element = Feld[jx][jy];
2959 boolean player_relocated = (old_jx != jx || old_jy != jy);
2961 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2962 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2964 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2965 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2966 int leave_side_horiz = move_dir_horiz;
2967 int leave_side_vert = move_dir_vert;
2969 static int trigger_sides[4][2] =
2971 /* enter side leave side */
2972 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2973 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2974 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2975 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2977 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2978 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2979 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2980 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2982 int enter_side = enter_side_horiz | enter_side_vert;
2983 int leave_side = leave_side_horiz | leave_side_vert;
2985 if (player->GameOver) /* do not reanimate dead player */
2988 if (!player_relocated) /* no need to relocate the player */
2991 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2993 RemoveField(jx, jy); /* temporarily remove newly placed player */
2994 DrawLevelField(jx, jy);
2997 if (player->present)
2999 while (player->MovPos)
3001 ScrollPlayer(player, SCROLL_GO_ON);
3002 ScrollScreen(NULL, SCROLL_GO_ON);
3004 #if USE_NEW_MOVE_DELAY
3005 AdvanceFrameAndPlayerCounters(player->index_nr);
3013 Delay(wait_delay_value);
3016 DrawPlayer(player); /* needed here only to cleanup last field */
3017 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3019 player->is_moving = FALSE;
3023 if (IS_CUSTOM_ELEMENT(old_element))
3024 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3026 player->index_bit, leave_side);
3028 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3030 player->index_bit, leave_side);
3033 Feld[jx][jy] = el_player;
3034 InitPlayerField(jx, jy, el_player, TRUE);
3036 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3038 Feld[jx][jy] = element;
3039 InitField(jx, jy, FALSE);
3043 if (player == local_player) /* only visually relocate local player */
3044 DrawRelocatePlayer(player);
3048 TestIfHeroTouchesBadThing(jx, jy);
3049 TestIfPlayerTouchesCustomElement(jx, jy);
3053 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3058 /* needed to allow change of walkable custom element by entering player */
3059 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3060 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3062 /* needed to allow change of walkable custom element by entering player */
3063 Changed[jx][jy] = 0; /* allow another change */
3068 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3069 enter_side == MV_LEFT ? "left" :
3070 enter_side == MV_RIGHT ? "right" :
3071 enter_side == MV_UP ? "top" :
3072 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3076 if (IS_CUSTOM_ELEMENT(element))
3077 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3078 player->index_bit, enter_side);
3080 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3081 CE_OTHER_GETS_ENTERED,
3082 player->index_bit, enter_side);
3086 void Explode(int ex, int ey, int phase, int mode)
3093 /* !!! eliminate this variable !!! */
3094 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3099 int last_phase = num_phase * delay;
3100 int half_phase = (num_phase / 2) * delay;
3101 int first_phase_after_start = EX_PHASE_START + 1;
3105 if (game.explosions_delayed)
3107 ExplodeField[ex][ey] = mode;
3111 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3113 int center_element = Feld[ex][ey];
3116 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3120 /* --- This is only really needed (and now handled) in "Impact()". --- */
3121 /* do not explode moving elements that left the explode field in time */
3122 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3123 center_element == EL_EMPTY &&
3124 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3128 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3129 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3131 /* remove things displayed in background while burning dynamite */
3132 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3135 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3137 /* put moving element to center field (and let it explode there) */
3138 center_element = MovingOrBlocked2Element(ex, ey);
3139 RemoveMovingField(ex, ey);
3140 Feld[ex][ey] = center_element;
3146 last_phase = element_info[center_element].explosion_delay + 1;
3148 last_phase = element_info[center_element].explosion_delay;
3152 printf("::: %d -> %d\n", center_element, last_phase);
3156 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3158 int xx = x - ex + 1;
3159 int yy = y - ey + 1;
3164 if (!IN_LEV_FIELD(x, y) ||
3165 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3166 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3169 if (!IN_LEV_FIELD(x, y) ||
3170 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3174 if (!IN_LEV_FIELD(x, y) ||
3175 ((mode != EX_TYPE_NORMAL ||
3176 center_element == EL_AMOEBA_TO_DIAMOND) &&
3177 (x != ex || y != ey)))
3181 element = Feld[x][y];
3183 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3185 element = MovingOrBlocked2Element(x, y);
3187 if (!IS_EXPLOSION_PROOF(element))
3188 RemoveMovingField(x, y);
3194 if (IS_EXPLOSION_PROOF(element))
3197 /* indestructible elements can only explode in center (but not flames) */
3199 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3200 mode == EX_TYPE_BORDER)) ||
3201 element == EL_FLAMES)
3204 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3205 element == EL_FLAMES)
3211 if ((IS_INDESTRUCTIBLE(element) &&
3212 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3213 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3214 element == EL_FLAMES)
3219 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3220 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3221 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3223 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3226 if (IS_ACTIVE_BOMB(element))
3228 /* re-activate things under the bomb like gate or penguin */
3230 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3233 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3238 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3239 element_info[Feld[x][y]].token_name,
3240 Store[x][y], Store2[x][y]);
3247 /* save walkable background elements while explosion on same tile */
3249 if (IS_INDESTRUCTIBLE(element))
3250 Back[x][y] = element;
3254 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3255 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3256 Back[x][y] = element;
3258 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3259 (x != ex || y != ey))
3260 Back[x][y] = element;
3263 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3264 Back[x][y] = element;
3268 /* ignite explodable elements reached by other explosion */
3269 if (element == EL_EXPLOSION)
3270 element = Store2[x][y];
3273 if (AmoebaNr[x][y] &&
3274 (element == EL_AMOEBA_FULL ||
3275 element == EL_BD_AMOEBA ||
3276 element == EL_AMOEBA_GROWING))
3278 AmoebaCnt[AmoebaNr[x][y]]--;
3279 AmoebaCnt2[AmoebaNr[x][y]]--;
3285 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3287 switch(StorePlayer[ex][ey])
3290 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3293 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3296 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3300 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3305 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3306 Store[x][y] = EL_EMPTY;
3308 if (game.emulation == EMU_SUPAPLEX)
3309 Store[x][y] = EL_EMPTY;
3312 else if (center_element == EL_MOLE)
3313 Store[x][y] = EL_EMERALD_RED;
3314 else if (center_element == EL_PENGUIN)
3315 Store[x][y] = EL_EMERALD_PURPLE;
3316 else if (center_element == EL_BUG)
3317 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3318 else if (center_element == EL_BD_BUTTERFLY)
3319 Store[x][y] = EL_BD_DIAMOND;
3320 else if (center_element == EL_SP_ELECTRON)
3321 Store[x][y] = EL_SP_INFOTRON;
3322 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3323 Store[x][y] = level.amoeba_content;
3324 else if (center_element == EL_YAMYAM)
3325 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3326 else if (IS_CUSTOM_ELEMENT(center_element) &&
3327 element_info[center_element].content[xx][yy] != EL_EMPTY)
3328 Store[x][y] = element_info[center_element].content[xx][yy];
3329 else if (element == EL_WALL_EMERALD)
3330 Store[x][y] = EL_EMERALD;
3331 else if (element == EL_WALL_DIAMOND)
3332 Store[x][y] = EL_DIAMOND;
3333 else if (element == EL_WALL_BD_DIAMOND)
3334 Store[x][y] = EL_BD_DIAMOND;
3335 else if (element == EL_WALL_EMERALD_YELLOW)
3336 Store[x][y] = EL_EMERALD_YELLOW;
3337 else if (element == EL_WALL_EMERALD_RED)
3338 Store[x][y] = EL_EMERALD_RED;
3339 else if (element == EL_WALL_EMERALD_PURPLE)
3340 Store[x][y] = EL_EMERALD_PURPLE;
3341 else if (element == EL_WALL_PEARL)
3342 Store[x][y] = EL_PEARL;
3343 else if (element == EL_WALL_CRYSTAL)
3344 Store[x][y] = EL_CRYSTAL;
3345 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3346 Store[x][y] = element_info[element].content[1][1];
3348 Store[x][y] = EL_EMPTY;
3350 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3351 center_element == EL_AMOEBA_TO_DIAMOND)
3352 Store2[x][y] = element;
3355 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3356 element_info[Store2[x][y]].token_name);
3360 if (AmoebaNr[x][y] &&
3361 (element == EL_AMOEBA_FULL ||
3362 element == EL_BD_AMOEBA ||
3363 element == EL_AMOEBA_GROWING))
3365 AmoebaCnt[AmoebaNr[x][y]]--;
3366 AmoebaCnt2[AmoebaNr[x][y]]--;
3372 MovDir[x][y] = MovPos[x][y] = 0;
3373 GfxDir[x][y] = MovDir[x][y];
3378 Feld[x][y] = EL_EXPLOSION;
3380 GfxElement[x][y] = center_element;
3382 GfxElement[x][y] = EL_UNDEFINED;
3385 ExplodePhase[x][y] = 1;
3387 ExplodeDelay[x][y] = last_phase;
3392 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3394 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3401 if (center_element == EL_YAMYAM)
3402 game.yamyam_content_nr =
3403 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3406 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3407 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3421 GfxFrame[x][y] = 0; /* restart explosion animation */
3425 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3429 last_phase = ExplodeDelay[x][y];
3432 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3436 /* activate this even in non-DEBUG version until cause for crash in
3437 getGraphicAnimationFrame() (see below) is found and eliminated */
3441 if (GfxElement[x][y] == EL_UNDEFINED)
3444 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3445 printf("Explode(): This should never happen!\n");
3448 GfxElement[x][y] = EL_EMPTY;
3454 border_element = Store2[x][y];
3456 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3457 border_element = StorePlayer[x][y];
3459 if (IS_PLAYER(x, y))
3460 border_element = StorePlayer[x][y];
3464 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3465 element_info[border_element].token_name, Store2[x][y]);
3469 printf("::: phase == %d\n", phase);
3472 if (phase == element_info[border_element].ignition_delay ||
3473 phase == last_phase)
3475 boolean border_explosion = FALSE;
3479 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3480 !PLAYER_EXPLOSION_PROTECTED(x, y))
3482 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3485 if (IS_PLAYER(x, y))
3488 KillHeroUnlessExplosionProtected(x, y);
3489 border_explosion = TRUE;
3492 if (phase == last_phase)
3493 printf("::: IS_PLAYER\n");
3496 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3499 printf("::: %d,%d: %d %s\n", x, y, border_element,
3500 element_info[border_element].token_name);
3503 Feld[x][y] = Store2[x][y];
3506 border_explosion = TRUE;
3509 if (phase == last_phase)
3510 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3513 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3515 AmoebeUmwandeln(x, y);
3517 border_explosion = TRUE;
3520 if (phase == last_phase)
3521 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3522 element_info[border_element].explosion_delay,
3523 element_info[border_element].ignition_delay,
3529 /* if an element just explodes due to another explosion (chain-reaction),
3530 do not immediately end the new explosion when it was the last frame of
3531 the explosion (as it would be done in the following "if"-statement!) */
3532 if (border_explosion && phase == last_phase)
3539 if (phase == first_phase_after_start)
3541 int element = Store2[x][y];
3543 if (element == EL_BLACK_ORB)
3545 Feld[x][y] = Store2[x][y];
3550 else if (phase == half_phase)
3552 int element = Store2[x][y];
3554 if (IS_PLAYER(x, y))
3555 KillHeroUnlessExplosionProtected(x, y);
3556 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3558 Feld[x][y] = Store2[x][y];
3562 else if (element == EL_AMOEBA_TO_DIAMOND)
3563 AmoebeUmwandeln(x, y);
3567 if (phase == last_phase)
3572 printf("::: done: phase == %d\n", phase);
3576 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3579 element = Feld[x][y] = Store[x][y];
3580 Store[x][y] = Store2[x][y] = 0;
3581 GfxElement[x][y] = EL_UNDEFINED;
3583 /* player can escape from explosions and might therefore be still alive */
3584 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3585 element <= EL_PLAYER_IS_EXPLODING_4)
3586 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3588 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3589 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3590 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3593 /* restore probably existing indestructible background element */
3594 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3595 element = Feld[x][y] = Back[x][y];
3598 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3599 GfxDir[x][y] = MV_NO_MOVING;
3600 ChangeDelay[x][y] = 0;
3601 ChangePage[x][y] = -1;
3604 InitField_WithBug2(x, y, FALSE);
3606 InitField(x, y, FALSE);
3608 /* !!! not needed !!! */
3610 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3611 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3614 if (CAN_MOVE(element))
3619 DrawLevelField(x, y);
3621 TestIfElementTouchesCustomElement(x, y);
3623 if (GFX_CRUMBLED(element))
3624 DrawLevelFieldCrumbledSandNeighbours(x, y);
3626 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3627 StorePlayer[x][y] = 0;
3629 if (ELEM_IS_PLAYER(element))
3630 RelocatePlayer(x, y, element);
3633 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3635 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3639 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3641 int stored = Store[x][y];
3642 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3643 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3647 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3649 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3653 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3657 printf("::: %d / %d [%d - %d]\n",
3658 GfxFrame[x][y], phase - delay, phase, delay);
3662 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3663 element_info[GfxElement[x][y]].token_name,
3668 DrawLevelFieldCrumbledSand(x, y);
3670 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3672 DrawLevelElement(x, y, Back[x][y]);
3673 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3675 else if (IS_WALKABLE_UNDER(Back[x][y]))
3677 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3678 DrawLevelElementThruMask(x, y, Back[x][y]);
3680 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3681 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3685 void DynaExplode(int ex, int ey)
3688 int dynabomb_element = Feld[ex][ey];
3689 int dynabomb_size = 1;
3690 boolean dynabomb_xl = FALSE;
3691 struct PlayerInfo *player;
3692 static int xy[4][2] =
3700 if (IS_ACTIVE_BOMB(dynabomb_element))
3702 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3703 dynabomb_size = player->dynabomb_size;
3704 dynabomb_xl = player->dynabomb_xl;
3705 player->dynabombs_left++;
3708 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3710 for (i = 0; i < NUM_DIRECTIONS; i++)
3712 for (j = 1; j <= dynabomb_size; j++)
3714 int x = ex + j * xy[i][0];
3715 int y = ey + j * xy[i][1];
3718 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3721 element = Feld[x][y];
3723 /* do not restart explosions of fields with active bombs */
3724 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3727 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3731 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3732 !IS_DIGGABLE(element) && !dynabomb_xl)
3735 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3736 !CAN_GROW_INTO(element) && !dynabomb_xl)
3740 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3741 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3742 element != EL_SAND && !dynabomb_xl)
3749 void Bang(int x, int y)
3752 int element = MovingOrBlocked2Element(x, y);
3754 int element = Feld[x][y];
3758 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3760 if (IS_PLAYER(x, y))
3763 struct PlayerInfo *player = PLAYERINFO(x, y);
3765 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3766 player->element_nr);
3771 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3773 if (game.emulation == EMU_SUPAPLEX)
3774 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3776 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3781 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3789 case EL_BD_BUTTERFLY:
3792 case EL_DARK_YAMYAM:
3796 RaiseScoreElement(element);
3797 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3799 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3800 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3801 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3802 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3803 case EL_DYNABOMB_INCREASE_NUMBER:
3804 case EL_DYNABOMB_INCREASE_SIZE:
3805 case EL_DYNABOMB_INCREASE_POWER:
3810 case EL_LAMP_ACTIVE:
3812 case EL_AMOEBA_TO_DIAMOND:
3814 if (IS_PLAYER(x, y))
3815 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3817 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3821 if (element_info[element].explosion_type == EXPLODES_CROSS)
3823 if (CAN_EXPLODE_CROSS(element))
3826 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3831 else if (element_info[element].explosion_type == EXPLODES_1X1)
3833 else if (CAN_EXPLODE_1X1(element))
3835 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3837 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3841 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3844 void SplashAcid(int x, int y)
3847 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3848 (!IN_LEV_FIELD(x - 1, y - 2) ||
3849 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3850 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3852 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3853 (!IN_LEV_FIELD(x + 1, y - 2) ||
3854 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3855 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3857 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3859 /* input: position of element entering acid (obsolete) */
3861 int element = Feld[x][y];
3863 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3866 if (element != EL_ACID_SPLASH_LEFT &&
3867 element != EL_ACID_SPLASH_RIGHT)
3869 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3871 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3872 (!IN_LEV_FIELD(x - 1, y - 1) ||
3873 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3874 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3876 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3877 (!IN_LEV_FIELD(x + 1, y - 1) ||
3878 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3879 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3884 static void InitBeltMovement()
3886 static int belt_base_element[4] =
3888 EL_CONVEYOR_BELT_1_LEFT,
3889 EL_CONVEYOR_BELT_2_LEFT,
3890 EL_CONVEYOR_BELT_3_LEFT,
3891 EL_CONVEYOR_BELT_4_LEFT
3893 static int belt_base_active_element[4] =
3895 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3896 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3897 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3898 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3903 /* set frame order for belt animation graphic according to belt direction */
3904 for (i = 0; i < NUM_BELTS; i++)
3908 for (j = 0; j < NUM_BELT_PARTS; j++)
3910 int element = belt_base_active_element[belt_nr] + j;
3911 int graphic = el2img(element);
3913 if (game.belt_dir[i] == MV_LEFT)
3914 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3916 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3920 for (y = 0; y < lev_fieldy; y++)
3922 for (x = 0; x < lev_fieldx; x++)
3924 int element = Feld[x][y];
3926 for (i = 0; i < NUM_BELTS; i++)
3928 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3930 int e_belt_nr = getBeltNrFromBeltElement(element);
3933 if (e_belt_nr == belt_nr)
3935 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3937 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3945 static void ToggleBeltSwitch(int x, int y)
3947 static int belt_base_element[4] =
3949 EL_CONVEYOR_BELT_1_LEFT,
3950 EL_CONVEYOR_BELT_2_LEFT,
3951 EL_CONVEYOR_BELT_3_LEFT,
3952 EL_CONVEYOR_BELT_4_LEFT
3954 static int belt_base_active_element[4] =
3956 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3957 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3958 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3959 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3961 static int belt_base_switch_element[4] =
3963 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3964 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3965 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3966 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3968 static int belt_move_dir[4] =
3976 int element = Feld[x][y];
3977 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3978 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3979 int belt_dir = belt_move_dir[belt_dir_nr];
3982 if (!IS_BELT_SWITCH(element))
3985 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3986 game.belt_dir[belt_nr] = belt_dir;
3988 if (belt_dir_nr == 3)
3991 /* set frame order for belt animation graphic according to belt direction */
3992 for (i = 0; i < NUM_BELT_PARTS; i++)
3994 int element = belt_base_active_element[belt_nr] + i;
3995 int graphic = el2img(element);
3997 if (belt_dir == MV_LEFT)
3998 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4000 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4003 for (yy = 0; yy < lev_fieldy; yy++)
4005 for (xx = 0; xx < lev_fieldx; xx++)
4007 int element = Feld[xx][yy];
4009 if (IS_BELT_SWITCH(element))
4011 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4013 if (e_belt_nr == belt_nr)
4015 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4016 DrawLevelField(xx, yy);
4019 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4021 int e_belt_nr = getBeltNrFromBeltElement(element);
4023 if (e_belt_nr == belt_nr)
4025 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4027 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4028 DrawLevelField(xx, yy);
4031 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4033 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4035 if (e_belt_nr == belt_nr)
4037 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4039 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4040 DrawLevelField(xx, yy);
4047 static void ToggleSwitchgateSwitch(int x, int y)
4051 game.switchgate_pos = !game.switchgate_pos;
4053 for (yy = 0; yy < lev_fieldy; yy++)
4055 for (xx = 0; xx < lev_fieldx; xx++)
4057 int element = Feld[xx][yy];
4059 if (element == EL_SWITCHGATE_SWITCH_UP ||
4060 element == EL_SWITCHGATE_SWITCH_DOWN)
4062 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4063 DrawLevelField(xx, yy);
4065 else if (element == EL_SWITCHGATE_OPEN ||
4066 element == EL_SWITCHGATE_OPENING)
4068 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4070 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4072 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4075 else if (element == EL_SWITCHGATE_CLOSED ||
4076 element == EL_SWITCHGATE_CLOSING)
4078 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4080 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4082 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4089 static int getInvisibleActiveFromInvisibleElement(int element)
4091 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4092 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4093 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4097 static int getInvisibleFromInvisibleActiveElement(int element)
4099 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4100 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4101 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4105 static void RedrawAllLightSwitchesAndInvisibleElements()
4109 for (y = 0; y < lev_fieldy; y++)
4111 for (x = 0; x < lev_fieldx; x++)
4113 int element = Feld[x][y];
4115 if (element == EL_LIGHT_SWITCH &&
4116 game.light_time_left > 0)
4118 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4119 DrawLevelField(x, y);
4121 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4122 game.light_time_left == 0)
4124 Feld[x][y] = EL_LIGHT_SWITCH;
4125 DrawLevelField(x, y);
4127 else if (element == EL_INVISIBLE_STEELWALL ||
4128 element == EL_INVISIBLE_WALL ||
4129 element == EL_INVISIBLE_SAND)
4131 if (game.light_time_left > 0)
4132 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4134 DrawLevelField(x, y);
4136 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4137 element == EL_INVISIBLE_WALL_ACTIVE ||
4138 element == EL_INVISIBLE_SAND_ACTIVE)
4140 if (game.light_time_left == 0)
4141 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4143 DrawLevelField(x, y);
4149 static void ToggleLightSwitch(int x, int y)
4151 int element = Feld[x][y];
4153 game.light_time_left =
4154 (element == EL_LIGHT_SWITCH ?
4155 level.time_light * FRAMES_PER_SECOND : 0);
4157 RedrawAllLightSwitchesAndInvisibleElements();
4160 static void ActivateTimegateSwitch(int x, int y)
4164 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4166 for (yy = 0; yy < lev_fieldy; yy++)
4168 for (xx = 0; xx < lev_fieldx; xx++)
4170 int element = Feld[xx][yy];
4172 if (element == EL_TIMEGATE_CLOSED ||
4173 element == EL_TIMEGATE_CLOSING)
4175 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4176 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4180 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4182 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4183 DrawLevelField(xx, yy);
4190 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4193 inline static int getElementMoveStepsize(int x, int y)
4195 int element = Feld[x][y];
4196 int direction = MovDir[x][y];
4197 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4198 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4199 int horiz_move = (dx != 0);
4200 int sign = (horiz_move ? dx : dy);
4201 int step = sign * element_info[element].move_stepsize;
4203 /* special values for move stepsize for spring and things on conveyor belt */
4207 if (element == EL_SPRING)
4208 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4209 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4210 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4211 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4213 if (CAN_FALL(element) &&
4214 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4215 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4216 else if (element == EL_SPRING)
4217 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4224 void Impact(int x, int y)
4226 boolean lastline = (y == lev_fieldy-1);
4227 boolean object_hit = FALSE;
4228 boolean impact = (lastline || object_hit);
4229 int element = Feld[x][y];
4230 int smashed = EL_STEELWALL;
4232 if (!lastline) /* check if element below was hit */
4234 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4237 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4238 MovDir[x][y + 1] != MV_DOWN ||
4239 MovPos[x][y + 1] <= TILEY / 2));
4242 object_hit = !IS_FREE(x, y + 1);
4245 /* do not smash moving elements that left the smashed field in time */
4246 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4247 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4251 smashed = MovingOrBlocked2Element(x, y + 1);
4253 impact = (lastline || object_hit);
4256 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4258 SplashAcid(x, y + 1);
4262 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4263 /* only reset graphic animation if graphic really changes after impact */
4265 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4267 ResetGfxAnimation(x, y);
4268 DrawLevelField(x, y);
4271 if (impact && CAN_EXPLODE_IMPACT(element))
4276 else if (impact && element == EL_PEARL)
4278 ResetGfxAnimation(x, y);
4280 Feld[x][y] = EL_PEARL_BREAKING;
4281 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4284 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4286 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4291 if (impact && element == EL_AMOEBA_DROP)
4293 if (object_hit && IS_PLAYER(x, y + 1))
4294 KillHeroUnlessEnemyProtected(x, y + 1);
4295 else if (object_hit && smashed == EL_PENGUIN)
4299 Feld[x][y] = EL_AMOEBA_GROWING;
4300 Store[x][y] = EL_AMOEBA_WET;
4302 ResetRandomAnimationValue(x, y);
4307 if (object_hit) /* check which object was hit */
4309 if (CAN_PASS_MAGIC_WALL(element) &&
4310 (smashed == EL_MAGIC_WALL ||
4311 smashed == EL_BD_MAGIC_WALL))
4314 int activated_magic_wall =
4315 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4316 EL_BD_MAGIC_WALL_ACTIVE);
4318 /* activate magic wall / mill */
4319 for (yy = 0; yy < lev_fieldy; yy++)
4320 for (xx = 0; xx < lev_fieldx; xx++)
4321 if (Feld[xx][yy] == smashed)
4322 Feld[xx][yy] = activated_magic_wall;
4324 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4325 game.magic_wall_active = TRUE;
4327 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4328 SND_MAGIC_WALL_ACTIVATING :
4329 SND_BD_MAGIC_WALL_ACTIVATING));
4332 if (IS_PLAYER(x, y + 1))
4334 if (CAN_SMASH_PLAYER(element))
4336 KillHeroUnlessEnemyProtected(x, y + 1);
4340 else if (smashed == EL_PENGUIN)
4342 if (CAN_SMASH_PLAYER(element))
4348 else if (element == EL_BD_DIAMOND)
4350 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4356 else if (((element == EL_SP_INFOTRON ||
4357 element == EL_SP_ZONK) &&
4358 (smashed == EL_SP_SNIKSNAK ||
4359 smashed == EL_SP_ELECTRON ||
4360 smashed == EL_SP_DISK_ORANGE)) ||
4361 (element == EL_SP_INFOTRON &&
4362 smashed == EL_SP_DISK_YELLOW))
4368 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4374 else if (CAN_SMASH_EVERYTHING(element))
4376 if (IS_CLASSIC_ENEMY(smashed) ||
4377 CAN_EXPLODE_SMASHED(smashed))
4382 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4384 if (smashed == EL_LAMP ||
4385 smashed == EL_LAMP_ACTIVE)
4390 else if (smashed == EL_NUT)
4392 Feld[x][y + 1] = EL_NUT_BREAKING;
4393 PlayLevelSound(x, y, SND_NUT_BREAKING);
4394 RaiseScoreElement(EL_NUT);
4397 else if (smashed == EL_PEARL)
4399 ResetGfxAnimation(x, y);
4401 Feld[x][y + 1] = EL_PEARL_BREAKING;
4402 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4405 else if (smashed == EL_DIAMOND)
4407 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4408 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4411 else if (IS_BELT_SWITCH(smashed))
4413 ToggleBeltSwitch(x, y + 1);
4415 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4416 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4418 ToggleSwitchgateSwitch(x, y + 1);
4420 else if (smashed == EL_LIGHT_SWITCH ||
4421 smashed == EL_LIGHT_SWITCH_ACTIVE)
4423 ToggleLightSwitch(x, y + 1);
4428 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4431 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4434 /* !!! TEST ONLY !!! */
4435 CheckElementChangeBySide(x, y + 1, smashed, element,
4436 CE_SWITCHED, CH_SIDE_TOP);
4437 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4438 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4440 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4441 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4442 CheckElementChangeBySide(x, y + 1, smashed, element,
4443 CE_SWITCHED, CH_SIDE_TOP);
4449 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4454 /* play sound of magic wall / mill */
4456 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4457 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4459 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4460 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4461 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4462 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4467 /* play sound of object that hits the ground */
4468 if (lastline || object_hit)
4469 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4472 inline static void TurnRoundExt(int x, int y)
4484 { 0, 0 }, { 0, 0 }, { 0, 0 },
4489 int left, right, back;
4493 { MV_DOWN, MV_UP, MV_RIGHT },
4494 { MV_UP, MV_DOWN, MV_LEFT },
4496 { MV_LEFT, MV_RIGHT, MV_DOWN },
4500 { MV_RIGHT, MV_LEFT, MV_UP }
4503 int element = Feld[x][y];
4504 int move_pattern = element_info[element].move_pattern;
4506 int old_move_dir = MovDir[x][y];
4507 int left_dir = turn[old_move_dir].left;
4508 int right_dir = turn[old_move_dir].right;
4509 int back_dir = turn[old_move_dir].back;
4511 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4512 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4513 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4514 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4516 int left_x = x + left_dx, left_y = y + left_dy;
4517 int right_x = x + right_dx, right_y = y + right_dy;
4518 int move_x = x + move_dx, move_y = y + move_dy;
4522 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4524 TestIfBadThingTouchesOtherBadThing(x, y);
4526 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4527 MovDir[x][y] = right_dir;
4528 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4529 MovDir[x][y] = left_dir;
4531 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4533 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4537 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4538 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4540 TestIfBadThingTouchesOtherBadThing(x, y);
4542 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4543 MovDir[x][y] = left_dir;
4544 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4545 MovDir[x][y] = right_dir;
4547 if ((element == EL_SPACESHIP ||
4548 element == EL_SP_SNIKSNAK ||
4549 element == EL_SP_ELECTRON)
4550 && MovDir[x][y] != old_move_dir)
4552 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4556 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4558 TestIfBadThingTouchesOtherBadThing(x, y);
4560 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4561 MovDir[x][y] = left_dir;
4562 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4563 MovDir[x][y] = right_dir;
4565 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4567 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4570 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4572 TestIfBadThingTouchesOtherBadThing(x, y);
4574 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4575 MovDir[x][y] = left_dir;
4576 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4577 MovDir[x][y] = right_dir;
4579 if (MovDir[x][y] != old_move_dir)
4583 else if (element == EL_YAMYAM)
4585 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4586 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4588 if (can_turn_left && can_turn_right)
4589 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4590 else if (can_turn_left)
4591 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4592 else if (can_turn_right)
4593 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4595 MovDir[x][y] = back_dir;
4597 MovDelay[x][y] = 16 + 16 * RND(3);
4599 else if (element == EL_DARK_YAMYAM)
4601 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4603 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4606 if (can_turn_left && can_turn_right)
4607 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4608 else if (can_turn_left)
4609 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4610 else if (can_turn_right)
4611 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4613 MovDir[x][y] = back_dir;
4615 MovDelay[x][y] = 16 + 16 * RND(3);
4617 else if (element == EL_PACMAN)
4619 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4620 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4622 if (can_turn_left && can_turn_right)
4623 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4624 else if (can_turn_left)
4625 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4626 else if (can_turn_right)
4627 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4629 MovDir[x][y] = back_dir;
4631 MovDelay[x][y] = 6 + RND(40);
4633 else if (element == EL_PIG)
4635 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4636 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4637 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4638 boolean should_turn_left, should_turn_right, should_move_on;
4640 int rnd = RND(rnd_value);
4642 should_turn_left = (can_turn_left &&
4644 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4645 y + back_dy + left_dy)));
4646 should_turn_right = (can_turn_right &&
4648 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4649 y + back_dy + right_dy)));
4650 should_move_on = (can_move_on &&
4653 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4654 y + move_dy + left_dy) ||
4655 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4656 y + move_dy + right_dy)));
4658 if (should_turn_left || should_turn_right || should_move_on)
4660 if (should_turn_left && should_turn_right && should_move_on)
4661 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4662 rnd < 2 * rnd_value / 3 ? right_dir :
4664 else if (should_turn_left && should_turn_right)
4665 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4666 else if (should_turn_left && should_move_on)
4667 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4668 else if (should_turn_right && should_move_on)
4669 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4670 else if (should_turn_left)
4671 MovDir[x][y] = left_dir;
4672 else if (should_turn_right)
4673 MovDir[x][y] = right_dir;
4674 else if (should_move_on)
4675 MovDir[x][y] = old_move_dir;
4677 else if (can_move_on && rnd > rnd_value / 8)
4678 MovDir[x][y] = old_move_dir;
4679 else if (can_turn_left && can_turn_right)
4680 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4681 else if (can_turn_left && rnd > rnd_value / 8)
4682 MovDir[x][y] = left_dir;
4683 else if (can_turn_right && rnd > rnd_value/8)
4684 MovDir[x][y] = right_dir;
4686 MovDir[x][y] = back_dir;
4688 xx = x + move_xy[MovDir[x][y]].x;
4689 yy = y + move_xy[MovDir[x][y]].y;
4692 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4693 if (!IN_LEV_FIELD(xx, yy) ||
4694 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4695 MovDir[x][y] = old_move_dir;
4697 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4698 MovDir[x][y] = old_move_dir;
4703 else if (element == EL_DRAGON)
4705 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4706 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4707 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4709 int rnd = RND(rnd_value);
4712 if (FrameCounter < 1 && x == 0 && y == 29)
4713 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4716 if (can_move_on && rnd > rnd_value / 8)
4717 MovDir[x][y] = old_move_dir;
4718 else if (can_turn_left && can_turn_right)
4719 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4720 else if (can_turn_left && rnd > rnd_value / 8)
4721 MovDir[x][y] = left_dir;
4722 else if (can_turn_right && rnd > rnd_value / 8)
4723 MovDir[x][y] = right_dir;
4725 MovDir[x][y] = back_dir;
4727 xx = x + move_xy[MovDir[x][y]].x;
4728 yy = y + move_xy[MovDir[x][y]].y;
4731 if (FrameCounter < 1 && x == 0 && y == 29)
4732 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4733 xx, yy, Feld[xx][yy],
4738 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4739 MovDir[x][y] = old_move_dir;
4741 if (!IS_FREE(xx, yy))
4742 MovDir[x][y] = old_move_dir;
4746 if (FrameCounter < 1 && x == 0 && y == 29)
4747 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4752 else if (element == EL_MOLE)
4754 boolean can_move_on =
4755 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4756 IS_AMOEBOID(Feld[move_x][move_y]) ||
4757 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4760 boolean can_turn_left =
4761 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4762 IS_AMOEBOID(Feld[left_x][left_y])));
4764 boolean can_turn_right =
4765 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4766 IS_AMOEBOID(Feld[right_x][right_y])));
4768 if (can_turn_left && can_turn_right)
4769 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4770 else if (can_turn_left)
4771 MovDir[x][y] = left_dir;
4773 MovDir[x][y] = right_dir;
4776 if (MovDir[x][y] != old_move_dir)
4779 else if (element == EL_BALLOON)
4781 MovDir[x][y] = game.balloon_dir;
4784 else if (element == EL_SPRING)
4787 if (MovDir[x][y] & MV_HORIZONTAL &&
4788 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4789 MovDir[x][y] = MV_NO_MOVING;
4791 if (MovDir[x][y] & MV_HORIZONTAL &&
4792 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4793 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4794 MovDir[x][y] = MV_NO_MOVING;
4799 else if (element == EL_ROBOT ||
4800 element == EL_SATELLITE ||
4801 element == EL_PENGUIN)
4803 int attr_x = -1, attr_y = -1;
4814 for (i = 0; i < MAX_PLAYERS; i++)
4816 struct PlayerInfo *player = &stored_player[i];
4817 int jx = player->jx, jy = player->jy;
4819 if (!player->active)
4823 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4832 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4833 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4834 game.engine_version < VERSION_IDENT(3,1,0,0)))
4836 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4843 if (element == EL_PENGUIN)
4846 static int xy[4][2] =
4854 for (i = 0; i < NUM_DIRECTIONS; i++)
4856 int ex = x + xy[i][0];
4857 int ey = y + xy[i][1];
4859 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4868 MovDir[x][y] = MV_NO_MOVING;
4870 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4871 else if (attr_x > x)
4872 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4874 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4875 else if (attr_y > y)
4876 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4878 if (element == EL_ROBOT)
4882 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4883 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4884 Moving2Blocked(x, y, &newx, &newy);
4886 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4887 MovDelay[x][y] = 8 + 8 * !RND(3);
4889 MovDelay[x][y] = 16;
4891 else if (element == EL_PENGUIN)
4897 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4899 boolean first_horiz = RND(2);
4900 int new_move_dir = MovDir[x][y];
4903 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4904 Moving2Blocked(x, y, &newx, &newy);
4906 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4910 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4911 Moving2Blocked(x, y, &newx, &newy);
4913 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4916 MovDir[x][y] = old_move_dir;
4920 else /* (element == EL_SATELLITE) */
4926 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4928 boolean first_horiz = RND(2);
4929 int new_move_dir = MovDir[x][y];
4932 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4933 Moving2Blocked(x, y, &newx, &newy);
4935 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4939 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4940 Moving2Blocked(x, y, &newx, &newy);
4942 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4945 MovDir[x][y] = old_move_dir;
4950 else if (move_pattern == MV_TURNING_LEFT ||
4951 move_pattern == MV_TURNING_RIGHT ||
4952 move_pattern == MV_TURNING_LEFT_RIGHT ||
4953 move_pattern == MV_TURNING_RIGHT_LEFT ||
4954 move_pattern == MV_TURNING_RANDOM ||
4955 move_pattern == MV_ALL_DIRECTIONS)
4957 boolean can_turn_left =
4958 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4959 boolean can_turn_right =
4960 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4962 if (move_pattern == MV_TURNING_LEFT)
4963 MovDir[x][y] = left_dir;
4964 else if (move_pattern == MV_TURNING_RIGHT)
4965 MovDir[x][y] = right_dir;
4966 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4967 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4968 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4969 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4970 else if (move_pattern == MV_TURNING_RANDOM)
4971 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4972 can_turn_right && !can_turn_left ? right_dir :
4973 RND(2) ? left_dir : right_dir);
4974 else if (can_turn_left && can_turn_right)
4975 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4976 else if (can_turn_left)
4977 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4978 else if (can_turn_right)
4979 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4981 MovDir[x][y] = back_dir;
4983 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4985 else if (move_pattern == MV_HORIZONTAL ||
4986 move_pattern == MV_VERTICAL)
4988 if (move_pattern & old_move_dir)
4989 MovDir[x][y] = back_dir;
4990 else if (move_pattern == MV_HORIZONTAL)
4991 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4992 else if (move_pattern == MV_VERTICAL)
4993 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4995 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4997 else if (move_pattern & MV_ANY_DIRECTION)
4999 MovDir[x][y] = move_pattern;
5000 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5002 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5004 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5005 MovDir[x][y] = left_dir;
5006 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5007 MovDir[x][y] = right_dir;
5009 if (MovDir[x][y] != old_move_dir)
5010 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5012 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5014 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5015 MovDir[x][y] = right_dir;
5016 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5017 MovDir[x][y] = left_dir;
5019 if (MovDir[x][y] != old_move_dir)
5020 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5022 else if (move_pattern == MV_TOWARDS_PLAYER ||
5023 move_pattern == MV_AWAY_FROM_PLAYER)
5025 int attr_x = -1, attr_y = -1;
5027 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5038 for (i = 0; i < MAX_PLAYERS; i++)
5040 struct PlayerInfo *player = &stored_player[i];
5041 int jx = player->jx, jy = player->jy;
5043 if (!player->active)
5047 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5055 MovDir[x][y] = MV_NO_MOVING;
5057 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5058 else if (attr_x > x)
5059 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5061 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5062 else if (attr_y > y)
5063 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5065 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5067 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5069 boolean first_horiz = RND(2);
5070 int new_move_dir = MovDir[x][y];
5073 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5074 Moving2Blocked(x, y, &newx, &newy);
5076 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5080 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5081 Moving2Blocked(x, y, &newx, &newy);
5083 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5086 MovDir[x][y] = old_move_dir;
5089 else if (move_pattern == MV_WHEN_PUSHED ||
5090 move_pattern == MV_WHEN_DROPPED)
5092 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5093 MovDir[x][y] = MV_NO_MOVING;
5097 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5099 static int test_xy[7][2] =
5109 static int test_dir[7] =
5119 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5120 int move_preference = -1000000; /* start with very low preference */
5121 int new_move_dir = MV_NO_MOVING;
5122 int start_test = RND(4);
5125 for (i = 0; i < NUM_DIRECTIONS; i++)
5127 int move_dir = test_dir[start_test + i];
5128 int move_dir_preference;
5130 xx = x + test_xy[start_test + i][0];
5131 yy = y + test_xy[start_test + i][1];
5133 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5134 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5136 new_move_dir = move_dir;
5141 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5144 move_dir_preference = -1 * RunnerVisit[xx][yy];
5145 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5146 move_dir_preference = PlayerVisit[xx][yy];
5148 if (move_dir_preference > move_preference)
5150 /* prefer field that has not been visited for the longest time */
5151 move_preference = move_dir_preference;
5152 new_move_dir = move_dir;
5154 else if (move_dir_preference == move_preference &&
5155 move_dir == old_move_dir)
5157 /* prefer last direction when all directions are preferred equally */
5158 move_preference = move_dir_preference;
5159 new_move_dir = move_dir;
5163 MovDir[x][y] = new_move_dir;
5164 if (old_move_dir != new_move_dir)
5167 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5175 static void TurnRound(int x, int y)
5177 int direction = MovDir[x][y];
5180 GfxDir[x][y] = MovDir[x][y];
5186 GfxDir[x][y] = MovDir[x][y];
5189 if (direction != MovDir[x][y])
5194 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5197 GfxAction[x][y] = ACTION_WAITING;
5201 static boolean JustBeingPushed(int x, int y)
5205 for (i = 0; i < MAX_PLAYERS; i++)
5207 struct PlayerInfo *player = &stored_player[i];
5209 if (player->active && player->is_pushing && player->MovPos)
5211 int next_jx = player->jx + (player->jx - player->last_jx);
5212 int next_jy = player->jy + (player->jy - player->last_jy);
5214 if (x == next_jx && y == next_jy)
5222 void StartMoving(int x, int y)
5225 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5227 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5228 int element = Feld[x][y];
5234 if (MovDelay[x][y] == 0)
5235 GfxAction[x][y] = ACTION_DEFAULT;
5237 /* !!! this should be handled more generic (not only for mole) !!! */
5238 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5239 GfxAction[x][y] = ACTION_DEFAULT;
5242 if (CAN_FALL(element) && y < lev_fieldy - 1)
5244 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5245 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5246 if (JustBeingPushed(x, y))
5249 if (element == EL_QUICKSAND_FULL)
5251 if (IS_FREE(x, y + 1))
5253 InitMovingField(x, y, MV_DOWN);
5254 started_moving = TRUE;
5256 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5257 Store[x][y] = EL_ROCK;
5259 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5261 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5264 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5266 if (!MovDelay[x][y])
5267 MovDelay[x][y] = TILEY + 1;
5276 Feld[x][y] = EL_QUICKSAND_EMPTY;
5277 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5278 Store[x][y + 1] = Store[x][y];
5281 PlayLevelSoundAction(x, y, ACTION_FILLING);
5283 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5287 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5288 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5290 InitMovingField(x, y, MV_DOWN);
5291 started_moving = TRUE;
5293 Feld[x][y] = EL_QUICKSAND_FILLING;
5294 Store[x][y] = element;
5296 PlayLevelSoundAction(x, y, ACTION_FILLING);
5298 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5301 else if (element == EL_MAGIC_WALL_FULL)
5303 if (IS_FREE(x, y + 1))
5305 InitMovingField(x, y, MV_DOWN);
5306 started_moving = TRUE;
5308 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5309 Store[x][y] = EL_CHANGED(Store[x][y]);
5311 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5313 if (!MovDelay[x][y])
5314 MovDelay[x][y] = TILEY/4 + 1;
5323 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5324 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5325 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5329 else if (element == EL_BD_MAGIC_WALL_FULL)
5331 if (IS_FREE(x, y + 1))
5333 InitMovingField(x, y, MV_DOWN);
5334 started_moving = TRUE;
5336 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5337 Store[x][y] = EL_CHANGED2(Store[x][y]);
5339 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5341 if (!MovDelay[x][y])
5342 MovDelay[x][y] = TILEY/4 + 1;
5351 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5352 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5353 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5357 else if (CAN_PASS_MAGIC_WALL(element) &&
5358 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5359 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5361 InitMovingField(x, y, MV_DOWN);
5362 started_moving = TRUE;
5365 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5366 EL_BD_MAGIC_WALL_FILLING);
5367 Store[x][y] = element;
5370 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5372 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5375 SplashAcid(x, y + 1);
5377 InitMovingField(x, y, MV_DOWN);
5378 started_moving = TRUE;
5380 Store[x][y] = EL_ACID;
5382 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5383 GfxAction[x][y + 1] = ACTION_ACTIVE;
5387 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5388 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5390 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5391 CAN_SMASH(element) && WasJustFalling[x][y] &&
5392 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5394 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5395 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5396 (Feld[x][y + 1] == EL_BLOCKED)))
5400 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5401 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5402 WasJustMoving[x][y] && !Pushed[x][y + 1])
5404 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5405 WasJustMoving[x][y])
5410 /* this is needed for a special case not covered by calling "Impact()"
5411 from "ContinueMoving()": if an element moves to a tile directly below
5412 another element which was just falling on that tile (which was empty
5413 in the previous frame), the falling element above would just stop
5414 instead of smashing the element below (in previous version, the above
5415 element was just checked for "moving" instead of "falling", resulting
5416 in incorrect smashes caused by horizontal movement of the above
5417 element; also, the case of the player being the element to smash was
5418 simply not covered here... :-/ ) */
5421 WasJustMoving[x][y] = 0;
5422 WasJustFalling[x][y] = 0;
5425 CheckCollision[x][y] = 0;
5428 if (IS_PLAYER(x, y + 1))
5429 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5434 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5436 if (MovDir[x][y] == MV_NO_MOVING)
5438 InitMovingField(x, y, MV_DOWN);
5439 started_moving = TRUE;
5442 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5444 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5445 MovDir[x][y] = MV_DOWN;
5447 InitMovingField(x, y, MV_DOWN);
5448 started_moving = TRUE;
5450 else if (element == EL_AMOEBA_DROP)
5452 Feld[x][y] = EL_AMOEBA_GROWING;
5453 Store[x][y] = EL_AMOEBA_WET;
5455 /* Store[x][y + 1] must be zero, because:
5456 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5459 #if OLD_GAME_BEHAVIOUR
5460 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5462 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5463 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5464 element != EL_DX_SUPABOMB)
5467 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5468 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5469 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5470 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5473 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5474 (IS_FREE(x - 1, y + 1) ||
5475 Feld[x - 1][y + 1] == EL_ACID));
5476 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5477 (IS_FREE(x + 1, y + 1) ||
5478 Feld[x + 1][y + 1] == EL_ACID));
5479 boolean can_fall_any = (can_fall_left || can_fall_right);
5480 boolean can_fall_both = (can_fall_left && can_fall_right);
5482 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5484 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5486 if (slippery_type == SLIPPERY_ONLY_LEFT)
5487 can_fall_right = FALSE;
5488 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5489 can_fall_left = FALSE;
5490 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5491 can_fall_right = FALSE;
5492 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5493 can_fall_left = FALSE;
5495 can_fall_any = (can_fall_left || can_fall_right);
5496 can_fall_both = (can_fall_left && can_fall_right);
5499 #if USE_NEW_SP_SLIPPERY
5500 /* !!! better use the same properties as for custom elements here !!! */
5501 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5502 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5504 can_fall_right = FALSE; /* slip down on left side */
5505 can_fall_both = FALSE;
5512 if (game.emulation == EMU_BOULDERDASH ||
5513 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5514 can_fall_right = FALSE; /* slip down on left side */
5516 can_fall_left = !(can_fall_right = RND(2));
5518 can_fall_both = FALSE;
5525 if (can_fall_both &&
5526 (game.emulation != EMU_BOULDERDASH &&
5527 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5528 can_fall_left = !(can_fall_right = RND(2));
5531 /* if not determined otherwise, prefer left side for slipping down */
5532 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5533 started_moving = TRUE;
5537 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5539 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5542 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5543 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5544 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5545 int belt_dir = game.belt_dir[belt_nr];
5547 if ((belt_dir == MV_LEFT && left_is_free) ||
5548 (belt_dir == MV_RIGHT && right_is_free))
5551 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5554 InitMovingField(x, y, belt_dir);
5555 started_moving = TRUE;
5558 Pushed[x][y] = TRUE;
5559 Pushed[nextx][y] = TRUE;
5562 GfxAction[x][y] = ACTION_DEFAULT;
5566 MovDir[x][y] = 0; /* if element was moving, stop it */
5571 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5573 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5575 if (CAN_MOVE(element) && !started_moving)
5578 int move_pattern = element_info[element].move_pattern;
5583 if (MovDir[x][y] == MV_NO_MOVING)
5585 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5586 x, y, element, element_info[element].token_name);
5587 printf("StartMoving(): This should never happen!\n");
5592 Moving2Blocked(x, y, &newx, &newy);
5595 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5598 if ((element == EL_SATELLITE ||
5599 element == EL_BALLOON ||
5600 element == EL_SPRING)
5601 && JustBeingPushed(x, y))
5608 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5609 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5611 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5612 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5613 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5617 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5618 element, element_info[element].token_name,
5619 WasJustMoving[x][y],
5620 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5621 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5622 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5623 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5627 WasJustMoving[x][y] = 0;
5630 CheckCollision[x][y] = 0;
5632 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5635 if (Feld[x][y] != element) /* element has changed */
5637 element = Feld[x][y];
5638 move_pattern = element_info[element].move_pattern;
5640 if (!CAN_MOVE(element))
5644 if (Feld[x][y] != element) /* element has changed */
5652 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5653 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5655 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5657 Moving2Blocked(x, y, &newx, &newy);
5658 if (Feld[newx][newy] == EL_BLOCKED)
5659 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5665 if (FrameCounter < 1 && x == 0 && y == 29)
5666 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5669 if (!MovDelay[x][y]) /* start new movement phase */
5671 /* all objects that can change their move direction after each step
5672 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5674 if (element != EL_YAMYAM &&
5675 element != EL_DARK_YAMYAM &&
5676 element != EL_PACMAN &&
5677 !(move_pattern & MV_ANY_DIRECTION) &&
5678 move_pattern != MV_TURNING_LEFT &&
5679 move_pattern != MV_TURNING_RIGHT &&
5680 move_pattern != MV_TURNING_LEFT_RIGHT &&
5681 move_pattern != MV_TURNING_RIGHT_LEFT &&
5682 move_pattern != MV_TURNING_RANDOM)
5687 if (FrameCounter < 1 && x == 0 && y == 29)
5688 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5691 if (MovDelay[x][y] && (element == EL_BUG ||
5692 element == EL_SPACESHIP ||
5693 element == EL_SP_SNIKSNAK ||
5694 element == EL_SP_ELECTRON ||
5695 element == EL_MOLE))
5696 DrawLevelField(x, y);
5700 if (MovDelay[x][y]) /* wait some time before next movement */
5705 if (element == EL_YAMYAM)
5708 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5709 DrawLevelElementAnimation(x, y, element);
5713 if (MovDelay[x][y]) /* element still has to wait some time */
5716 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5717 ResetGfxAnimation(x, y);
5721 if (GfxAction[x][y] != ACTION_WAITING)
5722 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5724 GfxAction[x][y] = ACTION_WAITING;
5728 if (element == EL_ROBOT ||
5730 element == EL_PACMAN ||
5732 element == EL_YAMYAM ||
5733 element == EL_DARK_YAMYAM)
5736 DrawLevelElementAnimation(x, y, element);
5738 DrawLevelElementAnimationIfNeeded(x, y, element);
5740 PlayLevelSoundAction(x, y, ACTION_WAITING);
5742 else if (element == EL_SP_ELECTRON)
5743 DrawLevelElementAnimationIfNeeded(x, y, element);
5744 else if (element == EL_DRAGON)
5747 int dir = MovDir[x][y];
5748 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5749 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5750 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5751 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5752 dir == MV_UP ? IMG_FLAMES_1_UP :
5753 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5754 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5757 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5760 GfxAction[x][y] = ACTION_ATTACKING;
5762 if (IS_PLAYER(x, y))
5763 DrawPlayerField(x, y);
5765 DrawLevelField(x, y);
5767 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5769 for (i = 1; i <= 3; i++)
5771 int xx = x + i * dx;
5772 int yy = y + i * dy;
5773 int sx = SCREENX(xx);
5774 int sy = SCREENY(yy);
5775 int flame_graphic = graphic + (i - 1);
5777 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5782 int flamed = MovingOrBlocked2Element(xx, yy);
5786 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5788 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5789 RemoveMovingField(xx, yy);
5791 RemoveField(xx, yy);
5793 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5796 RemoveMovingField(xx, yy);
5800 if (ChangeDelay[xx][yy])
5801 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5802 Feld[xx][yy] == EL_BLOCKED));
5806 ChangeDelay[xx][yy] = 0;
5808 Feld[xx][yy] = EL_FLAMES;
5809 if (IN_SCR_FIELD(sx, sy))
5811 DrawLevelFieldCrumbledSand(xx, yy);
5812 DrawGraphic(sx, sy, flame_graphic, frame);
5817 if (Feld[xx][yy] == EL_FLAMES)
5818 Feld[xx][yy] = EL_EMPTY;
5819 DrawLevelField(xx, yy);
5824 if (MovDelay[x][y]) /* element still has to wait some time */
5826 PlayLevelSoundAction(x, y, ACTION_WAITING);
5832 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5833 for all other elements GfxAction will be set by InitMovingField() */
5834 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5835 GfxAction[x][y] = ACTION_MOVING;
5839 /* now make next step */
5841 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5843 if (DONT_COLLIDE_WITH(element) &&
5844 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5845 !PLAYER_ENEMY_PROTECTED(newx, newy))
5848 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5852 /* player killed by element which is deadly when colliding with */
5854 KillHero(PLAYERINFO(newx, newy));
5861 else if (CAN_MOVE_INTO_ACID(element) &&
5862 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5863 (MovDir[x][y] == MV_DOWN ||
5864 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5866 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5867 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5871 else if ((element == EL_PENGUIN ||
5872 element == EL_ROBOT ||
5873 element == EL_SATELLITE ||
5874 element == EL_BALLOON ||
5875 IS_CUSTOM_ELEMENT(element)) &&
5876 IN_LEV_FIELD(newx, newy) &&
5877 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5880 SplashAcid(newx, newy);
5881 Store[x][y] = EL_ACID;
5883 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5885 if (Feld[newx][newy] == EL_EXIT_OPEN)
5889 DrawLevelField(x, y);
5891 Feld[x][y] = EL_EMPTY;
5892 DrawLevelField(x, y);
5895 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5896 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5897 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5899 local_player->friends_still_needed--;
5900 if (!local_player->friends_still_needed &&
5901 !local_player->GameOver && AllPlayersGone)
5902 local_player->LevelSolved = local_player->GameOver = TRUE;
5906 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5908 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5909 DrawLevelField(newx, newy);
5911 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5913 else if (!IS_FREE(newx, newy))
5915 GfxAction[x][y] = ACTION_WAITING;
5917 if (IS_PLAYER(x, y))
5918 DrawPlayerField(x, y);
5920 DrawLevelField(x, y);
5925 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5927 if (IS_FOOD_PIG(Feld[newx][newy]))
5929 if (IS_MOVING(newx, newy))
5930 RemoveMovingField(newx, newy);
5933 Feld[newx][newy] = EL_EMPTY;
5934 DrawLevelField(newx, newy);
5937 PlayLevelSound(x, y, SND_PIG_DIGGING);
5939 else if (!IS_FREE(newx, newy))
5941 if (IS_PLAYER(x, y))
5942 DrawPlayerField(x, y);
5944 DrawLevelField(x, y);
5953 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5956 else if (IS_CUSTOM_ELEMENT(element) &&
5957 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5961 !IS_FREE(newx, newy)
5966 int new_element = Feld[newx][newy];
5969 printf("::: '%s' digs '%s' [%d]\n",
5970 element_info[element].token_name,
5971 element_info[Feld[newx][newy]].token_name,
5972 StorePlayer[newx][newy]);
5975 if (!IS_FREE(newx, newy))
5977 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5978 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5981 /* no element can dig solid indestructible elements */
5982 if (IS_INDESTRUCTIBLE(new_element) &&
5983 !IS_DIGGABLE(new_element) &&
5984 !IS_COLLECTIBLE(new_element))
5987 if (AmoebaNr[newx][newy] &&
5988 (new_element == EL_AMOEBA_FULL ||
5989 new_element == EL_BD_AMOEBA ||
5990 new_element == EL_AMOEBA_GROWING))
5992 AmoebaCnt[AmoebaNr[newx][newy]]--;
5993 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5996 if (IS_MOVING(newx, newy))
5997 RemoveMovingField(newx, newy);
6000 RemoveField(newx, newy);
6001 DrawLevelField(newx, newy);
6004 /* if digged element was about to explode, prevent the explosion */
6005 ExplodeField[newx][newy] = EX_TYPE_NONE;
6007 PlayLevelSoundAction(x, y, action);
6012 Store[newx][newy] = EL_EMPTY;
6013 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6014 Store[newx][newy] = element_info[element].move_leave_element;
6016 Store[newx][newy] = EL_EMPTY;
6017 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6018 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6019 Store[newx][newy] = element_info[element].move_leave_element;
6022 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6023 element_info[element].can_leave_element = TRUE;
6026 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6028 RunnerVisit[x][y] = FrameCounter;
6029 PlayerVisit[x][y] /= 8; /* expire player visit path */
6035 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6037 if (!IS_FREE(newx, newy))
6039 if (IS_PLAYER(x, y))
6040 DrawPlayerField(x, y);
6042 DrawLevelField(x, y);
6048 boolean wanna_flame = !RND(10);
6049 int dx = newx - x, dy = newy - y;
6050 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6051 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6052 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6053 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6054 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6055 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6058 IS_CLASSIC_ENEMY(element1) ||
6059 IS_CLASSIC_ENEMY(element2)) &&
6060 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6061 element1 != EL_FLAMES && element2 != EL_FLAMES)
6064 ResetGfxAnimation(x, y);
6065 GfxAction[x][y] = ACTION_ATTACKING;
6068 if (IS_PLAYER(x, y))
6069 DrawPlayerField(x, y);
6071 DrawLevelField(x, y);
6073 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6075 MovDelay[x][y] = 50;
6079 RemoveField(newx, newy);
6081 Feld[newx][newy] = EL_FLAMES;
6082 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6085 RemoveField(newx1, newy1);
6087 Feld[newx1][newy1] = EL_FLAMES;
6089 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6092 RemoveField(newx2, newy2);
6094 Feld[newx2][newy2] = EL_FLAMES;
6101 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6102 Feld[newx][newy] == EL_DIAMOND)
6104 if (IS_MOVING(newx, newy))
6105 RemoveMovingField(newx, newy);
6108 Feld[newx][newy] = EL_EMPTY;
6109 DrawLevelField(newx, newy);
6112 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6114 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6115 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6117 if (AmoebaNr[newx][newy])
6119 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6120 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6121 Feld[newx][newy] == EL_BD_AMOEBA)
6122 AmoebaCnt[AmoebaNr[newx][newy]]--;
6127 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6129 if (IS_MOVING(newx, newy))
6132 RemoveMovingField(newx, newy);
6136 Feld[newx][newy] = EL_EMPTY;
6137 DrawLevelField(newx, newy);
6140 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6142 else if ((element == EL_PACMAN || element == EL_MOLE)
6143 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6145 if (AmoebaNr[newx][newy])
6147 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6148 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6149 Feld[newx][newy] == EL_BD_AMOEBA)
6150 AmoebaCnt[AmoebaNr[newx][newy]]--;
6153 if (element == EL_MOLE)
6155 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6156 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6158 ResetGfxAnimation(x, y);
6159 GfxAction[x][y] = ACTION_DIGGING;
6160 DrawLevelField(x, y);
6162 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6164 return; /* wait for shrinking amoeba */
6166 else /* element == EL_PACMAN */
6168 Feld[newx][newy] = EL_EMPTY;
6169 DrawLevelField(newx, newy);
6170 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6173 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6174 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6175 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6177 /* wait for shrinking amoeba to completely disappear */
6180 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6182 /* object was running against a wall */
6187 if (move_pattern & MV_ANY_DIRECTION &&
6188 move_pattern == MovDir[x][y])
6190 int blocking_element =
6191 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6194 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6195 element_info[element].token_name,
6196 element_info[blocking_element].token_name,
6200 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6203 element = Feld[x][y]; /* element might have changed */
6208 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6209 DrawLevelElementAnimation(x, y, element);
6211 if (element == EL_BUG ||
6212 element == EL_SPACESHIP ||
6213 element == EL_SP_SNIKSNAK)
6214 DrawLevelField(x, y);
6215 else if (element == EL_MOLE)
6216 DrawLevelField(x, y);
6217 else if (element == EL_BD_BUTTERFLY ||
6218 element == EL_BD_FIREFLY)
6219 DrawLevelElementAnimationIfNeeded(x, y, element);
6220 else if (element == EL_SATELLITE)
6221 DrawLevelElementAnimationIfNeeded(x, y, element);
6222 else if (element == EL_SP_ELECTRON)
6223 DrawLevelElementAnimationIfNeeded(x, y, element);
6226 if (DONT_TOUCH(element))
6227 TestIfBadThingTouchesHero(x, y);
6230 PlayLevelSoundAction(x, y, ACTION_WAITING);
6236 InitMovingField(x, y, MovDir[x][y]);
6238 PlayLevelSoundAction(x, y, ACTION_MOVING);
6242 ContinueMoving(x, y);
6245 void ContinueMoving(int x, int y)
6247 int element = Feld[x][y];
6248 int stored = Store[x][y];
6249 struct ElementInfo *ei = &element_info[element];
6250 int direction = MovDir[x][y];
6251 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6252 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6253 int newx = x + dx, newy = y + dy;
6255 int nextx = newx + dx, nexty = newy + dy;
6258 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6259 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6261 boolean pushed_by_player = Pushed[x][y];
6264 MovPos[x][y] += getElementMoveStepsize(x, y);
6267 if (pushed_by_player && IS_PLAYER(x, y))
6269 /* special case: moving object pushed by player */
6270 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6273 if (pushed_by_player) /* special case: moving object pushed by player */
6274 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6277 if (ABS(MovPos[x][y]) < TILEX)
6279 DrawLevelField(x, y);
6281 return; /* element is still moving */
6284 /* element reached destination field */
6286 Feld[x][y] = EL_EMPTY;
6287 Feld[newx][newy] = element;
6288 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6291 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6293 element = Feld[newx][newy] = EL_ACID;
6296 else if (element == EL_MOLE)
6298 Feld[x][y] = EL_SAND;
6300 DrawLevelFieldCrumbledSandNeighbours(x, y);
6302 else if (element == EL_QUICKSAND_FILLING)
6304 element = Feld[newx][newy] = get_next_element(element);
6305 Store[newx][newy] = Store[x][y];
6307 else if (element == EL_QUICKSAND_EMPTYING)
6309 Feld[x][y] = get_next_element(element);
6310 element = Feld[newx][newy] = Store[x][y];
6312 else if (element == EL_MAGIC_WALL_FILLING)
6314 element = Feld[newx][newy] = get_next_element(element);
6315 if (!game.magic_wall_active)
6316 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6317 Store[newx][newy] = Store[x][y];
6319 else if (element == EL_MAGIC_WALL_EMPTYING)
6321 Feld[x][y] = get_next_element(element);
6322 if (!game.magic_wall_active)
6323 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6324 element = Feld[newx][newy] = Store[x][y];
6326 else if (element == EL_BD_MAGIC_WALL_FILLING)
6328 element = Feld[newx][newy] = get_next_element(element);
6329 if (!game.magic_wall_active)
6330 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6331 Store[newx][newy] = Store[x][y];
6333 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6335 Feld[x][y] = get_next_element(element);
6336 if (!game.magic_wall_active)
6337 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6338 element = Feld[newx][newy] = Store[x][y];
6340 else if (element == EL_AMOEBA_DROPPING)
6342 Feld[x][y] = get_next_element(element);
6343 element = Feld[newx][newy] = Store[x][y];
6345 else if (element == EL_SOKOBAN_OBJECT)
6348 Feld[x][y] = Back[x][y];
6350 if (Back[newx][newy])
6351 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6353 Back[x][y] = Back[newx][newy] = 0;
6356 else if (Store[x][y] == EL_ACID)
6358 element = Feld[newx][newy] = EL_ACID;
6362 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6363 ei->move_leave_element != EL_EMPTY &&
6364 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6365 Store[x][y] != EL_EMPTY))
6367 /* some elements can leave other elements behind after moving */
6369 Feld[x][y] = ei->move_leave_element;
6370 InitField(x, y, FALSE);
6372 if (GFX_CRUMBLED(Feld[x][y]))
6373 DrawLevelFieldCrumbledSandNeighbours(x, y);
6377 Store[x][y] = EL_EMPTY;
6378 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6379 MovDelay[newx][newy] = 0;
6381 if (CAN_CHANGE(element))
6383 /* copy element change control values to new field */
6384 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6385 ChangePage[newx][newy] = ChangePage[x][y];
6386 Changed[newx][newy] = Changed[x][y];
6387 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6390 ChangeDelay[x][y] = 0;
6391 ChangePage[x][y] = -1;
6392 Changed[x][y] = CE_BITMASK_DEFAULT;
6393 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6395 /* copy animation control values to new field */
6396 GfxFrame[newx][newy] = GfxFrame[x][y];
6397 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6398 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6399 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6401 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6403 ResetGfxAnimation(x, y); /* reset animation values for old field */
6406 /* some elements can leave other elements behind after moving */
6408 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6409 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6410 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6412 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6413 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6417 int move_leave_element = ei->move_leave_element;
6419 Feld[x][y] = move_leave_element;
6420 InitField(x, y, FALSE);
6422 if (GFX_CRUMBLED(Feld[x][y]))
6423 DrawLevelFieldCrumbledSandNeighbours(x, y);
6425 if (ELEM_IS_PLAYER(move_leave_element))
6426 RelocatePlayer(x, y, move_leave_element);
6431 /* some elements can leave other elements behind after moving */
6432 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6433 ei->move_leave_element != EL_EMPTY &&
6434 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6435 ei->can_leave_element_last))
6437 Feld[x][y] = ei->move_leave_element;
6438 InitField(x, y, FALSE);
6440 if (GFX_CRUMBLED(Feld[x][y]))
6441 DrawLevelFieldCrumbledSandNeighbours(x, y);
6444 ei->can_leave_element_last = ei->can_leave_element;
6445 ei->can_leave_element = FALSE;
6449 /* 2.1.1 (does not work correctly for spring) */
6450 if (!CAN_MOVE(element))
6451 MovDir[newx][newy] = 0;
6455 /* (does not work for falling objects that slide horizontally) */
6456 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6457 MovDir[newx][newy] = 0;
6460 if (!CAN_MOVE(element) ||
6461 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6462 MovDir[newx][newy] = 0;
6466 if (!CAN_MOVE(element) ||
6467 (CAN_FALL(element) && direction == MV_DOWN))
6468 GfxDir[x][y] = MovDir[newx][newy] = 0;
6470 if (!CAN_MOVE(element) ||
6471 (CAN_FALL(element) && direction == MV_DOWN &&
6472 (element == EL_SPRING ||
6473 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6474 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6475 GfxDir[x][y] = MovDir[newx][newy] = 0;
6481 DrawLevelField(x, y);
6482 DrawLevelField(newx, newy);
6484 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6486 /* prevent pushed element from moving on in pushed direction */
6487 if (pushed_by_player && CAN_MOVE(element) &&
6488 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6489 !(element_info[element].move_pattern & direction))
6490 TurnRound(newx, newy);
6493 /* prevent elements on conveyor belt from moving on in last direction */
6494 if (pushed_by_conveyor && CAN_FALL(element) &&
6495 direction & MV_HORIZONTAL)
6498 if (CAN_MOVE(element))
6499 InitMovDir(newx, newy);
6501 MovDir[newx][newy] = 0;
6503 MovDir[newx][newy] = 0;
6508 if (!pushed_by_player)
6510 int nextx = newx + dx, nexty = newy + dy;
6511 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6513 WasJustMoving[newx][newy] = 3;
6515 if (CAN_FALL(element) && direction == MV_DOWN)
6516 WasJustFalling[newx][newy] = 3;
6518 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6519 CheckCollision[newx][newy] = 2;
6522 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6524 TestIfBadThingTouchesHero(newx, newy);
6525 TestIfBadThingTouchesFriend(newx, newy);
6527 if (!IS_CUSTOM_ELEMENT(element))
6528 TestIfBadThingTouchesOtherBadThing(newx, newy);
6530 else if (element == EL_PENGUIN)
6531 TestIfFriendTouchesBadThing(newx, newy);
6533 #if USE_NEW_MOVE_STYLE
6535 if (CAN_FALL(element) && direction == MV_DOWN &&
6536 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6537 IS_PLAYER(x, newy + 1))
6538 printf("::: we would now kill the player [%d]\n", FrameCounter);
6541 /* give the player one last chance (one more frame) to move away */
6542 if (CAN_FALL(element) && direction == MV_DOWN &&
6543 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6544 (!IS_PLAYER(x, newy + 1) ||
6545 game.engine_version < VERSION_IDENT(3,1,1,0)))
6548 if (CAN_FALL(element) && direction == MV_DOWN &&
6549 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6557 if (pushed_by_player && !game.use_bug_change_when_pushing)
6559 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6562 if (pushed_by_player)
6567 int dig_side = MV_DIR_OPPOSITE(direction);
6569 static int trigger_sides[4] =
6571 CH_SIDE_RIGHT, /* moving left */
6572 CH_SIDE_LEFT, /* moving right */
6573 CH_SIDE_BOTTOM, /* moving up */
6574 CH_SIDE_TOP, /* moving down */
6576 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6578 struct PlayerInfo *player = PLAYERINFO(x, y);
6580 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6581 player->index_bit, dig_side);
6582 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6583 player->index_bit, dig_side);
6588 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6592 if (ChangePage[newx][newy] != -1) /* delayed change */
6593 ChangeElement(newx, newy, ChangePage[newx][newy]);
6598 TestIfElementHitsCustomElement(newx, newy, direction);
6602 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6604 int hitting_element = Feld[newx][newy];
6606 /* !!! fix side (direction) orientation here and elsewhere !!! */
6607 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6611 if (IN_LEV_FIELD(nextx, nexty))
6613 int opposite_direction = MV_DIR_OPPOSITE(direction);
6614 int hitting_side = direction;
6615 int touched_side = opposite_direction;
6616 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6617 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6618 MovDir[nextx][nexty] != direction ||
6619 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6625 CheckElementChangeBySide(nextx, nexty, touched_element,
6626 CE_HIT_BY_SOMETHING, opposite_direction);
6628 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6629 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6631 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6633 struct ElementChangeInfo *change =
6634 &element_info[hitting_element].change_page[i];
6636 if (change->can_change &&
6637 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6638 change->trigger_side & touched_side &&
6639 change->trigger_element == touched_element)
6641 CheckElementChangeByPage(newx, newy, hitting_element,
6642 touched_element, CE_OTHER_IS_HITTING,i);
6648 if (IS_CUSTOM_ELEMENT(touched_element) &&
6649 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6651 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6653 struct ElementChangeInfo *change =
6654 &element_info[touched_element].change_page[i];
6656 if (change->can_change &&
6657 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6658 change->trigger_side & hitting_side &&
6659 change->trigger_element == hitting_element)
6661 CheckElementChangeByPage(nextx, nexty, touched_element,
6662 hitting_element, CE_OTHER_GETS_HIT, i);
6673 TestIfPlayerTouchesCustomElement(newx, newy);
6674 TestIfElementTouchesCustomElement(newx, newy);
6677 int AmoebeNachbarNr(int ax, int ay)
6680 int element = Feld[ax][ay];
6682 static int xy[4][2] =
6690 for (i = 0; i < NUM_DIRECTIONS; i++)
6692 int x = ax + xy[i][0];
6693 int y = ay + xy[i][1];
6695 if (!IN_LEV_FIELD(x, y))
6698 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6699 group_nr = AmoebaNr[x][y];
6705 void AmoebenVereinigen(int ax, int ay)
6707 int i, x, y, xx, yy;
6708 int new_group_nr = AmoebaNr[ax][ay];
6709 static int xy[4][2] =
6717 if (new_group_nr == 0)
6720 for (i = 0; i < NUM_DIRECTIONS; i++)
6725 if (!IN_LEV_FIELD(x, y))
6728 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6729 Feld[x][y] == EL_BD_AMOEBA ||
6730 Feld[x][y] == EL_AMOEBA_DEAD) &&
6731 AmoebaNr[x][y] != new_group_nr)
6733 int old_group_nr = AmoebaNr[x][y];
6735 if (old_group_nr == 0)
6738 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6739 AmoebaCnt[old_group_nr] = 0;
6740 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6741 AmoebaCnt2[old_group_nr] = 0;
6743 for (yy = 0; yy < lev_fieldy; yy++)
6745 for (xx = 0; xx < lev_fieldx; xx++)
6747 if (AmoebaNr[xx][yy] == old_group_nr)
6748 AmoebaNr[xx][yy] = new_group_nr;
6755 void AmoebeUmwandeln(int ax, int ay)
6759 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6761 int group_nr = AmoebaNr[ax][ay];
6766 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6767 printf("AmoebeUmwandeln(): This should never happen!\n");
6772 for (y = 0; y < lev_fieldy; y++)
6774 for (x = 0; x < lev_fieldx; x++)
6776 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6779 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6783 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6784 SND_AMOEBA_TURNING_TO_GEM :
6785 SND_AMOEBA_TURNING_TO_ROCK));
6790 static int xy[4][2] =
6798 for (i = 0; i < NUM_DIRECTIONS; i++)
6803 if (!IN_LEV_FIELD(x, y))
6806 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6808 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6809 SND_AMOEBA_TURNING_TO_GEM :
6810 SND_AMOEBA_TURNING_TO_ROCK));
6817 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6820 int group_nr = AmoebaNr[ax][ay];
6821 boolean done = FALSE;
6826 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6827 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6832 for (y = 0; y < lev_fieldy; y++)
6834 for (x = 0; x < lev_fieldx; x++)
6836 if (AmoebaNr[x][y] == group_nr &&
6837 (Feld[x][y] == EL_AMOEBA_DEAD ||
6838 Feld[x][y] == EL_BD_AMOEBA ||
6839 Feld[x][y] == EL_AMOEBA_GROWING))
6842 Feld[x][y] = new_element;
6843 InitField(x, y, FALSE);
6844 DrawLevelField(x, y);
6851 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6852 SND_BD_AMOEBA_TURNING_TO_ROCK :
6853 SND_BD_AMOEBA_TURNING_TO_GEM));
6856 void AmoebeWaechst(int x, int y)
6858 static unsigned long sound_delay = 0;
6859 static unsigned long sound_delay_value = 0;
6861 if (!MovDelay[x][y]) /* start new growing cycle */
6865 if (DelayReached(&sound_delay, sound_delay_value))
6868 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6870 if (Store[x][y] == EL_BD_AMOEBA)
6871 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6873 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6875 sound_delay_value = 30;
6879 if (MovDelay[x][y]) /* wait some time before growing bigger */
6882 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6884 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6885 6 - MovDelay[x][y]);
6887 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6890 if (!MovDelay[x][y])
6892 Feld[x][y] = Store[x][y];
6894 DrawLevelField(x, y);
6899 void AmoebaDisappearing(int x, int y)
6901 static unsigned long sound_delay = 0;
6902 static unsigned long sound_delay_value = 0;
6904 if (!MovDelay[x][y]) /* start new shrinking cycle */
6908 if (DelayReached(&sound_delay, sound_delay_value))
6909 sound_delay_value = 30;
6912 if (MovDelay[x][y]) /* wait some time before shrinking */
6915 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6917 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6918 6 - MovDelay[x][y]);
6920 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6923 if (!MovDelay[x][y])
6925 Feld[x][y] = EL_EMPTY;
6926 DrawLevelField(x, y);
6928 /* don't let mole enter this field in this cycle;
6929 (give priority to objects falling to this field from above) */
6935 void AmoebeAbleger(int ax, int ay)
6938 int element = Feld[ax][ay];
6939 int graphic = el2img(element);
6940 int newax = ax, neway = ay;
6941 static int xy[4][2] =
6949 if (!level.amoeba_speed)
6951 Feld[ax][ay] = EL_AMOEBA_DEAD;
6952 DrawLevelField(ax, ay);
6956 if (IS_ANIMATED(graphic))
6957 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6959 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6960 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6962 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6965 if (MovDelay[ax][ay])
6969 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6972 int x = ax + xy[start][0];
6973 int y = ay + xy[start][1];
6975 if (!IN_LEV_FIELD(x, y))
6979 if (IS_FREE(x, y) ||
6980 CAN_GROW_INTO(Feld[x][y]) ||
6981 Feld[x][y] == EL_QUICKSAND_EMPTY)
6987 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6988 if (IS_FREE(x, y) ||
6989 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6996 if (newax == ax && neway == ay)
6999 else /* normal or "filled" (BD style) amoeba */
7002 boolean waiting_for_player = FALSE;
7004 for (i = 0; i < NUM_DIRECTIONS; i++)
7006 int j = (start + i) % 4;
7007 int x = ax + xy[j][0];
7008 int y = ay + xy[j][1];
7010 if (!IN_LEV_FIELD(x, y))
7014 if (IS_FREE(x, y) ||
7015 CAN_GROW_INTO(Feld[x][y]) ||
7016 Feld[x][y] == EL_QUICKSAND_EMPTY)
7023 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7024 if (IS_FREE(x, y) ||
7025 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7032 else if (IS_PLAYER(x, y))
7033 waiting_for_player = TRUE;
7036 if (newax == ax && neway == ay) /* amoeba cannot grow */
7039 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7041 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7044 Feld[ax][ay] = EL_AMOEBA_DEAD;
7045 DrawLevelField(ax, ay);
7046 AmoebaCnt[AmoebaNr[ax][ay]]--;
7048 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7050 if (element == EL_AMOEBA_FULL)
7051 AmoebeUmwandeln(ax, ay);
7052 else if (element == EL_BD_AMOEBA)
7053 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7058 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7060 /* amoeba gets larger by growing in some direction */
7062 int new_group_nr = AmoebaNr[ax][ay];
7065 if (new_group_nr == 0)
7067 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7068 printf("AmoebeAbleger(): This should never happen!\n");
7073 AmoebaNr[newax][neway] = new_group_nr;
7074 AmoebaCnt[new_group_nr]++;
7075 AmoebaCnt2[new_group_nr]++;
7077 /* if amoeba touches other amoeba(s) after growing, unify them */
7078 AmoebenVereinigen(newax, neway);
7080 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7082 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7088 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7089 (neway == lev_fieldy - 1 && newax != ax))
7091 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7092 Store[newax][neway] = element;
7094 else if (neway == ay)
7096 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7098 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7100 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7105 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7106 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7107 Store[ax][ay] = EL_AMOEBA_DROP;
7108 ContinueMoving(ax, ay);
7112 DrawLevelField(newax, neway);
7115 void Life(int ax, int ay)
7118 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7120 int element = Feld[ax][ay];
7121 int graphic = el2img(element);
7122 boolean changed = FALSE;
7124 if (IS_ANIMATED(graphic))
7125 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7130 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7131 MovDelay[ax][ay] = life_time;
7133 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7136 if (MovDelay[ax][ay])
7140 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7142 int xx = ax+x1, yy = ay+y1;
7145 if (!IN_LEV_FIELD(xx, yy))
7148 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7150 int x = xx+x2, y = yy+y2;
7152 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7155 if (((Feld[x][y] == element ||
7156 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7158 (IS_FREE(x, y) && Stop[x][y]))
7162 if (xx == ax && yy == ay) /* field in the middle */
7164 if (nachbarn < life[0] || nachbarn > life[1])
7166 Feld[xx][yy] = EL_EMPTY;
7168 DrawLevelField(xx, yy);
7169 Stop[xx][yy] = TRUE;
7174 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7175 { /* free border field */
7176 if (nachbarn >= life[2] && nachbarn <= life[3])
7178 Feld[xx][yy] = element;
7179 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7181 DrawLevelField(xx, yy);
7182 Stop[xx][yy] = TRUE;
7187 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7188 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7189 { /* free border field */
7190 if (nachbarn >= life[2] && nachbarn <= life[3])
7192 Feld[xx][yy] = element;
7193 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7195 DrawLevelField(xx, yy);
7196 Stop[xx][yy] = TRUE;
7204 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7205 SND_GAME_OF_LIFE_GROWING);
7208 static void InitRobotWheel(int x, int y)
7210 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7213 static void RunRobotWheel(int x, int y)
7215 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7218 static void StopRobotWheel(int x, int y)
7220 if (ZX == x && ZY == y)
7224 static void InitTimegateWheel(int x, int y)
7227 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7229 /* another brainless, "type style" bug ... :-( */
7230 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7234 static void RunTimegateWheel(int x, int y)
7236 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7239 void CheckExit(int x, int y)
7241 if (local_player->gems_still_needed > 0 ||
7242 local_player->sokobanfields_still_needed > 0 ||
7243 local_player->lights_still_needed > 0)
7245 int element = Feld[x][y];
7246 int graphic = el2img(element);
7248 if (IS_ANIMATED(graphic))
7249 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7254 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7257 Feld[x][y] = EL_EXIT_OPENING;
7259 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7262 void CheckExitSP(int x, int y)
7264 if (local_player->gems_still_needed > 0)
7266 int element = Feld[x][y];
7267 int graphic = el2img(element);
7269 if (IS_ANIMATED(graphic))
7270 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7275 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7278 Feld[x][y] = EL_SP_EXIT_OPENING;
7280 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7283 static void CloseAllOpenTimegates()
7287 for (y = 0; y < lev_fieldy; y++)
7289 for (x = 0; x < lev_fieldx; x++)
7291 int element = Feld[x][y];
7293 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7295 Feld[x][y] = EL_TIMEGATE_CLOSING;
7297 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7299 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7306 void EdelsteinFunkeln(int x, int y)
7308 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7311 if (Feld[x][y] == EL_BD_DIAMOND)
7314 if (MovDelay[x][y] == 0) /* next animation frame */
7315 MovDelay[x][y] = 11 * !SimpleRND(500);
7317 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7321 if (setup.direct_draw && MovDelay[x][y])
7322 SetDrawtoField(DRAW_BUFFERED);
7324 DrawLevelElementAnimation(x, y, Feld[x][y]);
7326 if (MovDelay[x][y] != 0)
7328 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7329 10 - MovDelay[x][y]);
7331 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7333 if (setup.direct_draw)
7337 dest_x = FX + SCREENX(x) * TILEX;
7338 dest_y = FY + SCREENY(y) * TILEY;
7340 BlitBitmap(drawto_field, window,
7341 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7342 SetDrawtoField(DRAW_DIRECT);
7348 void MauerWaechst(int x, int y)
7352 if (!MovDelay[x][y]) /* next animation frame */
7353 MovDelay[x][y] = 3 * delay;
7355 if (MovDelay[x][y]) /* wait some time before next frame */
7359 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7361 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7362 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7364 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7367 if (!MovDelay[x][y])
7369 if (MovDir[x][y] == MV_LEFT)
7371 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7372 DrawLevelField(x - 1, y);
7374 else if (MovDir[x][y] == MV_RIGHT)
7376 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7377 DrawLevelField(x + 1, y);
7379 else if (MovDir[x][y] == MV_UP)
7381 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7382 DrawLevelField(x, y - 1);
7386 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7387 DrawLevelField(x, y + 1);
7390 Feld[x][y] = Store[x][y];
7392 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7393 DrawLevelField(x, y);
7398 void MauerAbleger(int ax, int ay)
7400 int element = Feld[ax][ay];
7401 int graphic = el2img(element);
7402 boolean oben_frei = FALSE, unten_frei = FALSE;
7403 boolean links_frei = FALSE, rechts_frei = FALSE;
7404 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7405 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7406 boolean new_wall = FALSE;
7408 if (IS_ANIMATED(graphic))
7409 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7411 if (!MovDelay[ax][ay]) /* start building new wall */
7412 MovDelay[ax][ay] = 6;
7414 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7417 if (MovDelay[ax][ay])
7421 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7423 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7425 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7427 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7430 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7431 element == EL_EXPANDABLE_WALL_ANY)
7435 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7436 Store[ax][ay-1] = element;
7437 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7438 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7439 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7440 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7445 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7446 Store[ax][ay+1] = element;
7447 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7448 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7449 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7450 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7455 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7456 element == EL_EXPANDABLE_WALL_ANY ||
7457 element == EL_EXPANDABLE_WALL)
7461 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7462 Store[ax-1][ay] = element;
7463 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7464 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7465 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7466 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7472 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7473 Store[ax+1][ay] = element;
7474 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7475 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7476 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7477 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7482 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7483 DrawLevelField(ax, ay);
7485 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7487 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7488 unten_massiv = TRUE;
7489 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7490 links_massiv = TRUE;
7491 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7492 rechts_massiv = TRUE;
7494 if (((oben_massiv && unten_massiv) ||
7495 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7496 element == EL_EXPANDABLE_WALL) &&
7497 ((links_massiv && rechts_massiv) ||
7498 element == EL_EXPANDABLE_WALL_VERTICAL))
7499 Feld[ax][ay] = EL_WALL;
7503 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7505 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7509 void CheckForDragon(int x, int y)
7512 boolean dragon_found = FALSE;
7513 static int xy[4][2] =
7521 for (i = 0; i < NUM_DIRECTIONS; i++)
7523 for (j = 0; j < 4; j++)
7525 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7527 if (IN_LEV_FIELD(xx, yy) &&
7528 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7530 if (Feld[xx][yy] == EL_DRAGON)
7531 dragon_found = TRUE;
7540 for (i = 0; i < NUM_DIRECTIONS; i++)
7542 for (j = 0; j < 3; j++)
7544 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7546 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7548 Feld[xx][yy] = EL_EMPTY;
7549 DrawLevelField(xx, yy);
7558 static void InitBuggyBase(int x, int y)
7560 int element = Feld[x][y];
7561 int activating_delay = FRAMES_PER_SECOND / 4;
7564 (element == EL_SP_BUGGY_BASE ?
7565 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7566 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7568 element == EL_SP_BUGGY_BASE_ACTIVE ?
7569 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7572 static void WarnBuggyBase(int x, int y)
7575 static int xy[4][2] =
7583 for (i = 0; i < NUM_DIRECTIONS; i++)
7585 int xx = x + xy[i][0], yy = y + xy[i][1];
7587 if (IS_PLAYER(xx, yy))
7589 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7596 static void InitTrap(int x, int y)
7598 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7601 static void ActivateTrap(int x, int y)
7603 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7606 static void ChangeActiveTrap(int x, int y)
7608 int graphic = IMG_TRAP_ACTIVE;
7610 /* if new animation frame was drawn, correct crumbled sand border */
7611 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7612 DrawLevelFieldCrumbledSand(x, y);
7615 static void ChangeElementNowExt(int x, int y, int target_element)
7617 int previous_move_direction = MovDir[x][y];
7619 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7620 IS_WALKABLE(Feld[x][y]));
7622 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7623 IS_WALKABLE(Feld[x][y]) &&
7627 /* check if element under player changes from accessible to unaccessible
7628 (needed for special case of dropping element which then changes) */
7629 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7630 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7633 printf("::: BOOOM! [%d, '%s']\n", target_element,
7634 element_info[target_element].token_name);
7646 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7647 RemoveMovingField(x, y);
7651 Feld[x][y] = target_element;
7654 Feld[x][y] = target_element;
7657 ResetGfxAnimation(x, y);
7658 ResetRandomAnimationValue(x, y);
7660 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7661 MovDir[x][y] = previous_move_direction;
7664 InitField_WithBug1(x, y, FALSE);
7666 InitField(x, y, FALSE);
7667 if (CAN_MOVE(Feld[x][y]))
7671 DrawLevelField(x, y);
7673 if (GFX_CRUMBLED(Feld[x][y]))
7674 DrawLevelFieldCrumbledSandNeighbours(x, y);
7678 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7682 TestIfBadThingTouchesHero(x, y);
7683 TestIfPlayerTouchesCustomElement(x, y);
7684 TestIfElementTouchesCustomElement(x, y);
7687 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7688 if (ELEM_IS_PLAYER(target_element))
7689 RelocatePlayer(x, y, target_element);
7692 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7696 TestIfBadThingTouchesHero(x, y);
7697 TestIfPlayerTouchesCustomElement(x, y);
7698 TestIfElementTouchesCustomElement(x, y);
7702 static boolean ChangeElementNow(int x, int y, int element, int page)
7704 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7706 int old_element = Feld[x][y];
7708 /* always use default change event to prevent running into a loop */
7709 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7710 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7712 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7714 /* reset actual trigger element and player */
7715 change->actual_trigger_element = EL_EMPTY;
7716 change->actual_trigger_player = EL_PLAYER_1;
7719 /* do not change already changed elements with same change event */
7721 if (Changed[x][y] & ChangeEvent[x][y])
7728 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7731 /* !!! indirect change before direct change !!! */
7732 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7735 if (change->explode)
7742 if (change->use_target_content)
7744 boolean complete_replace = TRUE;
7745 boolean can_replace[3][3];
7748 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7751 boolean is_walkable;
7752 boolean is_diggable;
7753 boolean is_collectible;
7754 boolean is_removable;
7755 boolean is_destructible;
7756 int ex = x + xx - 1;
7757 int ey = y + yy - 1;
7758 int content_element = change->target_content[xx][yy];
7761 can_replace[xx][yy] = TRUE;
7763 if (ex == x && ey == y) /* do not check changing element itself */
7766 if (content_element == EL_EMPTY_SPACE)
7768 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7773 if (!IN_LEV_FIELD(ex, ey))
7775 can_replace[xx][yy] = FALSE;
7776 complete_replace = FALSE;
7782 if (Changed[ex][ey]) /* do not change already changed elements */
7784 can_replace[xx][yy] = FALSE;
7785 complete_replace = FALSE;
7793 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7794 e = MovingOrBlocked2Element(ex, ey);
7799 is_empty = (IS_FREE(ex, ey) ||
7800 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7801 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7802 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7806 is_empty = (IS_FREE(ex, ey) ||
7807 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7809 is_empty = (IS_FREE(ex, ey) ||
7810 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7815 is_walkable = (is_empty || IS_WALKABLE(e));
7816 is_diggable = (is_empty || IS_DIGGABLE(e));
7817 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7818 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7819 is_removable = (is_diggable || is_collectible);
7821 can_replace[xx][yy] =
7822 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7823 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7824 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7825 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7826 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7827 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7828 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7830 if (!can_replace[xx][yy])
7831 complete_replace = FALSE;
7833 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7834 IS_WALKABLE(content_element)));
7836 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7838 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7841 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7842 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7843 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7845 can_replace[xx][yy] = FALSE;
7846 complete_replace = FALSE;
7851 if (!change->only_if_complete || complete_replace)
7853 boolean something_has_changed = FALSE;
7855 if (change->only_if_complete && change->use_random_replace &&
7856 RND(100) < change->random_percentage)
7859 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7861 int ex = x + xx - 1;
7862 int ey = y + yy - 1;
7863 int content_element;
7865 if (can_replace[xx][yy] && (!change->use_random_replace ||
7866 RND(100) < change->random_percentage))
7868 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7869 RemoveMovingField(ex, ey);
7871 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7873 content_element = change->target_content[xx][yy];
7874 target_element = GET_TARGET_ELEMENT(content_element, change);
7876 ChangeElementNowExt(ex, ey, target_element);
7878 something_has_changed = TRUE;
7880 /* for symmetry reasons, freeze newly created border elements */
7881 if (ex != x || ey != y)
7882 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7886 if (something_has_changed)
7887 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7892 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7894 ChangeElementNowExt(x, y, target_element);
7896 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7900 /* this uses direct change before indirect change */
7901 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7907 static void ChangeElement(int x, int y, int page)
7909 int element = MovingOrBlocked2Element(x, y);
7910 struct ElementInfo *ei = &element_info[element];
7911 struct ElementChangeInfo *change = &ei->change_page[page];
7914 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7917 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7918 x, y, element, element_info[element].token_name);
7919 printf("ChangeElement(): This should never happen!\n");
7924 /* this can happen with classic bombs on walkable, changing elements */
7925 if (!CAN_CHANGE(element))
7928 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7929 ChangeDelay[x][y] = 0;
7935 if (ChangeDelay[x][y] == 0) /* initialize element change */
7937 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7938 RND(change->delay_random * change->delay_frames)) + 1;
7940 ResetGfxAnimation(x, y);
7941 ResetRandomAnimationValue(x, y);
7943 if (change->pre_change_function)
7944 change->pre_change_function(x, y);
7947 ChangeDelay[x][y]--;
7949 if (ChangeDelay[x][y] != 0) /* continue element change */
7951 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7953 if (IS_ANIMATED(graphic))
7954 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7956 if (change->change_function)
7957 change->change_function(x, y);
7959 else /* finish element change */
7961 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7963 page = ChangePage[x][y];
7964 ChangePage[x][y] = -1;
7966 change = &ei->change_page[page];
7970 if (IS_MOVING(x, y) && !change->explode)
7972 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7975 ChangeDelay[x][y] = 1; /* try change after next move step */
7976 ChangePage[x][y] = page; /* remember page to use for change */
7981 if (ChangeElementNow(x, y, element, page))
7983 if (change->post_change_function)
7984 change->post_change_function(x, y);
7989 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7990 int trigger_element,
7997 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7999 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8002 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8004 int element = EL_CUSTOM_START + i;
8006 boolean change_element = FALSE;
8009 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8012 for (j = 0; j < element_info[element].num_change_pages; j++)
8014 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8016 if (change->can_change &&
8017 change->events & CH_EVENT_BIT(trigger_event) &&
8018 change->trigger_side & trigger_side &&
8019 change->trigger_player & trigger_player &&
8020 change->trigger_page & trigger_page_bits &&
8021 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8024 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8025 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8026 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8029 change_element = TRUE;
8032 change->actual_trigger_element = trigger_element;
8033 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8039 if (!change_element)
8042 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8045 if (x == lx && y == ly) /* do not change trigger element itself */
8049 if (Feld[x][y] == element)
8051 ChangeDelay[x][y] = 1;
8052 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8053 ChangeElement(x, y, page);
8061 static boolean CheckElementChangeExt(int x, int y,
8063 int trigger_element,
8069 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8072 if (Feld[x][y] == EL_BLOCKED)
8074 Blocked2Moving(x, y, &x, &y);
8075 element = Feld[x][y];
8079 if (Feld[x][y] != element) /* check if element has already changed */
8082 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8083 Feld[x][y], element_info[Feld[x][y]].token_name,
8084 element, element_info[element].token_name,
8093 if (trigger_page < 0)
8095 boolean change_element = FALSE;
8098 for (i = 0; i < element_info[element].num_change_pages; i++)
8100 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8102 if (change->can_change &&
8103 change->events & CH_EVENT_BIT(trigger_event) &&
8104 change->trigger_side & trigger_side &&
8105 change->trigger_player & trigger_player)
8107 change_element = TRUE;
8110 change->actual_trigger_element = trigger_element;
8111 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8117 if (!change_element)
8122 struct ElementInfo *ei = &element_info[element];
8123 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8125 change->actual_trigger_element = trigger_element;
8126 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8131 /* !!! this check misses pages with same event, but different side !!! */
8133 if (trigger_page < 0)
8134 trigger_page = element_info[element].event_page_nr[trigger_event];
8136 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8140 ChangeDelay[x][y] = 1;
8141 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8142 ChangeElement(x, y, trigger_page);
8147 static void PlayPlayerSound(struct PlayerInfo *player)
8149 int jx = player->jx, jy = player->jy;
8150 int element = player->element_nr;
8151 int last_action = player->last_action_waiting;
8152 int action = player->action_waiting;
8154 if (player->is_waiting)
8156 if (action != last_action)
8157 PlayLevelSoundElementAction(jx, jy, element, action);
8159 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8163 if (action != last_action)
8164 StopSound(element_info[element].sound[last_action]);
8166 if (last_action == ACTION_SLEEPING)
8167 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8171 static void PlayAllPlayersSound()
8175 for (i = 0; i < MAX_PLAYERS; i++)
8176 if (stored_player[i].active)
8177 PlayPlayerSound(&stored_player[i]);
8180 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8182 boolean last_waiting = player->is_waiting;
8183 int move_dir = player->MovDir;
8185 player->last_action_waiting = player->action_waiting;
8189 if (!last_waiting) /* not waiting -> waiting */
8191 player->is_waiting = TRUE;
8193 player->frame_counter_bored =
8195 game.player_boring_delay_fixed +
8196 SimpleRND(game.player_boring_delay_random);
8197 player->frame_counter_sleeping =
8199 game.player_sleeping_delay_fixed +
8200 SimpleRND(game.player_sleeping_delay_random);
8202 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8205 if (game.player_sleeping_delay_fixed +
8206 game.player_sleeping_delay_random > 0 &&
8207 player->anim_delay_counter == 0 &&
8208 player->post_delay_counter == 0 &&
8209 FrameCounter >= player->frame_counter_sleeping)
8210 player->is_sleeping = TRUE;
8211 else if (game.player_boring_delay_fixed +
8212 game.player_boring_delay_random > 0 &&
8213 FrameCounter >= player->frame_counter_bored)
8214 player->is_bored = TRUE;
8216 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8217 player->is_bored ? ACTION_BORING :
8220 if (player->is_sleeping)
8222 if (player->num_special_action_sleeping > 0)
8224 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8226 int last_special_action = player->special_action_sleeping;
8227 int num_special_action = player->num_special_action_sleeping;
8228 int special_action =
8229 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8230 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8231 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8232 last_special_action + 1 : ACTION_SLEEPING);
8233 int special_graphic =
8234 el_act_dir2img(player->element_nr, special_action, move_dir);
8236 player->anim_delay_counter =
8237 graphic_info[special_graphic].anim_delay_fixed +
8238 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8239 player->post_delay_counter =
8240 graphic_info[special_graphic].post_delay_fixed +
8241 SimpleRND(graphic_info[special_graphic].post_delay_random);
8243 player->special_action_sleeping = special_action;
8246 if (player->anim_delay_counter > 0)
8248 player->action_waiting = player->special_action_sleeping;
8249 player->anim_delay_counter--;
8251 else if (player->post_delay_counter > 0)
8253 player->post_delay_counter--;
8257 else if (player->is_bored)
8259 if (player->num_special_action_bored > 0)
8261 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8263 int special_action =
8264 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8265 int special_graphic =
8266 el_act_dir2img(player->element_nr, special_action, move_dir);
8268 player->anim_delay_counter =
8269 graphic_info[special_graphic].anim_delay_fixed +
8270 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8271 player->post_delay_counter =
8272 graphic_info[special_graphic].post_delay_fixed +
8273 SimpleRND(graphic_info[special_graphic].post_delay_random);
8275 player->special_action_bored = special_action;
8278 if (player->anim_delay_counter > 0)
8280 player->action_waiting = player->special_action_bored;
8281 player->anim_delay_counter--;
8283 else if (player->post_delay_counter > 0)
8285 player->post_delay_counter--;
8290 else if (last_waiting) /* waiting -> not waiting */
8292 player->is_waiting = FALSE;
8293 player->is_bored = FALSE;
8294 player->is_sleeping = FALSE;
8296 player->frame_counter_bored = -1;
8297 player->frame_counter_sleeping = -1;
8299 player->anim_delay_counter = 0;
8300 player->post_delay_counter = 0;
8302 player->action_waiting = ACTION_DEFAULT;
8304 player->special_action_bored = ACTION_DEFAULT;
8305 player->special_action_sleeping = ACTION_DEFAULT;
8310 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8313 static byte stored_player_action[MAX_PLAYERS];
8314 static int num_stored_actions = 0;
8316 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8317 int left = player_action & JOY_LEFT;
8318 int right = player_action & JOY_RIGHT;
8319 int up = player_action & JOY_UP;
8320 int down = player_action & JOY_DOWN;
8321 int button1 = player_action & JOY_BUTTON_1;
8322 int button2 = player_action & JOY_BUTTON_2;
8323 int dx = (left ? -1 : right ? 1 : 0);
8324 int dy = (up ? -1 : down ? 1 : 0);
8327 stored_player_action[player->index_nr] = 0;
8328 num_stored_actions++;
8332 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8335 if (!player->active || tape.pausing)
8339 printf("::: [%d %d %d %d] [%d %d]\n",
8340 left, right, up, down, button1, button2);
8346 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8351 if (player->MovPos == 0)
8352 CheckGravityMovement(player);
8355 snapped = SnapField(player, dx, dy);
8359 dropped = DropElement(player);
8361 moved = MovePlayer(player, dx, dy);
8364 if (tape.single_step && tape.recording && !tape.pausing)
8366 if (button1 || (dropped && !moved))
8368 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8369 SnapField(player, 0, 0); /* stop snapping */
8373 SetPlayerWaiting(player, FALSE);
8376 return player_action;
8378 stored_player_action[player->index_nr] = player_action;
8384 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8387 /* no actions for this player (no input at player's configured device) */
8389 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8390 SnapField(player, 0, 0);
8391 CheckGravityMovementWhenNotMoving(player);
8393 if (player->MovPos == 0)
8394 SetPlayerWaiting(player, TRUE);
8396 if (player->MovPos == 0) /* needed for tape.playing */
8397 player->is_moving = FALSE;
8399 player->is_dropping = FALSE;
8405 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8407 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8409 TapeRecordAction(stored_player_action);
8410 num_stored_actions = 0;
8417 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8419 static byte stored_player_action[MAX_PLAYERS];
8420 static int num_stored_actions = 0;
8421 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8422 int left = player_action & JOY_LEFT;
8423 int right = player_action & JOY_RIGHT;
8424 int up = player_action & JOY_UP;
8425 int down = player_action & JOY_DOWN;
8426 int button1 = player_action & JOY_BUTTON_1;
8427 int button2 = player_action & JOY_BUTTON_2;
8428 int dx = (left ? -1 : right ? 1 : 0);
8429 int dy = (up ? -1 : down ? 1 : 0);
8431 stored_player_action[player->index_nr] = 0;
8432 num_stored_actions++;
8434 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8436 if (!player->active || tape.pausing)
8441 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8444 snapped = SnapField(player, dx, dy);
8448 dropped = DropElement(player);
8450 moved = MovePlayer(player, dx, dy);
8453 if (tape.single_step && tape.recording && !tape.pausing)
8455 if (button1 || (dropped && !moved))
8457 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8458 SnapField(player, 0, 0); /* stop snapping */
8462 stored_player_action[player->index_nr] = player_action;
8466 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8468 /* no actions for this player (no input at player's configured device) */
8470 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8471 SnapField(player, 0, 0);
8472 CheckGravityMovementWhenNotMoving(player);
8474 if (player->MovPos == 0)
8475 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8477 if (player->MovPos == 0) /* needed for tape.playing */
8478 player->is_moving = FALSE;
8481 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8483 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8485 TapeRecordAction(stored_player_action);
8486 num_stored_actions = 0;
8491 void AdvanceFrameAndPlayerCounters(int player_nr)
8495 /* advance frame counters (global frame counter and time frame counter) */
8499 /* advance player counters (counters for move delay, move animation etc.) */
8500 for (i = 0; i < MAX_PLAYERS; i++)
8502 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8504 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8506 if (!advance_player_counters) /* not all players may be affected */
8509 stored_player[i].Frame += move_frames;
8511 if (stored_player[i].MovPos != 0)
8512 stored_player[i].StepFrame += move_frames;
8514 #if USE_NEW_MOVE_DELAY
8515 if (stored_player[i].move_delay > 0)
8516 stored_player[i].move_delay--;
8519 #if USE_NEW_PUSH_DELAY
8520 /* due to bugs in previous versions, counter must count up, not down */
8521 if (stored_player[i].push_delay != -1)
8522 stored_player[i].push_delay++;
8525 if (stored_player[i].drop_delay > 0)
8526 stored_player[i].drop_delay--;
8532 static unsigned long game_frame_delay = 0;
8533 unsigned long game_frame_delay_value;
8534 int magic_wall_x = 0, magic_wall_y = 0;
8535 int i, x, y, element, graphic;
8536 byte *recorded_player_action;
8537 byte summarized_player_action = 0;
8539 byte tape_action[MAX_PLAYERS];
8542 if (game_status != GAME_MODE_PLAYING)
8545 game_frame_delay_value =
8546 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8548 if (tape.playing && tape.warp_forward && !tape.pausing)
8549 game_frame_delay_value = 0;
8551 /* ---------- main game synchronization point ---------- */
8553 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8555 if (network_playing && !network_player_action_received)
8559 printf("DEBUG: try to get network player actions in time\n");
8563 #if defined(NETWORK_AVALIABLE)
8564 /* last chance to get network player actions without main loop delay */
8568 if (game_status != GAME_MODE_PLAYING)
8571 if (!network_player_action_received)
8575 printf("DEBUG: failed to get network player actions in time\n");
8586 printf("::: getting new tape action [%d]\n", FrameCounter);
8589 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8592 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8593 if (recorded_player_action == NULL && tape.pausing)
8598 printf("::: %d\n", stored_player[0].action);
8602 if (recorded_player_action != NULL)
8603 for (i = 0; i < MAX_PLAYERS; i++)
8604 stored_player[i].action = recorded_player_action[i];
8607 for (i = 0; i < MAX_PLAYERS; i++)
8609 summarized_player_action |= stored_player[i].action;
8611 if (!network_playing)
8612 stored_player[i].effective_action = stored_player[i].action;
8615 #if defined(NETWORK_AVALIABLE)
8616 if (network_playing)
8617 SendToServer_MovePlayer(summarized_player_action);
8620 if (!options.network && !setup.team_mode)
8621 local_player->effective_action = summarized_player_action;
8624 if (recorded_player_action != NULL)
8625 for (i = 0; i < MAX_PLAYERS; i++)
8626 stored_player[i].effective_action = recorded_player_action[i];
8630 for (i = 0; i < MAX_PLAYERS; i++)
8632 tape_action[i] = stored_player[i].effective_action;
8634 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8635 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8638 /* only save actions from input devices, but not programmed actions */
8640 TapeRecordAction(tape_action);
8643 for (i = 0; i < MAX_PLAYERS; i++)
8645 int actual_player_action = stored_player[i].effective_action;
8648 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8649 - rnd_equinox_tetrachloride 048
8650 - rnd_equinox_tetrachloride_ii 096
8651 - rnd_emanuel_schmieg 002
8652 - doctor_sloan_ww 001, 020
8654 if (stored_player[i].MovPos == 0)
8655 CheckGravityMovement(&stored_player[i]);
8659 /* overwrite programmed action with tape action */
8660 if (stored_player[i].programmed_action)
8661 actual_player_action = stored_player[i].programmed_action;
8665 if (stored_player[i].programmed_action)
8666 printf("::: %d\n", stored_player[i].programmed_action);
8669 if (recorded_player_action)
8672 if (stored_player[i].programmed_action &&
8673 stored_player[i].programmed_action != recorded_player_action[i])
8674 printf("::: %d: %d <-> %d\n", i,
8675 stored_player[i].programmed_action, recorded_player_action[i]);
8679 actual_player_action = recorded_player_action[i];
8684 /* overwrite tape action with programmed action */
8685 if (stored_player[i].programmed_action)
8686 actual_player_action = stored_player[i].programmed_action;
8691 printf("::: action: %d: %x [%d]\n",
8692 stored_player[i].MovPos, actual_player_action, FrameCounter);
8696 PlayerActions(&stored_player[i], actual_player_action);
8698 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8700 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8701 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8704 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8709 TapeRecordAction(tape_action);
8712 network_player_action_received = FALSE;
8714 ScrollScreen(NULL, SCROLL_GO_ON);
8720 for (i = 0; i < MAX_PLAYERS; i++)
8721 stored_player[i].Frame++;
8725 /* for backwards compatibility, the following code emulates a fixed bug that
8726 occured when pushing elements (causing elements that just made their last
8727 pushing step to already (if possible) make their first falling step in the
8728 same game frame, which is bad); this code is also needed to use the famous
8729 "spring push bug" which is used in older levels and might be wanted to be
8730 used also in newer levels, but in this case the buggy pushing code is only
8731 affecting the "spring" element and no other elements */
8734 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8736 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8739 for (i = 0; i < MAX_PLAYERS; i++)
8741 struct PlayerInfo *player = &stored_player[i];
8746 if (player->active && player->is_pushing && player->is_moving &&
8748 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8749 Feld[x][y] == EL_SPRING))
8751 if (player->active && player->is_pushing && player->is_moving &&
8755 ContinueMoving(x, y);
8757 /* continue moving after pushing (this is actually a bug) */
8758 if (!IS_MOVING(x, y))
8767 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8769 Changed[x][y] = CE_BITMASK_DEFAULT;
8770 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8772 #if USE_NEW_BLOCK_STYLE
8773 /* this must be handled before main playfield loop */
8774 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8777 if (MovDelay[x][y] <= 0)
8783 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8785 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8786 printf("GameActions(): This should never happen!\n");
8788 ChangePage[x][y] = -1;
8793 if (WasJustMoving[x][y] > 0)
8794 WasJustMoving[x][y]--;
8795 if (WasJustFalling[x][y] > 0)
8796 WasJustFalling[x][y]--;
8797 if (CheckCollision[x][y] > 0)
8798 CheckCollision[x][y]--;
8803 /* reset finished pushing action (not done in ContinueMoving() to allow
8804 continous pushing animation for elements with zero push delay) */
8805 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8807 ResetGfxAnimation(x, y);
8808 DrawLevelField(x, y);
8813 if (IS_BLOCKED(x, y))
8817 Blocked2Moving(x, y, &oldx, &oldy);
8818 if (!IS_MOVING(oldx, oldy))
8820 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8821 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8822 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8823 printf("GameActions(): This should never happen!\n");
8829 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8831 element = Feld[x][y];
8833 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8835 graphic = el2img(element);
8841 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8843 element = graphic = 0;
8847 if (graphic_info[graphic].anim_global_sync)
8848 GfxFrame[x][y] = FrameCounter;
8850 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8851 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8852 ResetRandomAnimationValue(x, y);
8854 SetRandomAnimationValue(x, y);
8857 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8860 if (IS_INACTIVE(element))
8862 if (IS_ANIMATED(graphic))
8863 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8869 /* this may take place after moving, so 'element' may have changed */
8871 if (IS_CHANGING(x, y))
8873 if (IS_CHANGING(x, y) &&
8874 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8878 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8879 element_info[element].event_page_nr[CE_DELAY]);
8881 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8884 element = Feld[x][y];
8885 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8889 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8894 element = Feld[x][y];
8895 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8897 if (element == EL_MOLE)
8898 printf("::: %d, %d, %d [%d]\n",
8899 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8903 if (element == EL_YAMYAM)
8904 printf("::: %d, %d, %d\n",
8905 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8909 if (IS_ANIMATED(graphic) &&
8913 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8916 if (element == EL_BUG)
8917 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8921 if (element == EL_MOLE)
8922 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8926 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8927 EdelsteinFunkeln(x, y);
8929 else if ((element == EL_ACID ||
8930 element == EL_EXIT_OPEN ||
8931 element == EL_SP_EXIT_OPEN ||
8932 element == EL_SP_TERMINAL ||
8933 element == EL_SP_TERMINAL_ACTIVE ||
8934 element == EL_EXTRA_TIME ||
8935 element == EL_SHIELD_NORMAL ||
8936 element == EL_SHIELD_DEADLY) &&
8937 IS_ANIMATED(graphic))
8938 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8939 else if (IS_MOVING(x, y))
8940 ContinueMoving(x, y);
8941 else if (IS_ACTIVE_BOMB(element))
8942 CheckDynamite(x, y);
8944 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8945 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8947 else if (element == EL_AMOEBA_GROWING)
8948 AmoebeWaechst(x, y);
8949 else if (element == EL_AMOEBA_SHRINKING)
8950 AmoebaDisappearing(x, y);
8952 #if !USE_NEW_AMOEBA_CODE
8953 else if (IS_AMOEBALIVE(element))
8954 AmoebeAbleger(x, y);
8957 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8959 else if (element == EL_EXIT_CLOSED)
8961 else if (element == EL_SP_EXIT_CLOSED)
8963 else if (element == EL_EXPANDABLE_WALL_GROWING)
8965 else if (element == EL_EXPANDABLE_WALL ||
8966 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8967 element == EL_EXPANDABLE_WALL_VERTICAL ||
8968 element == EL_EXPANDABLE_WALL_ANY)
8970 else if (element == EL_FLAMES)
8971 CheckForDragon(x, y);
8973 else if (IS_AUTO_CHANGING(element))
8974 ChangeElement(x, y);
8976 else if (element == EL_EXPLOSION)
8977 ; /* drawing of correct explosion animation is handled separately */
8978 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8979 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8982 /* this may take place after moving, so 'element' may have changed */
8983 if (IS_AUTO_CHANGING(Feld[x][y]))
8984 ChangeElement(x, y);
8987 if (IS_BELT_ACTIVE(element))
8988 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8990 if (game.magic_wall_active)
8992 int jx = local_player->jx, jy = local_player->jy;
8994 /* play the element sound at the position nearest to the player */
8995 if ((element == EL_MAGIC_WALL_FULL ||
8996 element == EL_MAGIC_WALL_ACTIVE ||
8997 element == EL_MAGIC_WALL_EMPTYING ||
8998 element == EL_BD_MAGIC_WALL_FULL ||
8999 element == EL_BD_MAGIC_WALL_ACTIVE ||
9000 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9001 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9009 #if USE_NEW_AMOEBA_CODE
9010 /* new experimental amoeba growth stuff */
9012 if (!(FrameCounter % 8))
9015 static unsigned long random = 1684108901;
9017 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9020 x = (random >> 10) % lev_fieldx;
9021 y = (random >> 20) % lev_fieldy;
9023 x = RND(lev_fieldx);
9024 y = RND(lev_fieldy);
9026 element = Feld[x][y];
9029 if (!IS_PLAYER(x,y) &&
9030 (element == EL_EMPTY ||
9031 CAN_GROW_INTO(element) ||
9032 element == EL_QUICKSAND_EMPTY ||
9033 element == EL_ACID_SPLASH_LEFT ||
9034 element == EL_ACID_SPLASH_RIGHT))
9036 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9037 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9038 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9039 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9040 Feld[x][y] = EL_AMOEBA_DROP;
9043 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9044 if (!IS_PLAYER(x,y) &&
9045 (element == EL_EMPTY ||
9046 element == EL_SAND ||
9047 element == EL_QUICKSAND_EMPTY ||
9048 element == EL_ACID_SPLASH_LEFT ||
9049 element == EL_ACID_SPLASH_RIGHT))
9051 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9052 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9053 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9054 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9055 Feld[x][y] = EL_AMOEBA_DROP;
9059 random = random * 129 + 1;
9065 if (game.explosions_delayed)
9068 game.explosions_delayed = FALSE;
9070 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9072 element = Feld[x][y];
9074 if (ExplodeField[x][y])
9075 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9076 else if (element == EL_EXPLOSION)
9077 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9079 ExplodeField[x][y] = EX_TYPE_NONE;
9082 game.explosions_delayed = TRUE;
9085 if (game.magic_wall_active)
9087 if (!(game.magic_wall_time_left % 4))
9089 int element = Feld[magic_wall_x][magic_wall_y];
9091 if (element == EL_BD_MAGIC_WALL_FULL ||
9092 element == EL_BD_MAGIC_WALL_ACTIVE ||
9093 element == EL_BD_MAGIC_WALL_EMPTYING)
9094 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9096 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9099 if (game.magic_wall_time_left > 0)
9101 game.magic_wall_time_left--;
9102 if (!game.magic_wall_time_left)
9104 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9106 element = Feld[x][y];
9108 if (element == EL_MAGIC_WALL_ACTIVE ||
9109 element == EL_MAGIC_WALL_FULL)
9111 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9112 DrawLevelField(x, y);
9114 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9115 element == EL_BD_MAGIC_WALL_FULL)
9117 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9118 DrawLevelField(x, y);
9122 game.magic_wall_active = FALSE;
9127 if (game.light_time_left > 0)
9129 game.light_time_left--;
9131 if (game.light_time_left == 0)
9132 RedrawAllLightSwitchesAndInvisibleElements();
9135 if (game.timegate_time_left > 0)
9137 game.timegate_time_left--;
9139 if (game.timegate_time_left == 0)
9140 CloseAllOpenTimegates();
9143 for (i = 0; i < MAX_PLAYERS; i++)
9145 struct PlayerInfo *player = &stored_player[i];
9147 if (SHIELD_ON(player))
9149 if (player->shield_deadly_time_left)
9150 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9151 else if (player->shield_normal_time_left)
9152 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9156 if (TimeFrames >= FRAMES_PER_SECOND)
9161 for (i = 0; i < MAX_PLAYERS; i++)
9163 struct PlayerInfo *player = &stored_player[i];
9165 if (SHIELD_ON(player))
9167 player->shield_normal_time_left--;
9169 if (player->shield_deadly_time_left > 0)
9170 player->shield_deadly_time_left--;
9174 if (!level.use_step_counter)
9182 if (TimeLeft <= 10 && setup.time_limit)
9183 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9185 DrawGameValue_Time(TimeLeft);
9187 if (!TimeLeft && setup.time_limit)
9188 for (i = 0; i < MAX_PLAYERS; i++)
9189 KillHero(&stored_player[i]);
9191 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9192 DrawGameValue_Time(TimePlayed);
9195 if (tape.recording || tape.playing)
9196 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9200 PlayAllPlayersSound();
9202 if (options.debug) /* calculate frames per second */
9204 static unsigned long fps_counter = 0;
9205 static int fps_frames = 0;
9206 unsigned long fps_delay_ms = Counter() - fps_counter;
9210 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9212 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9215 fps_counter = Counter();
9218 redraw_mask |= REDRAW_FPS;
9222 if (stored_player[0].jx != stored_player[0].last_jx ||
9223 stored_player[0].jy != stored_player[0].last_jy)
9224 printf("::: %d, %d, %d, %d, %d\n",
9225 stored_player[0].MovDir,
9226 stored_player[0].MovPos,
9227 stored_player[0].GfxPos,
9228 stored_player[0].Frame,
9229 stored_player[0].StepFrame);
9232 #if USE_NEW_MOVE_DELAY
9233 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9238 for (i = 0; i < MAX_PLAYERS; i++)
9241 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9243 stored_player[i].Frame += move_frames;
9245 if (stored_player[i].MovPos != 0)
9246 stored_player[i].StepFrame += move_frames;
9248 #if USE_NEW_MOVE_DELAY
9249 if (stored_player[i].move_delay > 0)
9250 stored_player[i].move_delay--;
9253 if (stored_player[i].drop_delay > 0)
9254 stored_player[i].drop_delay--;
9259 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9261 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9263 local_player->show_envelope = 0;
9267 #if USE_NEW_RANDOMIZE
9268 /* use random number generator in every frame to make it less predictable */
9269 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9274 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9276 int min_x = x, min_y = y, max_x = x, max_y = y;
9279 for (i = 0; i < MAX_PLAYERS; i++)
9281 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9283 if (!stored_player[i].active || &stored_player[i] == player)
9286 min_x = MIN(min_x, jx);
9287 min_y = MIN(min_y, jy);
9288 max_x = MAX(max_x, jx);
9289 max_y = MAX(max_y, jy);
9292 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9295 static boolean AllPlayersInVisibleScreen()
9299 for (i = 0; i < MAX_PLAYERS; i++)
9301 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9303 if (!stored_player[i].active)
9306 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9313 void ScrollLevel(int dx, int dy)
9315 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9318 BlitBitmap(drawto_field, drawto_field,
9319 FX + TILEX * (dx == -1) - softscroll_offset,
9320 FY + TILEY * (dy == -1) - softscroll_offset,
9321 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9322 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9323 FX + TILEX * (dx == 1) - softscroll_offset,
9324 FY + TILEY * (dy == 1) - softscroll_offset);
9328 x = (dx == 1 ? BX1 : BX2);
9329 for (y = BY1; y <= BY2; y++)
9330 DrawScreenField(x, y);
9335 y = (dy == 1 ? BY1 : BY2);
9336 for (x = BX1; x <= BX2; x++)
9337 DrawScreenField(x, y);
9340 redraw_mask |= REDRAW_FIELD;
9344 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9346 int nextx = x + dx, nexty = y + dy;
9347 int element = Feld[x][y];
9350 element != EL_SP_PORT_LEFT &&
9351 element != EL_SP_GRAVITY_PORT_LEFT &&
9352 element != EL_SP_PORT_HORIZONTAL &&
9353 element != EL_SP_PORT_ANY) ||
9355 element != EL_SP_PORT_RIGHT &&
9356 element != EL_SP_GRAVITY_PORT_RIGHT &&
9357 element != EL_SP_PORT_HORIZONTAL &&
9358 element != EL_SP_PORT_ANY) ||
9360 element != EL_SP_PORT_UP &&
9361 element != EL_SP_GRAVITY_PORT_UP &&
9362 element != EL_SP_PORT_VERTICAL &&
9363 element != EL_SP_PORT_ANY) ||
9365 element != EL_SP_PORT_DOWN &&
9366 element != EL_SP_GRAVITY_PORT_DOWN &&
9367 element != EL_SP_PORT_VERTICAL &&
9368 element != EL_SP_PORT_ANY) ||
9369 !IN_LEV_FIELD(nextx, nexty) ||
9370 !IS_FREE(nextx, nexty))
9377 static boolean canFallDown(struct PlayerInfo *player)
9379 int jx = player->jx, jy = player->jy;
9381 return (IN_LEV_FIELD(jx, jy + 1) &&
9382 (IS_FREE(jx, jy + 1) ||
9383 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9384 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9385 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9388 static boolean canPassField(int x, int y, int move_dir)
9390 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9391 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9392 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9395 int element = Feld[x][y];
9397 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9398 !CAN_MOVE(element) &&
9399 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9400 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9401 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9404 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9406 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9407 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9408 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9412 int nextx = newx + dx;
9413 int nexty = newy + dy;
9417 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9418 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9420 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9422 (IS_DIGGABLE(Feld[newx][newy]) ||
9423 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9424 canPassField(newx, newy, move_dir)));
9427 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9428 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9429 (IS_DIGGABLE(Feld[newx][newy]) ||
9430 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9431 canPassField(newx, newy, move_dir)));
9434 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9435 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9436 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9437 canPassField(newx, newy, move_dir)));
9439 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9440 (IS_DIGGABLE(Feld[newx][newy]) ||
9441 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9442 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9443 !CAN_MOVE(Feld[newx][newy]) &&
9444 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9445 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9446 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9452 static void CheckGravityMovement(struct PlayerInfo *player)
9454 if (game.gravity && !player->programmed_action)
9457 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9458 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9460 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9461 int move_dir_vertical = player->action & MV_VERTICAL;
9465 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9467 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9470 int jx = player->jx, jy = player->jy;
9472 boolean player_is_moving_to_valid_field =
9473 (!player_is_snapping &&
9474 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9475 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9479 (player->last_move_dir & MV_HORIZONTAL ?
9480 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9481 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9485 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9486 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9487 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9488 int new_jx = jx + dx, new_jy = jy + dy;
9489 int nextx = new_jx + dx, nexty = new_jy + dy;
9495 boolean player_can_fall_down = canFallDown(player);
9497 boolean player_can_fall_down =
9498 (IN_LEV_FIELD(jx, jy + 1) &&
9499 (IS_FREE(jx, jy + 1) ||
9500 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9504 boolean player_can_fall_down =
9505 (IN_LEV_FIELD(jx, jy + 1) &&
9506 (IS_FREE(jx, jy + 1)));
9510 boolean player_is_moving_to_valid_field =
9513 !player_is_snapping &&
9517 IN_LEV_FIELD(new_jx, new_jy) &&
9518 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9519 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9520 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9521 IN_LEV_FIELD(nextx, nexty) &&
9522 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9524 IN_LEV_FIELD(new_jx, new_jy) &&
9525 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9526 Feld[new_jx][new_jy] == EL_SAND ||
9527 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9528 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9529 /* !!! extend EL_SAND to anything diggable !!! */
9535 boolean player_is_standing_on_valid_field =
9536 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9537 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9541 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9542 player_can_fall_down,
9543 player_is_standing_on_valid_field,
9544 player_is_moving_to_valid_field,
9545 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9546 player->effective_action,
9547 player->can_fall_into_acid);
9550 if (player_can_fall_down &&
9552 !player_is_standing_on_valid_field &&
9554 !player_is_moving_to_valid_field)
9557 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9558 jx, jy, FrameCounter);
9561 player->programmed_action = MV_DOWN;
9566 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9569 return CheckGravityMovement(player);
9572 if (game.gravity && !player->programmed_action)
9574 int jx = player->jx, jy = player->jy;
9575 boolean field_under_player_is_free =
9576 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9577 boolean player_is_standing_on_valid_field =
9578 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9579 (IS_WALKABLE(Feld[jx][jy]) &&
9580 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9582 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9583 player->programmed_action = MV_DOWN;
9589 -----------------------------------------------------------------------------
9590 dx, dy: direction (non-diagonal) to try to move the player to
9591 real_dx, real_dy: direction as read from input device (can be diagonal)
9594 boolean MovePlayerOneStep(struct PlayerInfo *player,
9595 int dx, int dy, int real_dx, int real_dy)
9598 static int trigger_sides[4][2] =
9600 /* enter side leave side */
9601 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9602 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9603 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9604 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9606 int move_direction = (dx == -1 ? MV_LEFT :
9607 dx == +1 ? MV_RIGHT :
9609 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9610 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9611 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9613 int jx = player->jx, jy = player->jy;
9614 int new_jx = jx + dx, new_jy = jy + dy;
9618 if (!player->active || (!dx && !dy))
9619 return MF_NO_ACTION;
9621 player->MovDir = (dx < 0 ? MV_LEFT :
9624 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9626 if (!IN_LEV_FIELD(new_jx, new_jy))
9627 return MF_NO_ACTION;
9629 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9630 return MF_NO_ACTION;
9633 element = MovingOrBlocked2Element(new_jx, new_jy);
9635 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9638 if (DONT_RUN_INTO(element))
9640 if (element == EL_ACID && dx == 0 && dy == 1)
9642 SplashAcid(new_jx, new_jy);
9643 Feld[jx][jy] = EL_PLAYER_1;
9644 InitMovingField(jx, jy, MV_DOWN);
9645 Store[jx][jy] = EL_ACID;
9646 ContinueMoving(jx, jy);
9650 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9655 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9656 if (can_move != MF_MOVING)
9659 /* check if DigField() has caused relocation of the player */
9660 if (player->jx != jx || player->jy != jy)
9661 return MF_NO_ACTION;
9663 StorePlayer[jx][jy] = 0;
9664 player->last_jx = jx;
9665 player->last_jy = jy;
9666 player->jx = new_jx;
9667 player->jy = new_jy;
9668 StorePlayer[new_jx][new_jy] = player->element_nr;
9671 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9673 player->step_counter++;
9676 player->drop_delay = 0;
9679 PlayerVisit[jx][jy] = FrameCounter;
9681 ScrollPlayer(player, SCROLL_INIT);
9684 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9686 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9688 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9691 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9693 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9694 CE_OTHER_GETS_ENTERED, enter_side);
9695 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9696 CE_ENTERED_BY_PLAYER, enter_side);
9703 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9705 int jx = player->jx, jy = player->jy;
9706 int old_jx = jx, old_jy = jy;
9707 int moved = MF_NO_ACTION;
9710 if (!player->active)
9715 if (player->MovPos == 0)
9717 player->is_moving = FALSE;
9718 player->is_digging = FALSE;
9719 player->is_collecting = FALSE;
9720 player->is_snapping = FALSE;
9721 player->is_pushing = FALSE;
9727 if (!player->active || (!dx && !dy))
9732 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9740 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9741 player->move_delay + player->move_delay_value);
9744 #if USE_NEW_MOVE_DELAY
9745 if (player->move_delay > 0)
9747 if (!FrameReached(&player->move_delay, player->move_delay_value))
9751 printf("::: can NOT move\n");
9757 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9758 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9765 printf("::: COULD move now\n");
9768 #if USE_NEW_MOVE_DELAY
9769 player->move_delay = -1; /* set to "uninitialized" value */
9772 /* store if player is automatically moved to next field */
9773 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9775 /* remove the last programmed player action */
9776 player->programmed_action = 0;
9780 /* should only happen if pre-1.2 tape recordings are played */
9781 /* this is only for backward compatibility */
9783 int original_move_delay_value = player->move_delay_value;
9786 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9790 /* scroll remaining steps with finest movement resolution */
9791 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9793 while (player->MovPos)
9795 ScrollPlayer(player, SCROLL_GO_ON);
9796 ScrollScreen(NULL, SCROLL_GO_ON);
9798 #if USE_NEW_MOVE_DELAY
9799 AdvanceFrameAndPlayerCounters(player->index_nr);
9808 player->move_delay_value = original_move_delay_value;
9811 if (player->last_move_dir & MV_HORIZONTAL)
9813 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9814 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9818 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9819 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9825 if (moved & MF_MOVING && !ScreenMovPos &&
9826 (player == local_player || !options.network))
9828 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9829 int offset = (setup.scroll_delay ? 3 : 0);
9831 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9833 /* actual player has left the screen -- scroll in that direction */
9834 if (jx != old_jx) /* player has moved horizontally */
9835 scroll_x += (jx - old_jx);
9836 else /* player has moved vertically */
9837 scroll_y += (jy - old_jy);
9841 if (jx != old_jx) /* player has moved horizontally */
9843 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9844 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9845 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9847 /* don't scroll over playfield boundaries */
9848 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9849 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9851 /* don't scroll more than one field at a time */
9852 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9854 /* don't scroll against the player's moving direction */
9855 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9856 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9857 scroll_x = old_scroll_x;
9859 else /* player has moved vertically */
9861 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9862 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9863 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9865 /* don't scroll over playfield boundaries */
9866 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9867 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9869 /* don't scroll more than one field at a time */
9870 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9872 /* don't scroll against the player's moving direction */
9873 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9874 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9875 scroll_y = old_scroll_y;
9879 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9881 if (!options.network && !AllPlayersInVisibleScreen())
9883 scroll_x = old_scroll_x;
9884 scroll_y = old_scroll_y;
9888 ScrollScreen(player, SCROLL_INIT);
9889 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9896 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9898 if (!(moved & MF_MOVING) && !player->is_pushing)
9903 player->StepFrame = 0;
9905 if (moved & MF_MOVING)
9908 printf("::: REALLY moves now\n");
9911 if (old_jx != jx && old_jy == jy)
9912 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9913 else if (old_jx == jx && old_jy != jy)
9914 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9916 DrawLevelField(jx, jy); /* for "crumbled sand" */
9918 player->last_move_dir = player->MovDir;
9919 player->is_moving = TRUE;
9921 player->is_snapping = FALSE;
9925 player->is_switching = FALSE;
9928 player->is_dropping = FALSE;
9932 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9935 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9938 int move_direction = player->MovDir;
9940 int enter_side = MV_DIR_OPPOSITE(move_direction);
9941 int leave_side = move_direction;
9943 static int trigger_sides[4][2] =
9945 /* enter side leave side */
9946 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9947 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9948 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9949 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9951 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9952 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9954 int old_element = Feld[old_jx][old_jy];
9955 int new_element = Feld[jx][jy];
9958 /* !!! TEST ONLY !!! */
9959 if (IS_CUSTOM_ELEMENT(old_element))
9960 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9962 player->index_bit, leave_side);
9964 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9966 player->index_bit, leave_side);
9968 if (IS_CUSTOM_ELEMENT(new_element))
9969 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9970 player->index_bit, enter_side);
9972 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9973 CE_OTHER_GETS_ENTERED,
9974 player->index_bit, enter_side);
9984 CheckGravityMovementWhenNotMoving(player);
9987 player->last_move_dir = MV_NO_MOVING;
9989 player->is_moving = FALSE;
9991 #if USE_NEW_MOVE_STYLE
9992 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9993 /* ensure that the player is also allowed to move in the next frame */
9994 /* (currently, the player is forced to wait eight frames before he can try
9997 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9998 player->move_delay = 0; /* allow direct movement in the next frame */
10002 #if USE_NEW_MOVE_DELAY
10003 if (player->move_delay == -1) /* not yet initialized by DigField() */
10004 player->move_delay = player->move_delay_value;
10007 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10009 TestIfHeroTouchesBadThing(jx, jy);
10010 TestIfPlayerTouchesCustomElement(jx, jy);
10013 if (!player->active)
10014 RemoveHero(player);
10019 void ScrollPlayer(struct PlayerInfo *player, int mode)
10021 int jx = player->jx, jy = player->jy;
10022 int last_jx = player->last_jx, last_jy = player->last_jy;
10023 int move_stepsize = TILEX / player->move_delay_value;
10025 if (!player->active || !player->MovPos)
10028 if (mode == SCROLL_INIT)
10030 player->actual_frame_counter = FrameCounter;
10031 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10034 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10036 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10037 player->block_delay);
10040 #if USE_NEW_BLOCK_STYLE
10043 if (player->block_delay <= 0)
10044 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10047 if (player->block_delay > 0 &&
10048 Feld[last_jx][last_jy] == EL_EMPTY)
10050 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10051 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10054 #if USE_NEW_MOVE_STYLE
10055 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10056 player->block_last_field) &&
10057 Feld[last_jx][last_jy] == EL_EMPTY)
10058 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10060 if (Feld[last_jx][last_jy] == EL_EMPTY)
10061 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10066 DrawPlayer(player);
10071 else if (!FrameReached(&player->actual_frame_counter, 1))
10074 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10075 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10077 #if USE_NEW_BLOCK_STYLE
10079 if (!player->block_last_field &&
10080 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10082 RemoveField(last_jx, last_jy);
10084 Feld[last_jx][last_jy] = EL_EMPTY;
10088 /* before DrawPlayer() to draw correct player graphic for this case */
10089 if (player->MovPos == 0)
10090 CheckGravityMovement(player);
10093 DrawPlayer(player); /* needed here only to cleanup last field */
10096 if (player->MovPos == 0) /* player reached destination field */
10099 if (player->move_delay_reset_counter > 0)
10101 player->move_delay_reset_counter--;
10103 if (player->move_delay_reset_counter == 0)
10105 /* continue with normal speed after quickly moving through gate */
10106 HALVE_PLAYER_SPEED(player);
10108 /* be able to make the next move without delay */
10109 player->move_delay = 0;
10113 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10115 /* continue with normal speed after quickly moving through gate */
10116 HALVE_PLAYER_SPEED(player);
10118 /* be able to make the next move without delay */
10119 player->move_delay = 0;
10123 #if USE_NEW_BLOCK_STYLE
10125 if (player->block_last_field &&
10126 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10128 RemoveField(last_jx, last_jy);
10130 Feld[last_jx][last_jy] = EL_EMPTY;
10134 player->last_jx = jx;
10135 player->last_jy = jy;
10137 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10138 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10139 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10141 DrawPlayer(player); /* needed here only to cleanup last field */
10142 RemoveHero(player);
10144 if (local_player->friends_still_needed == 0 ||
10145 IS_SP_ELEMENT(Feld[jx][jy]))
10146 player->LevelSolved = player->GameOver = TRUE;
10150 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10151 /* this breaks one level: "machine", level 000 */
10153 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10156 int move_direction = player->MovDir;
10158 int enter_side = MV_DIR_OPPOSITE(move_direction);
10159 int leave_side = move_direction;
10161 static int trigger_sides[4][2] =
10163 /* enter side leave side */
10164 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10165 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10166 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10167 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10169 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10170 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10172 int old_jx = last_jx;
10173 int old_jy = last_jy;
10174 int old_element = Feld[old_jx][old_jy];
10175 int new_element = Feld[jx][jy];
10178 /* !!! TEST ONLY !!! */
10179 if (IS_CUSTOM_ELEMENT(old_element))
10180 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10182 player->index_bit, leave_side);
10184 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10185 CE_OTHER_GETS_LEFT,
10186 player->index_bit, leave_side);
10188 if (IS_CUSTOM_ELEMENT(new_element))
10189 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10190 player->index_bit, enter_side);
10192 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10193 CE_OTHER_GETS_ENTERED,
10194 player->index_bit, enter_side);
10200 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10202 TestIfHeroTouchesBadThing(jx, jy);
10203 TestIfPlayerTouchesCustomElement(jx, jy);
10206 /* needed because pushed element has not yet reached its destination,
10207 so it would trigger a change event at its previous field location */
10208 if (!player->is_pushing)
10210 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10213 if (!player->active)
10214 RemoveHero(player);
10217 if (level.use_step_counter)
10227 if (TimeLeft <= 10 && setup.time_limit)
10228 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10230 DrawGameValue_Time(TimeLeft);
10232 if (!TimeLeft && setup.time_limit)
10233 for (i = 0; i < MAX_PLAYERS; i++)
10234 KillHero(&stored_player[i]);
10236 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10237 DrawGameValue_Time(TimePlayed);
10240 if (tape.single_step && tape.recording && !tape.pausing &&
10241 !player->programmed_action)
10242 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10246 void ScrollScreen(struct PlayerInfo *player, int mode)
10248 static unsigned long screen_frame_counter = 0;
10250 if (mode == SCROLL_INIT)
10252 /* set scrolling step size according to actual player's moving speed */
10253 ScrollStepSize = TILEX / player->move_delay_value;
10255 screen_frame_counter = FrameCounter;
10256 ScreenMovDir = player->MovDir;
10257 ScreenMovPos = player->MovPos;
10258 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10261 else if (!FrameReached(&screen_frame_counter, 1))
10266 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10267 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10268 redraw_mask |= REDRAW_FIELD;
10271 ScreenMovDir = MV_NO_MOVING;
10274 void TestIfPlayerTouchesCustomElement(int x, int y)
10276 static int xy[4][2] =
10283 static int trigger_sides[4][2] =
10285 /* center side border side */
10286 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10287 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10288 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10289 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10291 static int touch_dir[4] =
10293 MV_LEFT | MV_RIGHT,
10298 int center_element = Feld[x][y]; /* should always be non-moving! */
10301 for (i = 0; i < NUM_DIRECTIONS; i++)
10303 int xx = x + xy[i][0];
10304 int yy = y + xy[i][1];
10305 int center_side = trigger_sides[i][0];
10306 int border_side = trigger_sides[i][1];
10307 int border_element;
10309 if (!IN_LEV_FIELD(xx, yy))
10312 if (IS_PLAYER(x, y))
10314 struct PlayerInfo *player = PLAYERINFO(x, y);
10316 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10317 border_element = Feld[xx][yy]; /* may be moving! */
10318 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10319 border_element = Feld[xx][yy];
10320 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10321 border_element = MovingOrBlocked2Element(xx, yy);
10323 continue; /* center and border element do not touch */
10326 /* !!! TEST ONLY !!! */
10327 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10328 player->index_bit, border_side);
10329 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10330 CE_OTHER_GETS_TOUCHED,
10331 player->index_bit, border_side);
10333 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10334 CE_OTHER_GETS_TOUCHED,
10335 player->index_bit, border_side);
10336 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10337 player->index_bit, border_side);
10340 else if (IS_PLAYER(xx, yy))
10342 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10344 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10346 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10347 continue; /* center and border element do not touch */
10351 /* !!! TEST ONLY !!! */
10352 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10353 player->index_bit, center_side);
10354 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10355 CE_OTHER_GETS_TOUCHED,
10356 player->index_bit, center_side);
10358 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10359 CE_OTHER_GETS_TOUCHED,
10360 player->index_bit, center_side);
10361 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10362 player->index_bit, center_side);
10370 void TestIfElementTouchesCustomElement(int x, int y)
10372 static int xy[4][2] =
10379 static int trigger_sides[4][2] =
10381 /* center side border side */
10382 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10383 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10384 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10385 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10387 static int touch_dir[4] =
10389 MV_LEFT | MV_RIGHT,
10394 boolean change_center_element = FALSE;
10395 int center_element_change_page = 0;
10396 int center_element = Feld[x][y]; /* should always be non-moving! */
10397 int border_trigger_element = EL_UNDEFINED;
10400 for (i = 0; i < NUM_DIRECTIONS; i++)
10402 int xx = x + xy[i][0];
10403 int yy = y + xy[i][1];
10404 int center_side = trigger_sides[i][0];
10405 int border_side = trigger_sides[i][1];
10406 int border_element;
10408 if (!IN_LEV_FIELD(xx, yy))
10411 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10412 border_element = Feld[xx][yy]; /* may be moving! */
10413 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10414 border_element = Feld[xx][yy];
10415 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10416 border_element = MovingOrBlocked2Element(xx, yy);
10418 continue; /* center and border element do not touch */
10420 /* check for change of center element (but change it only once) */
10421 if (IS_CUSTOM_ELEMENT(center_element) &&
10422 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10423 !change_center_element)
10425 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10427 struct ElementChangeInfo *change =
10428 &element_info[center_element].change_page[j];
10430 if (change->can_change &&
10431 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10432 change->trigger_side & border_side &&
10434 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10436 change->trigger_element == border_element
10440 change_center_element = TRUE;
10441 center_element_change_page = j;
10442 border_trigger_element = border_element;
10449 /* check for change of border element */
10450 if (IS_CUSTOM_ELEMENT(border_element) &&
10451 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10453 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10455 struct ElementChangeInfo *change =
10456 &element_info[border_element].change_page[j];
10458 if (change->can_change &&
10459 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10460 change->trigger_side & center_side &&
10462 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10464 change->trigger_element == center_element
10469 printf("::: border_element %d, %d\n", x, y);
10472 CheckElementChangeByPage(xx, yy, border_element, center_element,
10473 CE_OTHER_IS_TOUCHING, j);
10480 if (change_center_element)
10483 printf("::: center_element %d, %d\n", x, y);
10486 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10487 CE_OTHER_IS_TOUCHING, center_element_change_page);
10491 void TestIfElementHitsCustomElement(int x, int y, int direction)
10493 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10494 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10495 int hitx = x + dx, hity = y + dy;
10496 int hitting_element = Feld[x][y];
10497 int touched_element;
10499 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10500 !IS_FREE(hitx, hity) &&
10501 (!IS_MOVING(hitx, hity) ||
10502 MovDir[hitx][hity] != direction ||
10503 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10506 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10510 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10514 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10515 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10517 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10518 CE_HITTING_SOMETHING, direction);
10520 if (IN_LEV_FIELD(hitx, hity))
10522 int opposite_direction = MV_DIR_OPPOSITE(direction);
10523 int hitting_side = direction;
10524 int touched_side = opposite_direction;
10526 int touched_element = MovingOrBlocked2Element(hitx, hity);
10529 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10530 MovDir[hitx][hity] != direction ||
10531 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10540 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10541 CE_HIT_BY_SOMETHING, opposite_direction);
10543 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10544 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10546 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10548 struct ElementChangeInfo *change =
10549 &element_info[hitting_element].change_page[i];
10551 if (change->can_change &&
10552 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10553 change->trigger_side & touched_side &&
10556 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10558 change->trigger_element == touched_element
10562 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10563 CE_OTHER_IS_HITTING, i);
10569 if (IS_CUSTOM_ELEMENT(touched_element) &&
10570 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10572 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10574 struct ElementChangeInfo *change =
10575 &element_info[touched_element].change_page[i];
10577 if (change->can_change &&
10578 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10579 change->trigger_side & hitting_side &&
10581 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10583 change->trigger_element == hitting_element
10587 CheckElementChangeByPage(hitx, hity, touched_element,
10588 hitting_element, CE_OTHER_GETS_HIT, i);
10598 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10600 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10601 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10602 int hitx = x + dx, hity = y + dy;
10603 int hitting_element = Feld[x][y];
10604 int touched_element;
10606 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10607 !IS_FREE(hitx, hity) &&
10608 (!IS_MOVING(hitx, hity) ||
10609 MovDir[hitx][hity] != direction ||
10610 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10613 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10617 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10621 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10622 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10624 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10625 EP_CAN_SMASH_EVERYTHING, direction);
10627 if (IN_LEV_FIELD(hitx, hity))
10629 int opposite_direction = MV_DIR_OPPOSITE(direction);
10630 int hitting_side = direction;
10631 int touched_side = opposite_direction;
10633 int touched_element = MovingOrBlocked2Element(hitx, hity);
10636 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10637 MovDir[hitx][hity] != direction ||
10638 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10647 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10648 CE_SMASHED_BY_SOMETHING, opposite_direction);
10650 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10651 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10653 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10655 struct ElementChangeInfo *change =
10656 &element_info[hitting_element].change_page[i];
10658 if (change->can_change &&
10659 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10660 change->trigger_side & touched_side &&
10663 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10665 change->trigger_element == touched_element
10669 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10670 CE_OTHER_IS_SMASHING, i);
10676 if (IS_CUSTOM_ELEMENT(touched_element) &&
10677 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10679 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10681 struct ElementChangeInfo *change =
10682 &element_info[touched_element].change_page[i];
10684 if (change->can_change &&
10685 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10686 change->trigger_side & hitting_side &&
10688 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10690 change->trigger_element == hitting_element
10694 CheckElementChangeByPage(hitx, hity, touched_element,
10695 hitting_element, CE_OTHER_GETS_SMASHED,i);
10705 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10707 int i, kill_x = -1, kill_y = -1;
10708 int bad_element = -1;
10709 static int test_xy[4][2] =
10716 static int test_dir[4] =
10724 for (i = 0; i < NUM_DIRECTIONS; i++)
10726 int test_x, test_y, test_move_dir, test_element;
10728 test_x = good_x + test_xy[i][0];
10729 test_y = good_y + test_xy[i][1];
10731 if (!IN_LEV_FIELD(test_x, test_y))
10735 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10738 test_element = Feld[test_x][test_y];
10740 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10743 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10744 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10746 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10747 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10751 bad_element = test_element;
10757 if (kill_x != -1 || kill_y != -1)
10759 if (IS_PLAYER(good_x, good_y))
10761 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10764 if (player->shield_deadly_time_left > 0 &&
10765 !IS_INDESTRUCTIBLE(bad_element))
10766 Bang(kill_x, kill_y);
10767 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10770 if (player->shield_deadly_time_left > 0)
10771 Bang(kill_x, kill_y);
10772 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10777 Bang(good_x, good_y);
10781 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10783 int i, kill_x = -1, kill_y = -1;
10784 int bad_element = Feld[bad_x][bad_y];
10785 static int test_xy[4][2] =
10792 static int touch_dir[4] =
10794 MV_LEFT | MV_RIGHT,
10799 static int test_dir[4] =
10807 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10810 for (i = 0; i < NUM_DIRECTIONS; i++)
10812 int test_x, test_y, test_move_dir, test_element;
10814 test_x = bad_x + test_xy[i][0];
10815 test_y = bad_y + test_xy[i][1];
10816 if (!IN_LEV_FIELD(test_x, test_y))
10820 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10822 test_element = Feld[test_x][test_y];
10824 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10825 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10827 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10828 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10830 /* good thing is player or penguin that does not move away */
10831 if (IS_PLAYER(test_x, test_y))
10833 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10835 if (bad_element == EL_ROBOT && player->is_moving)
10836 continue; /* robot does not kill player if he is moving */
10838 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10840 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10841 continue; /* center and border element do not touch */
10848 else if (test_element == EL_PENGUIN)
10857 if (kill_x != -1 || kill_y != -1)
10859 if (IS_PLAYER(kill_x, kill_y))
10861 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10864 if (player->shield_deadly_time_left > 0 &&
10865 !IS_INDESTRUCTIBLE(bad_element))
10866 Bang(bad_x, bad_y);
10867 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10870 if (player->shield_deadly_time_left > 0)
10871 Bang(bad_x, bad_y);
10872 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10877 Bang(kill_x, kill_y);
10881 void TestIfHeroTouchesBadThing(int x, int y)
10883 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10886 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10888 TestIfGoodThingHitsBadThing(x, y, move_dir);
10891 void TestIfBadThingTouchesHero(int x, int y)
10893 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10896 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10898 TestIfBadThingHitsGoodThing(x, y, move_dir);
10901 void TestIfFriendTouchesBadThing(int x, int y)
10903 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10906 void TestIfBadThingTouchesFriend(int x, int y)
10908 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10911 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10913 int i, kill_x = bad_x, kill_y = bad_y;
10914 static int xy[4][2] =
10922 for (i = 0; i < NUM_DIRECTIONS; i++)
10926 x = bad_x + xy[i][0];
10927 y = bad_y + xy[i][1];
10928 if (!IN_LEV_FIELD(x, y))
10931 element = Feld[x][y];
10932 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10933 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10941 if (kill_x != bad_x || kill_y != bad_y)
10942 Bang(bad_x, bad_y);
10945 void KillHero(struct PlayerInfo *player)
10947 int jx = player->jx, jy = player->jy;
10949 if (!player->active)
10952 /* remove accessible field at the player's position */
10953 Feld[jx][jy] = EL_EMPTY;
10955 /* deactivate shield (else Bang()/Explode() would not work right) */
10956 player->shield_normal_time_left = 0;
10957 player->shield_deadly_time_left = 0;
10963 static void KillHeroUnlessEnemyProtected(int x, int y)
10965 if (!PLAYER_ENEMY_PROTECTED(x, y))
10966 KillHero(PLAYERINFO(x, y));
10969 static void KillHeroUnlessExplosionProtected(int x, int y)
10971 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10972 KillHero(PLAYERINFO(x, y));
10975 void BuryHero(struct PlayerInfo *player)
10977 int jx = player->jx, jy = player->jy;
10979 if (!player->active)
10983 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10985 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10987 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10989 player->GameOver = TRUE;
10990 RemoveHero(player);
10993 void RemoveHero(struct PlayerInfo *player)
10995 int jx = player->jx, jy = player->jy;
10996 int i, found = FALSE;
10998 player->present = FALSE;
10999 player->active = FALSE;
11001 if (!ExplodeField[jx][jy])
11002 StorePlayer[jx][jy] = 0;
11004 for (i = 0; i < MAX_PLAYERS; i++)
11005 if (stored_player[i].active)
11009 AllPlayersGone = TRUE;
11016 =============================================================================
11017 checkDiagonalPushing()
11018 -----------------------------------------------------------------------------
11019 check if diagonal input device direction results in pushing of object
11020 (by checking if the alternative direction is walkable, diggable, ...)
11021 =============================================================================
11024 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11025 int x, int y, int real_dx, int real_dy)
11027 int jx, jy, dx, dy, xx, yy;
11029 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11032 /* diagonal direction: check alternative direction */
11037 xx = jx + (dx == 0 ? real_dx : 0);
11038 yy = jy + (dy == 0 ? real_dy : 0);
11040 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11044 =============================================================================
11046 -----------------------------------------------------------------------------
11047 x, y: field next to player (non-diagonal) to try to dig to
11048 real_dx, real_dy: direction as read from input device (can be diagonal)
11049 =============================================================================
11052 int DigField(struct PlayerInfo *player,
11053 int oldx, int oldy, int x, int y,
11054 int real_dx, int real_dy, int mode)
11057 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11059 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11060 boolean player_was_pushing = player->is_pushing;
11061 int jx = oldx, jy = oldy;
11062 int dx = x - jx, dy = y - jy;
11063 int nextx = x + dx, nexty = y + dy;
11064 int move_direction = (dx == -1 ? MV_LEFT :
11065 dx == +1 ? MV_RIGHT :
11067 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11068 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11070 int dig_side = MV_DIR_OPPOSITE(move_direction);
11072 static int trigger_sides[4] =
11074 CH_SIDE_RIGHT, /* moving left */
11075 CH_SIDE_LEFT, /* moving right */
11076 CH_SIDE_BOTTOM, /* moving up */
11077 CH_SIDE_TOP, /* moving down */
11079 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11081 int old_element = Feld[jx][jy];
11084 if (is_player) /* function can also be called by EL_PENGUIN */
11086 if (player->MovPos == 0)
11088 player->is_digging = FALSE;
11089 player->is_collecting = FALSE;
11092 if (player->MovPos == 0) /* last pushing move finished */
11093 player->is_pushing = FALSE;
11095 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11097 player->is_switching = FALSE;
11098 #if USE_NEW_PUSH_DELAY
11099 player->push_delay = -1;
11101 player->push_delay = 0;
11104 return MF_NO_ACTION;
11108 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11109 return MF_NO_ACTION;
11114 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11116 if (IS_TUBE(Feld[jx][jy]) ||
11117 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11121 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11122 int tube_leave_directions[][2] =
11124 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11125 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11126 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11127 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11128 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11129 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11130 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11131 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11132 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11133 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11134 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11135 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11138 while (tube_leave_directions[i][0] != tube_element)
11141 if (tube_leave_directions[i][0] == -1) /* should not happen */
11145 if (!(tube_leave_directions[i][1] & move_direction))
11146 return MF_NO_ACTION; /* tube has no opening in this direction */
11151 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11152 old_element = Back[jx][jy];
11156 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11157 return MF_NO_ACTION; /* field has no opening in this direction */
11159 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11160 return MF_NO_ACTION; /* field has no opening in this direction */
11162 element = Feld[x][y];
11164 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11165 return MF_NO_ACTION;
11167 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11168 game.engine_version >= VERSION_IDENT(2,2,0,0))
11169 return MF_NO_ACTION;
11172 if (game.gravity && is_player && !player->is_auto_moving &&
11173 canFallDown(player) && move_direction != MV_DOWN &&
11174 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11175 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11179 if (element == EL_EMPTY_SPACE &&
11180 game.gravity && !player->is_auto_moving &&
11181 canFallDown(player) && move_direction != MV_DOWN)
11182 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11188 case EL_SP_PORT_LEFT:
11189 case EL_SP_PORT_RIGHT:
11190 case EL_SP_PORT_UP:
11191 case EL_SP_PORT_DOWN:
11192 case EL_SP_PORT_HORIZONTAL:
11193 case EL_SP_PORT_VERTICAL:
11194 case EL_SP_PORT_ANY:
11195 case EL_SP_GRAVITY_PORT_LEFT:
11196 case EL_SP_GRAVITY_PORT_RIGHT:
11197 case EL_SP_GRAVITY_PORT_UP:
11198 case EL_SP_GRAVITY_PORT_DOWN:
11200 if (!canEnterSupaplexPort(x, y, dx, dy))
11201 return MF_NO_ACTION;
11204 element != EL_SP_PORT_LEFT &&
11205 element != EL_SP_GRAVITY_PORT_LEFT &&
11206 element != EL_SP_PORT_HORIZONTAL &&
11207 element != EL_SP_PORT_ANY) ||
11209 element != EL_SP_PORT_RIGHT &&
11210 element != EL_SP_GRAVITY_PORT_RIGHT &&
11211 element != EL_SP_PORT_HORIZONTAL &&
11212 element != EL_SP_PORT_ANY) ||
11214 element != EL_SP_PORT_UP &&
11215 element != EL_SP_GRAVITY_PORT_UP &&
11216 element != EL_SP_PORT_VERTICAL &&
11217 element != EL_SP_PORT_ANY) ||
11219 element != EL_SP_PORT_DOWN &&
11220 element != EL_SP_GRAVITY_PORT_DOWN &&
11221 element != EL_SP_PORT_VERTICAL &&
11222 element != EL_SP_PORT_ANY) ||
11223 !IN_LEV_FIELD(nextx, nexty) ||
11224 !IS_FREE(nextx, nexty))
11225 return MF_NO_ACTION;
11228 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11229 element == EL_SP_GRAVITY_PORT_RIGHT ||
11230 element == EL_SP_GRAVITY_PORT_UP ||
11231 element == EL_SP_GRAVITY_PORT_DOWN)
11232 game.gravity = !game.gravity;
11234 /* automatically move to the next field with double speed */
11235 player->programmed_action = move_direction;
11237 if (player->move_delay_reset_counter == 0)
11239 player->move_delay_reset_counter = 2; /* two double speed steps */
11241 DOUBLE_PLAYER_SPEED(player);
11244 player->move_delay_reset_counter = 2;
11246 DOUBLE_PLAYER_SPEED(player);
11250 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11253 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11259 case EL_TUBE_VERTICAL:
11260 case EL_TUBE_HORIZONTAL:
11261 case EL_TUBE_VERTICAL_LEFT:
11262 case EL_TUBE_VERTICAL_RIGHT:
11263 case EL_TUBE_HORIZONTAL_UP:
11264 case EL_TUBE_HORIZONTAL_DOWN:
11265 case EL_TUBE_LEFT_UP:
11266 case EL_TUBE_LEFT_DOWN:
11267 case EL_TUBE_RIGHT_UP:
11268 case EL_TUBE_RIGHT_DOWN:
11271 int tube_enter_directions[][2] =
11273 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11274 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11275 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11276 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11277 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11278 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11279 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11280 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11281 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11282 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11283 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11284 { -1, MV_NO_MOVING }
11287 while (tube_enter_directions[i][0] != element)
11290 if (tube_enter_directions[i][0] == -1) /* should not happen */
11294 if (!(tube_enter_directions[i][1] & move_direction))
11295 return MF_NO_ACTION; /* tube has no opening in this direction */
11297 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11305 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11307 if (IS_WALKABLE(element))
11310 int sound_element = SND_ELEMENT(element);
11311 int sound_action = ACTION_WALKING;
11314 if (!ACCESS_FROM(element, opposite_direction))
11315 return MF_NO_ACTION; /* field not accessible from this direction */
11319 if (element == EL_EMPTY_SPACE &&
11320 game.gravity && !player->is_auto_moving &&
11321 canFallDown(player) && move_direction != MV_DOWN)
11322 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11325 if (IS_GATE(element))
11327 if (!player->key[element - EL_GATE_1])
11328 return MF_NO_ACTION;
11330 else if (IS_GATE_GRAY(element))
11332 if (!player->key[element - EL_GATE_1_GRAY])
11333 return MF_NO_ACTION;
11335 else if (element == EL_EXIT_OPEN ||
11336 element == EL_SP_EXIT_OPEN ||
11337 element == EL_SP_EXIT_OPENING)
11339 sound_action = ACTION_PASSING; /* player is passing exit */
11341 else if (element == EL_EMPTY)
11343 sound_action = ACTION_MOVING; /* nothing to walk on */
11346 /* play sound from background or player, whatever is available */
11347 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11348 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11350 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11355 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11357 else if (IS_PASSABLE(element))
11361 if (!canPassField(x, y, move_direction))
11362 return MF_NO_ACTION;
11367 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11368 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11369 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11370 return MF_NO_ACTION;
11372 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11373 return MF_NO_ACTION;
11378 if (!ACCESS_FROM(element, opposite_direction))
11379 return MF_NO_ACTION; /* field not accessible from this direction */
11381 if (IS_CUSTOM_ELEMENT(element) &&
11382 !ACCESS_FROM(element, opposite_direction))
11383 return MF_NO_ACTION; /* field not accessible from this direction */
11387 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11388 return MF_NO_ACTION;
11393 if (IS_EM_GATE(element))
11395 if (!player->key[element - EL_EM_GATE_1])
11396 return MF_NO_ACTION;
11398 else if (IS_EM_GATE_GRAY(element))
11400 if (!player->key[element - EL_EM_GATE_1_GRAY])
11401 return MF_NO_ACTION;
11403 else if (IS_SP_PORT(element))
11405 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11406 element == EL_SP_GRAVITY_PORT_RIGHT ||
11407 element == EL_SP_GRAVITY_PORT_UP ||
11408 element == EL_SP_GRAVITY_PORT_DOWN)
11409 game.gravity = !game.gravity;
11410 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11411 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11412 element == EL_SP_GRAVITY_ON_PORT_UP ||
11413 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11414 game.gravity = TRUE;
11415 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11416 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11417 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11418 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11419 game.gravity = FALSE;
11422 /* automatically move to the next field with double speed */
11423 player->programmed_action = move_direction;
11425 if (player->move_delay_reset_counter == 0)
11427 player->move_delay_reset_counter = 2; /* two double speed steps */
11429 DOUBLE_PLAYER_SPEED(player);
11432 player->move_delay_reset_counter = 2;
11434 DOUBLE_PLAYER_SPEED(player);
11437 PlayLevelSoundAction(x, y, ACTION_PASSING);
11441 else if (IS_DIGGABLE(element))
11445 if (mode != DF_SNAP)
11448 GfxElement[x][y] = GFX_ELEMENT(element);
11451 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11453 player->is_digging = TRUE;
11456 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11458 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11459 player->index_bit, dig_side);
11462 if (mode == DF_SNAP)
11463 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11468 else if (IS_COLLECTIBLE(element))
11472 if (is_player && mode != DF_SNAP)
11474 GfxElement[x][y] = element;
11475 player->is_collecting = TRUE;
11478 if (element == EL_SPEED_PILL)
11479 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11480 else if (element == EL_EXTRA_TIME && level.time > 0)
11483 DrawGameValue_Time(TimeLeft);
11485 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11487 player->shield_normal_time_left += 10;
11488 if (element == EL_SHIELD_DEADLY)
11489 player->shield_deadly_time_left += 10;
11491 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11493 if (player->inventory_size < MAX_INVENTORY_SIZE)
11494 player->inventory_element[player->inventory_size++] = element;
11496 DrawGameValue_Dynamite(local_player->inventory_size);
11498 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11500 player->dynabomb_count++;
11501 player->dynabombs_left++;
11503 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11505 player->dynabomb_size++;
11507 else if (element == EL_DYNABOMB_INCREASE_POWER)
11509 player->dynabomb_xl = TRUE;
11511 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11512 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11514 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11515 element - EL_KEY_1 : element - EL_EM_KEY_1);
11517 player->key[key_nr] = TRUE;
11519 DrawGameValue_Keys(player);
11521 redraw_mask |= REDRAW_DOOR_1;
11523 else if (IS_ENVELOPE(element))
11526 player->show_envelope = element;
11528 ShowEnvelope(element - EL_ENVELOPE_1);
11531 else if (IS_DROPPABLE(element) ||
11532 IS_THROWABLE(element)) /* can be collected and dropped */
11536 if (element_info[element].collect_count == 0)
11537 player->inventory_infinite_element = element;
11539 for (i = 0; i < element_info[element].collect_count; i++)
11540 if (player->inventory_size < MAX_INVENTORY_SIZE)
11541 player->inventory_element[player->inventory_size++] = element;
11543 DrawGameValue_Dynamite(local_player->inventory_size);
11545 else if (element_info[element].collect_count > 0)
11547 local_player->gems_still_needed -=
11548 element_info[element].collect_count;
11549 if (local_player->gems_still_needed < 0)
11550 local_player->gems_still_needed = 0;
11552 DrawGameValue_Emeralds(local_player->gems_still_needed);
11555 RaiseScoreElement(element);
11556 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11559 CheckTriggeredElementChangeByPlayer(x, y, element,
11560 CE_OTHER_GETS_COLLECTED,
11561 player->index_bit, dig_side);
11564 if (mode == DF_SNAP)
11565 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11570 else if (IS_PUSHABLE(element))
11572 if (mode == DF_SNAP && element != EL_BD_ROCK)
11573 return MF_NO_ACTION;
11575 if (CAN_FALL(element) && dy)
11576 return MF_NO_ACTION;
11578 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11579 !(element == EL_SPRING && level.use_spring_bug))
11580 return MF_NO_ACTION;
11583 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11584 ((move_direction & MV_VERTICAL &&
11585 ((element_info[element].move_pattern & MV_LEFT &&
11586 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11587 (element_info[element].move_pattern & MV_RIGHT &&
11588 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11589 (move_direction & MV_HORIZONTAL &&
11590 ((element_info[element].move_pattern & MV_UP &&
11591 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11592 (element_info[element].move_pattern & MV_DOWN &&
11593 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11594 return MF_NO_ACTION;
11598 /* do not push elements already moving away faster than player */
11599 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11600 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11601 return MF_NO_ACTION;
11603 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11604 return MF_NO_ACTION;
11610 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11612 if (player->push_delay_value == -1 || !player_was_pushing)
11613 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11615 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11617 if (player->push_delay_value == -1)
11618 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11621 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11623 if (player->push_delay_value == -1 || !player_was_pushing)
11624 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11627 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11629 if (!player->is_pushing)
11630 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11634 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11635 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11636 !player_is_pushing))
11637 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11640 if (!player->is_pushing &&
11641 game.engine_version >= VERSION_IDENT(2,2,0,7))
11642 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11646 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11647 player->push_delay, player->push_delay_value,
11648 FrameCounter, game.engine_version,
11649 player_was_pushing, player->is_pushing,
11650 element, element_info[element].token_name,
11651 GET_NEW_PUSH_DELAY(element));
11654 player->is_pushing = TRUE;
11656 if (!(IN_LEV_FIELD(nextx, nexty) &&
11657 (IS_FREE(nextx, nexty) ||
11658 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11659 IS_SB_ELEMENT(element)))))
11660 return MF_NO_ACTION;
11662 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11663 return MF_NO_ACTION;
11665 #if USE_NEW_PUSH_DELAY
11668 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11669 printf("::: ALERT: %d, %d [%d / %d]\n",
11670 player->push_delay, player->push_delay2,
11671 FrameCounter, FrameCounter / 50);
11674 if (player->push_delay == -1) /* new pushing; restart delay */
11675 player->push_delay = 0;
11677 if (player->push_delay == 0) /* new pushing; restart delay */
11678 player->push_delay = FrameCounter;
11681 #if USE_NEW_PUSH_DELAY
11683 if ( (player->push_delay > 0) != (!xxx_fr) )
11684 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11685 player->push_delay,
11686 xxx_pdv2, player->push_delay2, player->push_delay_value,
11687 FrameCounter, FrameCounter / 50);
11691 if (player->push_delay > 0 &&
11692 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11693 element != EL_SPRING && element != EL_BALLOON)
11696 if (player->push_delay < player->push_delay_value &&
11697 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11698 element != EL_SPRING && element != EL_BALLOON)
11702 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11703 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11704 element != EL_SPRING && element != EL_BALLOON)
11707 /* make sure that there is no move delay before next try to push */
11708 #if USE_NEW_MOVE_DELAY
11709 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11710 player->move_delay = 0;
11712 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11713 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11716 return MF_NO_ACTION;
11720 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11723 if (IS_SB_ELEMENT(element))
11725 if (element == EL_SOKOBAN_FIELD_FULL)
11727 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11728 local_player->sokobanfields_still_needed++;
11731 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11733 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11734 local_player->sokobanfields_still_needed--;
11737 Feld[x][y] = EL_SOKOBAN_OBJECT;
11739 if (Back[x][y] == Back[nextx][nexty])
11740 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11741 else if (Back[x][y] != 0)
11742 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11745 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11748 if (local_player->sokobanfields_still_needed == 0 &&
11749 game.emulation == EMU_SOKOBAN)
11751 player->LevelSolved = player->GameOver = TRUE;
11752 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11756 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11758 InitMovingField(x, y, move_direction);
11759 GfxAction[x][y] = ACTION_PUSHING;
11761 if (mode == DF_SNAP)
11762 ContinueMoving(x, y);
11764 MovPos[x][y] = (dx != 0 ? dx : dy);
11766 Pushed[x][y] = TRUE;
11767 Pushed[nextx][nexty] = TRUE;
11769 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11770 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11772 player->push_delay_value = -1; /* get new value later */
11774 #if USE_PUSH_BUGFIX
11775 /* now: check for element change _after_ element has been pushed! */
11777 if (game.use_bug_change_when_pushing)
11779 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11782 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11783 player->index_bit, dig_side);
11784 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11785 player->index_bit, dig_side);
11791 /* check for element change _after_ element has been pushed! */
11795 /* !!! TEST ONLY !!! */
11796 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11797 player->index_bit, dig_side);
11798 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11799 player->index_bit, dig_side);
11801 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11802 player->index_bit, dig_side);
11803 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11804 player->index_bit, dig_side);
11812 else if (IS_SWITCHABLE(element))
11814 if (PLAYER_SWITCHING(player, x, y))
11816 CheckTriggeredElementChangeByPlayer(x,y, element,
11817 CE_OTHER_GETS_PRESSED,
11818 player->index_bit, dig_side);
11823 player->is_switching = TRUE;
11824 player->switch_x = x;
11825 player->switch_y = y;
11827 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11829 if (element == EL_ROBOT_WHEEL)
11831 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11835 DrawLevelField(x, y);
11837 else if (element == EL_SP_TERMINAL)
11841 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11843 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11845 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11846 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11849 else if (IS_BELT_SWITCH(element))
11851 ToggleBeltSwitch(x, y);
11853 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11854 element == EL_SWITCHGATE_SWITCH_DOWN)
11856 ToggleSwitchgateSwitch(x, y);
11858 else if (element == EL_LIGHT_SWITCH ||
11859 element == EL_LIGHT_SWITCH_ACTIVE)
11861 ToggleLightSwitch(x, y);
11864 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11865 SND_LIGHT_SWITCH_ACTIVATING :
11866 SND_LIGHT_SWITCH_DEACTIVATING);
11869 else if (element == EL_TIMEGATE_SWITCH)
11871 ActivateTimegateSwitch(x, y);
11873 else if (element == EL_BALLOON_SWITCH_LEFT ||
11874 element == EL_BALLOON_SWITCH_RIGHT ||
11875 element == EL_BALLOON_SWITCH_UP ||
11876 element == EL_BALLOON_SWITCH_DOWN ||
11877 element == EL_BALLOON_SWITCH_ANY)
11879 if (element == EL_BALLOON_SWITCH_ANY)
11880 game.balloon_dir = move_direction;
11882 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11883 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11884 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11885 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11888 else if (element == EL_LAMP)
11890 Feld[x][y] = EL_LAMP_ACTIVE;
11891 local_player->lights_still_needed--;
11893 DrawLevelField(x, y);
11895 else if (element == EL_TIME_ORB_FULL)
11897 Feld[x][y] = EL_TIME_ORB_EMPTY;
11899 DrawGameValue_Time(TimeLeft);
11901 DrawLevelField(x, y);
11904 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11908 CheckTriggeredElementChangeByPlayer(x, y, element,
11909 CE_OTHER_IS_SWITCHING,
11910 player->index_bit, dig_side);
11912 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11913 player->index_bit, dig_side);
11919 if (!PLAYER_SWITCHING(player, x, y))
11921 player->is_switching = TRUE;
11922 player->switch_x = x;
11923 player->switch_y = y;
11926 /* !!! TEST ONLY !!! */
11927 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11928 player->index_bit, dig_side);
11929 CheckTriggeredElementChangeByPlayer(x, y, element,
11930 CE_OTHER_IS_SWITCHING,
11931 player->index_bit, dig_side);
11933 CheckTriggeredElementChangeByPlayer(x, y, element,
11934 CE_OTHER_IS_SWITCHING,
11935 player->index_bit, dig_side);
11936 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11937 player->index_bit, dig_side);
11942 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11943 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11944 player->index_bit, dig_side);
11945 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11946 player->index_bit, dig_side);
11948 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11949 player->index_bit, dig_side);
11950 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11951 player->index_bit, dig_side);
11955 return MF_NO_ACTION;
11958 #if USE_NEW_PUSH_DELAY
11959 player->push_delay = -1;
11961 player->push_delay = 0;
11964 if (Feld[x][y] != element) /* really digged/collected something */
11965 player->is_collecting = !player->is_digging;
11970 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11972 int jx = player->jx, jy = player->jy;
11973 int x = jx + dx, y = jy + dy;
11974 int snap_direction = (dx == -1 ? MV_LEFT :
11975 dx == +1 ? MV_RIGHT :
11977 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11980 if (player->MovPos != 0)
11983 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11987 if (!player->active || !IN_LEV_FIELD(x, y))
11995 if (player->MovPos == 0)
11996 player->is_pushing = FALSE;
11998 player->is_snapping = FALSE;
12000 if (player->MovPos == 0)
12002 player->is_moving = FALSE;
12003 player->is_digging = FALSE;
12004 player->is_collecting = FALSE;
12010 if (player->is_snapping)
12013 player->MovDir = snap_direction;
12016 if (player->MovPos == 0)
12019 player->is_moving = FALSE;
12020 player->is_digging = FALSE;
12021 player->is_collecting = FALSE;
12024 player->is_dropping = FALSE;
12026 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12029 player->is_snapping = TRUE;
12032 if (player->MovPos == 0)
12035 player->is_moving = FALSE;
12036 player->is_digging = FALSE;
12037 player->is_collecting = FALSE;
12041 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12042 DrawLevelField(player->last_jx, player->last_jy);
12045 DrawLevelField(x, y);
12054 boolean DropElement(struct PlayerInfo *player)
12056 int old_element, new_element;
12057 int dropx = player->jx, dropy = player->jy;
12058 int drop_direction = player->MovDir;
12060 int drop_side = drop_direction;
12062 static int trigger_sides[4] =
12064 CH_SIDE_LEFT, /* dropping left */
12065 CH_SIDE_RIGHT, /* dropping right */
12066 CH_SIDE_TOP, /* dropping up */
12067 CH_SIDE_BOTTOM, /* dropping down */
12069 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12071 int drop_element = (player->inventory_size > 0 ?
12072 player->inventory_element[player->inventory_size - 1] :
12073 player->inventory_infinite_element != EL_UNDEFINED ?
12074 player->inventory_infinite_element :
12075 player->dynabombs_left > 0 ?
12076 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12079 if (IS_THROWABLE(drop_element))
12081 dropx += GET_DX_FROM_DIR(drop_direction);
12082 dropy += GET_DY_FROM_DIR(drop_direction);
12084 if (!IN_LEV_FIELD(dropx, dropy))
12088 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12089 new_element = drop_element; /* default: no change when dropping */
12091 /* check if player is active, not moving and ready to drop */
12092 if (!player->active || player->MovPos || player->drop_delay > 0)
12095 /* check if player has anything that can be dropped */
12097 if (new_element == EL_UNDEFINED)
12100 if (player->inventory_size == 0 &&
12101 player->inventory_infinite_element == EL_UNDEFINED &&
12102 player->dynabombs_left == 0)
12106 /* check if anything can be dropped at the current position */
12107 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12110 /* collected custom elements can only be dropped on empty fields */
12112 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12115 if (player->inventory_size > 0 &&
12116 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12117 && old_element != EL_EMPTY)
12121 if (old_element != EL_EMPTY)
12122 Back[dropx][dropy] = old_element; /* store old element on this field */
12124 ResetGfxAnimation(dropx, dropy);
12125 ResetRandomAnimationValue(dropx, dropy);
12127 if (player->inventory_size > 0 ||
12128 player->inventory_infinite_element != EL_UNDEFINED)
12130 if (player->inventory_size > 0)
12132 player->inventory_size--;
12135 new_element = player->inventory_element[player->inventory_size];
12138 DrawGameValue_Dynamite(local_player->inventory_size);
12140 if (new_element == EL_DYNAMITE)
12141 new_element = EL_DYNAMITE_ACTIVE;
12142 else if (new_element == EL_SP_DISK_RED)
12143 new_element = EL_SP_DISK_RED_ACTIVE;
12146 Feld[dropx][dropy] = new_element;
12148 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12149 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12150 el2img(Feld[dropx][dropy]), 0);
12152 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12155 /* needed if previous element just changed to "empty" in the last frame */
12156 Changed[dropx][dropy] = 0; /* allow another change */
12160 /* !!! TEST ONLY !!! */
12161 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12162 player->index_bit, drop_side);
12163 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12164 CE_OTHER_GETS_DROPPED,
12165 player->index_bit, drop_side);
12167 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12168 CE_OTHER_GETS_DROPPED,
12169 player->index_bit, drop_side);
12170 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12171 player->index_bit, drop_side);
12174 TestIfElementTouchesCustomElement(dropx, dropy);
12176 else /* player is dropping a dyna bomb */
12178 player->dynabombs_left--;
12181 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12184 Feld[dropx][dropy] = new_element;
12186 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12187 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12188 el2img(Feld[dropx][dropy]), 0);
12190 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12197 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12200 InitField_WithBug1(dropx, dropy, FALSE);
12202 InitField(dropx, dropy, FALSE);
12203 if (CAN_MOVE(Feld[dropx][dropy]))
12204 InitMovDir(dropx, dropy);
12208 new_element = Feld[dropx][dropy]; /* element might have changed */
12210 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12211 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12214 int move_stepsize = element_info[new_element].move_stepsize;
12216 int move_direction, nextx, nexty;
12218 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12219 MovDir[dropx][dropy] = drop_direction;
12221 move_direction = MovDir[dropx][dropy];
12222 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12223 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12226 Changed[dropx][dropy] = 0; /* allow another change */
12227 CheckCollision[dropx][dropy] = 2;
12230 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12233 WasJustMoving[dropx][dropy] = 3;
12236 InitMovingField(dropx, dropy, move_direction);
12237 ContinueMoving(dropx, dropy);
12242 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12245 Changed[dropx][dropy] = 0; /* allow another change */
12248 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12250 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12251 CE_HITTING_SOMETHING, move_direction);
12259 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12264 player->drop_delay = 8 + 8 + 8;
12268 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12273 player->is_dropping = TRUE;
12279 /* ------------------------------------------------------------------------- */
12280 /* game sound playing functions */
12281 /* ------------------------------------------------------------------------- */
12283 static int *loop_sound_frame = NULL;
12284 static int *loop_sound_volume = NULL;
12286 void InitPlayLevelSound()
12288 int num_sounds = getSoundListSize();
12290 checked_free(loop_sound_frame);
12291 checked_free(loop_sound_volume);
12293 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12294 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12297 static void PlayLevelSound(int x, int y, int nr)
12299 int sx = SCREENX(x), sy = SCREENY(y);
12300 int volume, stereo_position;
12301 int max_distance = 8;
12302 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12304 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12305 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12308 if (!IN_LEV_FIELD(x, y) ||
12309 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12310 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12313 volume = SOUND_MAX_VOLUME;
12315 if (!IN_SCR_FIELD(sx, sy))
12317 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12318 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12320 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12323 stereo_position = (SOUND_MAX_LEFT +
12324 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12325 (SCR_FIELDX + 2 * max_distance));
12327 if (IS_LOOP_SOUND(nr))
12329 /* This assures that quieter loop sounds do not overwrite louder ones,
12330 while restarting sound volume comparison with each new game frame. */
12332 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12335 loop_sound_volume[nr] = volume;
12336 loop_sound_frame[nr] = FrameCounter;
12339 PlaySoundExt(nr, volume, stereo_position, type);
12342 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12344 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12345 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12346 y < LEVELY(BY1) ? LEVELY(BY1) :
12347 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12351 static void PlayLevelSoundAction(int x, int y, int action)
12353 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12356 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12358 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12360 if (sound_effect != SND_UNDEFINED)
12361 PlayLevelSound(x, y, sound_effect);
12364 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12367 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12369 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12370 PlayLevelSound(x, y, sound_effect);
12373 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12375 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12377 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12378 PlayLevelSound(x, y, sound_effect);
12381 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12383 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12385 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12386 StopSound(sound_effect);
12389 static void PlayLevelMusic()
12391 if (levelset.music[level_nr] != MUS_UNDEFINED)
12392 PlayMusic(levelset.music[level_nr]); /* from config file */
12394 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12397 void PlayLevelSound_EM(int x, int y, int element, int action)
12399 PlayLevelSoundElementAction(x, y, element, action);
12402 void RaiseScore(int value)
12404 local_player->score += value;
12406 DrawGameValue_Score(local_player->score);
12409 void RaiseScoreElement(int element)
12414 case EL_BD_DIAMOND:
12415 case EL_EMERALD_YELLOW:
12416 case EL_EMERALD_RED:
12417 case EL_EMERALD_PURPLE:
12418 case EL_SP_INFOTRON:
12419 RaiseScore(level.score[SC_EMERALD]);
12422 RaiseScore(level.score[SC_DIAMOND]);
12425 RaiseScore(level.score[SC_CRYSTAL]);
12428 RaiseScore(level.score[SC_PEARL]);
12431 case EL_BD_BUTTERFLY:
12432 case EL_SP_ELECTRON:
12433 RaiseScore(level.score[SC_BUG]);
12436 case EL_BD_FIREFLY:
12437 case EL_SP_SNIKSNAK:
12438 RaiseScore(level.score[SC_SPACESHIP]);
12441 case EL_DARK_YAMYAM:
12442 RaiseScore(level.score[SC_YAMYAM]);
12445 RaiseScore(level.score[SC_ROBOT]);
12448 RaiseScore(level.score[SC_PACMAN]);
12451 RaiseScore(level.score[SC_NUT]);
12454 case EL_SP_DISK_RED:
12455 case EL_DYNABOMB_INCREASE_NUMBER:
12456 case EL_DYNABOMB_INCREASE_SIZE:
12457 case EL_DYNABOMB_INCREASE_POWER:
12458 RaiseScore(level.score[SC_DYNAMITE]);
12460 case EL_SHIELD_NORMAL:
12461 case EL_SHIELD_DEADLY:
12462 RaiseScore(level.score[SC_SHIELD]);
12464 case EL_EXTRA_TIME:
12465 RaiseScore(level.score[SC_TIME_BONUS]);
12471 RaiseScore(level.score[SC_KEY]);
12474 RaiseScore(element_info[element].collect_score);
12479 void RequestQuitGame(boolean ask_if_really_quit)
12481 if (AllPlayersGone ||
12482 !ask_if_really_quit ||
12483 level_editor_test_game ||
12484 Request("Do you really want to quit the game ?",
12485 REQ_ASK | REQ_STAY_CLOSED))
12487 #if defined(NETWORK_AVALIABLE)
12488 if (options.network)
12489 SendToServer_StopPlaying();
12493 game_status = GAME_MODE_MAIN;
12501 if (tape.playing && tape.deactivate_display)
12502 TapeDeactivateDisplayOff(TRUE);
12505 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12508 if (tape.playing && tape.deactivate_display)
12509 TapeDeactivateDisplayOn();
12516 /* ---------- new game button stuff ---------------------------------------- */
12518 /* graphic position values for game buttons */
12519 #define GAME_BUTTON_XSIZE 30
12520 #define GAME_BUTTON_YSIZE 30
12521 #define GAME_BUTTON_XPOS 5
12522 #define GAME_BUTTON_YPOS 215
12523 #define SOUND_BUTTON_XPOS 5
12524 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12526 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12527 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12528 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12529 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12530 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12531 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12538 } gamebutton_info[NUM_GAME_BUTTONS] =
12541 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12546 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12547 GAME_CTRL_ID_PAUSE,
12551 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12556 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12557 SOUND_CTRL_ID_MUSIC,
12558 "background music on/off"
12561 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12562 SOUND_CTRL_ID_LOOPS,
12563 "sound loops on/off"
12566 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12567 SOUND_CTRL_ID_SIMPLE,
12568 "normal sounds on/off"
12572 void CreateGameButtons()
12576 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12578 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12579 struct GadgetInfo *gi;
12582 unsigned long event_mask;
12583 int gd_xoffset, gd_yoffset;
12584 int gd_x1, gd_x2, gd_y1, gd_y2;
12587 gd_xoffset = gamebutton_info[i].x;
12588 gd_yoffset = gamebutton_info[i].y;
12589 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12590 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12592 if (id == GAME_CTRL_ID_STOP ||
12593 id == GAME_CTRL_ID_PAUSE ||
12594 id == GAME_CTRL_ID_PLAY)
12596 button_type = GD_TYPE_NORMAL_BUTTON;
12598 event_mask = GD_EVENT_RELEASED;
12599 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12600 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12604 button_type = GD_TYPE_CHECK_BUTTON;
12606 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12607 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12608 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12609 event_mask = GD_EVENT_PRESSED;
12610 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12611 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12614 gi = CreateGadget(GDI_CUSTOM_ID, id,
12615 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12616 GDI_X, DX + gd_xoffset,
12617 GDI_Y, DY + gd_yoffset,
12618 GDI_WIDTH, GAME_BUTTON_XSIZE,
12619 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12620 GDI_TYPE, button_type,
12621 GDI_STATE, GD_BUTTON_UNPRESSED,
12622 GDI_CHECKED, checked,
12623 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12624 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12625 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12626 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12627 GDI_EVENT_MASK, event_mask,
12628 GDI_CALLBACK_ACTION, HandleGameButtons,
12632 Error(ERR_EXIT, "cannot create gadget");
12634 game_gadget[id] = gi;
12638 void FreeGameButtons()
12642 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12643 FreeGadget(game_gadget[i]);
12646 static void MapGameButtons()
12650 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12651 MapGadget(game_gadget[i]);
12654 void UnmapGameButtons()
12658 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12659 UnmapGadget(game_gadget[i]);
12662 static void HandleGameButtons(struct GadgetInfo *gi)
12664 int id = gi->custom_id;
12666 if (game_status != GAME_MODE_PLAYING)
12671 case GAME_CTRL_ID_STOP:
12672 RequestQuitGame(TRUE);
12675 case GAME_CTRL_ID_PAUSE:
12676 if (options.network)
12678 #if defined(NETWORK_AVALIABLE)
12680 SendToServer_ContinuePlaying();
12682 SendToServer_PausePlaying();
12686 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12689 case GAME_CTRL_ID_PLAY:
12692 #if defined(NETWORK_AVALIABLE)
12693 if (options.network)
12694 SendToServer_ContinuePlaying();
12698 tape.pausing = FALSE;
12699 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12704 case SOUND_CTRL_ID_MUSIC:
12705 if (setup.sound_music)
12707 setup.sound_music = FALSE;
12710 else if (audio.music_available)
12712 setup.sound = setup.sound_music = TRUE;
12714 SetAudioMode(setup.sound);
12720 case SOUND_CTRL_ID_LOOPS:
12721 if (setup.sound_loops)
12722 setup.sound_loops = FALSE;
12723 else if (audio.loops_available)
12725 setup.sound = setup.sound_loops = TRUE;
12726 SetAudioMode(setup.sound);
12730 case SOUND_CTRL_ID_SIMPLE:
12731 if (setup.sound_simple)
12732 setup.sound_simple = FALSE;
12733 else if (audio.sound_available)
12735 setup.sound = setup.sound_simple = TRUE;
12736 SetAudioMode(setup.sound);