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)
37 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
38 #define USE_NEW_RANDOMIZE (TRUE * USE_NEW_STUFF * 1)
40 #define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
41 #define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
43 #define USE_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
45 #define USE_BLOCK_DELAY_BUGFIX (TRUE * USE_NEW_STUFF * 1)
47 #define USE_GRAVITY_BUGFIX_NEW (TRUE * USE_NEW_STUFF * 1)
48 #define USE_GRAVITY_BUGFIX_OLD (TRUE * USE_NEW_STUFF * 0)
56 /* for MovePlayer() */
57 #define MF_NO_ACTION 0
61 /* for ScrollPlayer() */
63 #define SCROLL_GO_ON 1
66 #define EX_PHASE_START 0
67 #define EX_TYPE_NONE 0
68 #define EX_TYPE_NORMAL (1 << 0)
69 #define EX_TYPE_CENTER (1 << 1)
70 #define EX_TYPE_BORDER (1 << 2)
71 #define EX_TYPE_CROSS (1 << 3)
72 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
74 /* special positions in the game control window (relative to control window) */
77 #define XX_EMERALDS 29
78 #define YY_EMERALDS 54
79 #define XX_DYNAMITE 29
80 #define YY_DYNAMITE 89
89 /* special positions in the game control window (relative to main window) */
90 #define DX_LEVEL (DX + XX_LEVEL)
91 #define DY_LEVEL (DY + YY_LEVEL)
92 #define DX_EMERALDS (DX + XX_EMERALDS)
93 #define DY_EMERALDS (DY + YY_EMERALDS)
94 #define DX_DYNAMITE (DX + XX_DYNAMITE)
95 #define DY_DYNAMITE (DY + YY_DYNAMITE)
96 #define DX_KEYS (DX + XX_KEYS)
97 #define DY_KEYS (DY + YY_KEYS)
98 #define DX_SCORE (DX + XX_SCORE)
99 #define DY_SCORE (DY + YY_SCORE)
100 #define DX_TIME1 (DX + XX_TIME1)
101 #define DX_TIME2 (DX + XX_TIME2)
102 #define DY_TIME (DY + YY_TIME)
104 /* values for initial player move delay (initial delay counter value) */
105 #define INITIAL_MOVE_DELAY_OFF -1
106 #define INITIAL_MOVE_DELAY_ON 0
108 /* values for player movement speed (which is in fact a delay value) */
109 #define MOVE_DELAY_NORMAL_SPEED 8
110 #define MOVE_DELAY_HIGH_SPEED 4
112 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
113 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
114 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
115 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
117 /* values for other actions */
118 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
120 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
121 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
123 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
125 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
126 RND(element_info[e].push_delay_random))
127 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
128 RND(element_info[e].drop_delay_random))
129 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
130 RND(element_info[e].move_delay_random))
131 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
132 (element_info[e].move_delay_random))
134 #define GET_TARGET_ELEMENT(e, ch) \
135 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
136 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
138 #define GET_VALID_PLAYER_ELEMENT(e) \
139 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
141 #define CAN_GROW_INTO(e) \
142 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
144 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
145 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
149 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
150 (CAN_MOVE_INTO_ACID(e) && \
151 Feld[x][y] == EL_ACID) || \
154 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
155 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
156 (CAN_MOVE_INTO_ACID(e) && \
157 Feld[x][y] == EL_ACID) || \
160 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
161 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
163 (CAN_MOVE_INTO_ACID(e) && \
164 Feld[x][y] == EL_ACID) || \
165 (DONT_COLLIDE_WITH(e) && \
167 !PLAYER_ENEMY_PROTECTED(x, y))))
170 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
171 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
173 (DONT_COLLIDE_WITH(e) && \
175 !PLAYER_ENEMY_PROTECTED(x, y))))
178 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
179 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
182 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
185 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
190 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
193 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
198 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
201 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
204 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
207 #define PIG_CAN_ENTER_FIELD(e, x, y) \
208 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
210 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
211 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
212 IS_FOOD_PENGUIN(Feld[x][y])))
213 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
214 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
216 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
217 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
219 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
220 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
224 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
226 (CAN_MOVE_INTO_ACID(e) && \
227 Feld[x][y] == EL_ACID) || \
228 Feld[x][y] == EL_DIAMOND))
230 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
231 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
232 (CAN_MOVE_INTO_ACID(e) && \
233 Feld[x][y] == EL_ACID) || \
234 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
236 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
237 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
238 (CAN_MOVE_INTO_ACID(e) && \
239 Feld[x][y] == EL_ACID) || \
240 IS_AMOEBOID(Feld[x][y])))
242 #define PIG_CAN_ENTER_FIELD(e, x, y) \
243 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
244 (CAN_MOVE_INTO_ACID(e) && \
245 Feld[x][y] == EL_ACID) || \
246 IS_FOOD_PIG(Feld[x][y])))
248 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
249 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
250 (CAN_MOVE_INTO_ACID(e) && \
251 Feld[x][y] == EL_ACID) || \
252 IS_FOOD_PENGUIN(Feld[x][y]) || \
253 Feld[x][y] == EL_EXIT_OPEN))
255 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
256 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
257 (CAN_MOVE_INTO_ACID(e) && \
258 Feld[x][y] == EL_ACID)))
260 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
261 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
262 (CAN_MOVE_INTO_ACID(e) && \
263 Feld[x][y] == EL_ACID) || \
266 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
267 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
268 (CAN_MOVE_INTO_ACID(e) && \
269 Feld[x][y] == EL_ACID)))
273 #define GROUP_NR(e) ((e) - EL_GROUP_START)
274 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
275 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
276 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
278 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
279 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
282 #define CE_ENTER_FIELD_COND(e, x, y) \
283 (!IS_PLAYER(x, y) && \
284 (Feld[x][y] == EL_ACID || \
285 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
287 #define CE_ENTER_FIELD_COND(e, x, y) \
288 (!IS_PLAYER(x, y) && \
289 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
292 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
293 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
295 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
296 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
298 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
299 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
300 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
301 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
303 /* game button identifiers */
304 #define GAME_CTRL_ID_STOP 0
305 #define GAME_CTRL_ID_PAUSE 1
306 #define GAME_CTRL_ID_PLAY 2
307 #define SOUND_CTRL_ID_MUSIC 3
308 #define SOUND_CTRL_ID_LOOPS 4
309 #define SOUND_CTRL_ID_SIMPLE 5
311 #define NUM_GAME_BUTTONS 6
314 /* forward declaration for internal use */
316 static void AdvanceFrameAndPlayerCounters(int);
318 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
319 static boolean MovePlayer(struct PlayerInfo *, int, int);
320 static void ScrollPlayer(struct PlayerInfo *, int);
321 static void ScrollScreen(struct PlayerInfo *, int);
323 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
325 static void InitBeltMovement(void);
326 static void CloseAllOpenTimegates(void);
327 static void CheckGravityMovement(struct PlayerInfo *);
328 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
329 static void KillHeroUnlessEnemyProtected(int, int);
330 static void KillHeroUnlessExplosionProtected(int, int);
332 static void TestIfPlayerTouchesCustomElement(int, int);
333 static void TestIfElementTouchesCustomElement(int, int);
334 static void TestIfElementHitsCustomElement(int, int, int);
336 static void TestIfElementSmashesCustomElement(int, int, int);
339 static void ChangeElement(int, int, int);
341 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
342 #define CheckTriggeredElementChange(x, y, e, ev) \
343 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
345 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
346 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
347 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
348 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
349 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
350 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
353 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
354 #define CheckElementChange(x, y, e, te, ev) \
355 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
356 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
357 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
358 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
359 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
360 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
361 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
363 static void PlayLevelSound(int, int, int);
364 static void PlayLevelSoundNearest(int, int, int);
365 static void PlayLevelSoundAction(int, int, int);
366 static void PlayLevelSoundElementAction(int, int, int, int);
367 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
368 static void PlayLevelSoundActionIfLoop(int, int, int);
369 static void StopLevelSoundActionIfLoop(int, int, int);
370 static void PlayLevelMusic();
372 static void MapGameButtons();
373 static void HandleGameButtons(struct GadgetInfo *);
375 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
378 /* ------------------------------------------------------------------------- */
379 /* definition of elements that automatically change to other elements after */
380 /* a specified time, eventually calling a function when changing */
381 /* ------------------------------------------------------------------------- */
383 /* forward declaration for changer functions */
384 static void InitBuggyBase(int x, int y);
385 static void WarnBuggyBase(int x, int y);
387 static void InitTrap(int x, int y);
388 static void ActivateTrap(int x, int y);
389 static void ChangeActiveTrap(int x, int y);
391 static void InitRobotWheel(int x, int y);
392 static void RunRobotWheel(int x, int y);
393 static void StopRobotWheel(int x, int y);
395 static void InitTimegateWheel(int x, int y);
396 static void RunTimegateWheel(int x, int y);
398 struct ChangingElementInfo
403 void (*pre_change_function)(int x, int y);
404 void (*change_function)(int x, int y);
405 void (*post_change_function)(int x, int y);
408 static struct ChangingElementInfo change_delay_list[] =
459 EL_SWITCHGATE_OPENING,
467 EL_SWITCHGATE_CLOSING,
468 EL_SWITCHGATE_CLOSED,
500 EL_ACID_SPLASH_RIGHT,
509 EL_SP_BUGGY_BASE_ACTIVATING,
516 EL_SP_BUGGY_BASE_ACTIVATING,
517 EL_SP_BUGGY_BASE_ACTIVE,
524 EL_SP_BUGGY_BASE_ACTIVE,
548 EL_ROBOT_WHEEL_ACTIVE,
556 EL_TIMEGATE_SWITCH_ACTIVE,
577 int push_delay_fixed, push_delay_random;
582 { EL_BALLOON, 0, 0 },
584 { EL_SOKOBAN_OBJECT, 2, 0 },
585 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
586 { EL_SATELLITE, 2, 0 },
587 { EL_SP_DISK_YELLOW, 2, 0 },
589 { EL_UNDEFINED, 0, 0 },
597 move_stepsize_list[] =
599 { EL_AMOEBA_DROP, 2 },
600 { EL_AMOEBA_DROPPING, 2 },
601 { EL_QUICKSAND_FILLING, 1 },
602 { EL_QUICKSAND_EMPTYING, 1 },
603 { EL_MAGIC_WALL_FILLING, 2 },
604 { EL_BD_MAGIC_WALL_FILLING, 2 },
605 { EL_MAGIC_WALL_EMPTYING, 2 },
606 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
616 collect_count_list[] =
619 { EL_BD_DIAMOND, 1 },
620 { EL_EMERALD_YELLOW, 1 },
621 { EL_EMERALD_RED, 1 },
622 { EL_EMERALD_PURPLE, 1 },
624 { EL_SP_INFOTRON, 1 },
636 access_direction_list[] =
638 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
639 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
640 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
641 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
642 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
643 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
644 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
645 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
646 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
647 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
648 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
650 { EL_SP_PORT_LEFT, MV_RIGHT },
651 { EL_SP_PORT_RIGHT, MV_LEFT },
652 { EL_SP_PORT_UP, MV_DOWN },
653 { EL_SP_PORT_DOWN, MV_UP },
654 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
655 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
656 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
657 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
658 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
659 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
660 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
661 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
662 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
663 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
664 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
665 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
666 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
667 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
668 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
670 { EL_UNDEFINED, MV_NO_MOVING }
673 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
675 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
676 CH_EVENT_BIT(CE_DELAY))
677 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
678 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
679 IS_JUST_CHANGING(x, y))
681 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
684 void GetPlayerConfig()
686 if (!audio.sound_available)
687 setup.sound_simple = FALSE;
689 if (!audio.loops_available)
690 setup.sound_loops = FALSE;
692 if (!audio.music_available)
693 setup.sound_music = FALSE;
695 if (!video.fullscreen_available)
696 setup.fullscreen = FALSE;
698 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
700 SetAudioMode(setup.sound);
704 static int getBeltNrFromBeltElement(int element)
706 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
707 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
708 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
711 static int getBeltNrFromBeltActiveElement(int element)
713 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
714 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
715 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
718 static int getBeltNrFromBeltSwitchElement(int element)
720 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
721 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
722 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
725 static int getBeltDirNrFromBeltSwitchElement(int element)
727 static int belt_base_element[4] =
729 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
730 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
731 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
732 EL_CONVEYOR_BELT_4_SWITCH_LEFT
735 int belt_nr = getBeltNrFromBeltSwitchElement(element);
736 int belt_dir_nr = element - belt_base_element[belt_nr];
738 return (belt_dir_nr % 3);
741 static int getBeltDirFromBeltSwitchElement(int element)
743 static int belt_move_dir[3] =
750 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
752 return belt_move_dir[belt_dir_nr];
755 static void InitPlayerField(int x, int y, int element, boolean init_game)
757 if (element == EL_SP_MURPHY)
761 if (stored_player[0].present)
763 Feld[x][y] = EL_SP_MURPHY_CLONE;
769 stored_player[0].use_murphy_graphic = TRUE;
772 Feld[x][y] = EL_PLAYER_1;
778 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
779 int jx = player->jx, jy = player->jy;
781 player->present = TRUE;
783 player->block_last_field = (element == EL_SP_MURPHY ?
784 level.sp_block_last_field :
785 level.block_last_field);
787 #if USE_NEW_BLOCK_STYLE
790 /* ---------- initialize player's last field block delay --------------- */
792 /* always start with reliable default value (no adjustment needed) */
793 player->block_delay_adjustment = 0;
795 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
796 if (player->block_last_field && element == EL_SP_MURPHY)
797 player->block_delay_adjustment = 1;
799 /* special case 2: in game engines before 3.1.1, blocking was different */
800 if (game.use_block_last_field_bug)
801 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
804 /* blocking the last field when moving was corrected in version 3.1.1 */
805 if (game.use_block_last_field_bug)
807 /* even "not blocking" was blocking the last field for one frame */
808 level.block_delay = (level.block_last_field ? 7 : 1);
809 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
811 level.block_last_field = TRUE;
812 level.sp_block_last_field = TRUE;
816 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
817 level.block_delay = 8; /* when blocking, block 8 frames */
818 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
822 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
828 player->block_delay = (player->block_last_field ?
829 (element == EL_SP_MURPHY ?
830 level.sp_block_delay :
831 level.block_delay) : 0);
833 player->block_delay = (element == EL_SP_MURPHY ?
834 (player->block_last_field ? 7 : 1) :
835 (player->block_last_field ? 7 : 1));
841 printf("::: block_last_field == %d, block_delay = %d\n",
842 player->block_last_field, player->block_delay);
846 if (!options.network || player->connected)
848 player->active = TRUE;
850 /* remove potentially duplicate players */
851 if (StorePlayer[jx][jy] == Feld[x][y])
852 StorePlayer[jx][jy] = 0;
854 StorePlayer[x][y] = Feld[x][y];
858 printf("Player %d activated.\n", player->element_nr);
859 printf("[Local player is %d and currently %s.]\n",
860 local_player->element_nr,
861 local_player->active ? "active" : "not active");
865 Feld[x][y] = EL_EMPTY;
867 player->jx = player->last_jx = x;
868 player->jy = player->last_jy = y;
872 static void InitField(int x, int y, boolean init_game)
874 int element = Feld[x][y];
883 InitPlayerField(x, y, element, init_game);
886 case EL_SOKOBAN_FIELD_PLAYER:
887 element = Feld[x][y] = EL_PLAYER_1;
888 InitField(x, y, init_game);
890 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
891 InitField(x, y, init_game);
894 case EL_SOKOBAN_FIELD_EMPTY:
895 local_player->sokobanfields_still_needed++;
899 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
900 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
901 else if (x > 0 && Feld[x-1][y] == EL_ACID)
902 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
903 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
904 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
905 else if (y > 0 && Feld[x][y-1] == EL_ACID)
906 Feld[x][y] = EL_ACID_POOL_BOTTOM;
907 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
908 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
916 case EL_SPACESHIP_RIGHT:
917 case EL_SPACESHIP_UP:
918 case EL_SPACESHIP_LEFT:
919 case EL_SPACESHIP_DOWN:
921 case EL_BD_BUTTERFLY_RIGHT:
922 case EL_BD_BUTTERFLY_UP:
923 case EL_BD_BUTTERFLY_LEFT:
924 case EL_BD_BUTTERFLY_DOWN:
925 case EL_BD_BUTTERFLY:
926 case EL_BD_FIREFLY_RIGHT:
927 case EL_BD_FIREFLY_UP:
928 case EL_BD_FIREFLY_LEFT:
929 case EL_BD_FIREFLY_DOWN:
931 case EL_PACMAN_RIGHT:
955 if (y == lev_fieldy - 1)
957 Feld[x][y] = EL_AMOEBA_GROWING;
958 Store[x][y] = EL_AMOEBA_WET;
962 case EL_DYNAMITE_ACTIVE:
963 case EL_SP_DISK_RED_ACTIVE:
964 case EL_DYNABOMB_PLAYER_1_ACTIVE:
965 case EL_DYNABOMB_PLAYER_2_ACTIVE:
966 case EL_DYNABOMB_PLAYER_3_ACTIVE:
967 case EL_DYNABOMB_PLAYER_4_ACTIVE:
972 local_player->lights_still_needed++;
976 local_player->friends_still_needed++;
981 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
986 Feld[x][y] = EL_EMPTY;
991 case EL_EM_KEY_1_FILE:
992 Feld[x][y] = EL_EM_KEY_1;
994 case EL_EM_KEY_2_FILE:
995 Feld[x][y] = EL_EM_KEY_2;
997 case EL_EM_KEY_3_FILE:
998 Feld[x][y] = EL_EM_KEY_3;
1000 case EL_EM_KEY_4_FILE:
1001 Feld[x][y] = EL_EM_KEY_4;
1005 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1006 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1007 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1008 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1009 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1010 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1011 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1012 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1013 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1014 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1015 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1016 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1019 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1020 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1021 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1023 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1025 game.belt_dir[belt_nr] = belt_dir;
1026 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1028 else /* more than one switch -- set it like the first switch */
1030 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1035 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1037 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1040 case EL_LIGHT_SWITCH_ACTIVE:
1042 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1046 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1048 else if (IS_GROUP_ELEMENT(element))
1050 struct ElementGroupInfo *group = element_info[element].group;
1051 int last_anim_random_frame = gfx.anim_random_frame;
1054 if (group->choice_mode == ANIM_RANDOM)
1055 gfx.anim_random_frame = RND(group->num_elements_resolved);
1057 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1058 group->choice_mode, 0,
1061 if (group->choice_mode == ANIM_RANDOM)
1062 gfx.anim_random_frame = last_anim_random_frame;
1064 group->choice_pos++;
1066 Feld[x][y] = group->element_resolved[element_pos];
1068 InitField(x, y, init_game);
1074 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1076 InitField(x, y, init_game);
1078 /* not needed to call InitMovDir() -- already done by InitField()! */
1079 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1080 CAN_MOVE(Feld[x][y]))
1084 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1086 int old_element = Feld[x][y];
1088 InitField(x, y, init_game);
1090 /* not needed to call InitMovDir() -- already done by InitField()! */
1091 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1092 CAN_MOVE(old_element) &&
1093 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1096 /* this case is in fact a combination of not less than three bugs:
1097 first, it calls InitMovDir() for elements that can move, although this is
1098 already done by InitField(); then, it checks the element that was at this
1099 field _before_ the call to InitField() (which can change it); lastly, it
1100 was not called for "mole with direction" elements, which were treated as
1101 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1105 inline void DrawGameValue_Emeralds(int value)
1107 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1110 inline void DrawGameValue_Dynamite(int value)
1112 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1115 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1119 /* currently only 4 of 8 possible keys are displayed */
1120 for (i = 0; i < STD_NUM_KEYS; i++)
1122 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1123 el2edimg(EL_KEY_1 + i));
1126 inline void DrawGameValue_Score(int value)
1128 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1131 inline void DrawGameValue_Time(int value)
1134 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1136 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1139 inline void DrawGameValue_Level(int value)
1142 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1145 /* misuse area for displaying emeralds to draw bigger level number */
1146 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1147 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1149 /* now copy it to the area for displaying level number */
1150 BlitBitmap(drawto, drawto,
1151 DX_EMERALDS, DY_EMERALDS + 1,
1152 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1153 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1154 DX_LEVEL - 1, DY_LEVEL + 1);
1156 /* restore the area for displaying emeralds */
1157 DrawGameValue_Emeralds(local_player->gems_still_needed);
1159 /* yes, this is all really ugly :-) */
1163 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1166 int key[MAX_NUM_KEYS];
1169 for (i = 0; i < MAX_NUM_KEYS; i++)
1170 key[i] = key_bits & (1 << i);
1172 DrawGameValue_Level(level_nr);
1174 DrawGameValue_Emeralds(emeralds);
1175 DrawGameValue_Dynamite(dynamite);
1176 DrawGameValue_Score(score);
1177 DrawGameValue_Time(time);
1179 DrawGameValue_Keys(key);
1182 void DrawGameDoorValues()
1186 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1188 DrawGameDoorValues_EM();
1193 DrawGameValue_Level(level_nr);
1195 DrawGameValue_Emeralds(local_player->gems_still_needed);
1196 DrawGameValue_Dynamite(local_player->inventory_size);
1197 DrawGameValue_Score(local_player->score);
1198 DrawGameValue_Time(TimeLeft);
1200 for (i = 0; i < MAX_PLAYERS; i++)
1201 DrawGameValue_Keys(stored_player[i].key);
1204 static void resolve_group_element(int group_element, int recursion_depth)
1206 static int group_nr;
1207 static struct ElementGroupInfo *group;
1208 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1211 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1213 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1214 group_element - EL_GROUP_START + 1);
1216 /* replace element which caused too deep recursion by question mark */
1217 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1222 if (recursion_depth == 0) /* initialization */
1224 group = element_info[group_element].group;
1225 group_nr = group_element - EL_GROUP_START;
1227 group->num_elements_resolved = 0;
1228 group->choice_pos = 0;
1231 for (i = 0; i < actual_group->num_elements; i++)
1233 int element = actual_group->element[i];
1235 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1238 if (IS_GROUP_ELEMENT(element))
1239 resolve_group_element(element, recursion_depth + 1);
1242 group->element_resolved[group->num_elements_resolved++] = element;
1243 element_info[element].in_group[group_nr] = TRUE;
1248 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1250 printf("::: group %d: %d resolved elements\n",
1251 group_element - EL_GROUP_START, group->num_elements_resolved);
1252 for (i = 0; i < group->num_elements_resolved; i++)
1253 printf("::: - %d ['%s']\n", group->element_resolved[i],
1254 element_info[group->element_resolved[i]].token_name);
1261 =============================================================================
1263 -----------------------------------------------------------------------------
1264 initialize game engine due to level / tape version number
1265 =============================================================================
1268 static void InitGameEngine()
1272 /* set game engine from tape file when re-playing, else from level file */
1273 game.engine_version = (tape.playing ? tape.engine_version :
1274 level.game_version);
1276 /* ---------------------------------------------------------------------- */
1277 /* set flags for bugs and changes according to active game engine version */
1278 /* ---------------------------------------------------------------------- */
1281 Summary of bugfix/change:
1282 Fixed handling for custom elements that change when pushed by the player.
1284 Fixed/changed in version:
1288 Before 3.1.0, custom elements that "change when pushing" changed directly
1289 after the player started pushing them (until then handled in "DigField()").
1290 Since 3.1.0, these custom elements are not changed until the "pushing"
1291 move of the element is finished (now handled in "ContinueMoving()").
1293 Affected levels/tapes:
1294 The first condition is generally needed for all levels/tapes before version
1295 3.1.0, which might use the old behaviour before it was changed; known tapes
1296 that are affected are some tapes from the level set "Walpurgis Gardens" by
1298 The second condition is an exception from the above case and is needed for
1299 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1300 above (including some development versions of 3.1.0), but before it was
1301 known that this change would break tapes like the above and was fixed in
1302 3.1.1, so that the changed behaviour was active although the engine version
1303 while recording maybe was before 3.1.0. There is at least one tape that is
1304 affected by this exception, which is the tape for the one-level set "Bug
1305 Machine" by Juergen Bonhagen.
1308 game.use_change_when_pushing_bug =
1309 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1311 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1312 tape.game_version < VERSION_IDENT(3,1,1,0)));
1315 Summary of bugfix/change:
1316 Fixed handling for blocking the field the player leaves when moving.
1318 Fixed/changed in version:
1322 Before 3.1.1, when "block last field when moving" was enabled, the field
1323 the player is leaving when moving was blocked for the time of the move,
1324 and was directly unblocked afterwards. This resulted in the last field
1325 being blocked for exactly one less than the number of frames of one player
1326 move. Additionally, even when blocking was disabled, the last field was
1327 blocked for exactly one frame.
1328 Since 3.1.1, due to changes in player movement handling, the last field
1329 is not blocked at all when blocking is disabled. When blocking is enabled,
1330 the last field is blocked for exactly the number of frames of one player
1331 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1332 last field is blocked for exactly one more than the number of frames of
1335 Affected levels/tapes:
1336 (!!! yet to be determined -- probably many !!!)
1339 game.use_block_last_field_bug =
1340 (game.engine_version < VERSION_IDENT(3,1,1,0));
1342 /* ---------------------------------------------------------------------- */
1344 /* dynamically adjust element properties according to game engine version */
1345 InitElementPropertiesEngine(game.engine_version);
1348 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1349 printf(" tape version == %06d [%s] [file: %06d]\n",
1350 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1352 printf(" => game.engine_version == %06d\n", game.engine_version);
1355 /* ---------- recursively resolve group elements ------------------------- */
1357 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1358 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1359 element_info[i].in_group[j] = FALSE;
1361 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1362 resolve_group_element(EL_GROUP_START + i, 0);
1364 /* ---------- initialize player's initial move delay --------------------- */
1366 #if USE_NEW_MOVE_DELAY
1367 /* dynamically adjust player properties according to level information */
1368 game.initial_move_delay_value =
1369 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1371 /* dynamically adjust player properties according to game engine version */
1372 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1373 game.initial_move_delay_value : 0);
1375 /* dynamically adjust player properties according to game engine version */
1376 game.initial_move_delay =
1377 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1378 INITIAL_MOVE_DELAY_OFF);
1380 /* dynamically adjust player properties according to level information */
1381 game.initial_move_delay_value =
1382 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1385 /* ---------- initialize player's initial push delay --------------------- */
1387 /* dynamically adjust player properties according to game engine version */
1388 game.initial_push_delay_value =
1389 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1391 /* ---------- initialize changing elements ------------------------------- */
1393 /* initialize changing elements information */
1394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1396 struct ElementInfo *ei = &element_info[i];
1398 /* this pointer might have been changed in the level editor */
1399 ei->change = &ei->change_page[0];
1401 if (!IS_CUSTOM_ELEMENT(i))
1403 ei->change->target_element = EL_EMPTY_SPACE;
1404 ei->change->delay_fixed = 0;
1405 ei->change->delay_random = 0;
1406 ei->change->delay_frames = 1;
1409 ei->change_events = CE_BITMASK_DEFAULT;
1410 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1412 ei->event_page_nr[j] = 0;
1413 ei->event_page[j] = &ei->change_page[0];
1417 /* add changing elements from pre-defined list */
1418 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1420 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1421 struct ElementInfo *ei = &element_info[ch_delay->element];
1423 ei->change->target_element = ch_delay->target_element;
1424 ei->change->delay_fixed = ch_delay->change_delay;
1426 ei->change->pre_change_function = ch_delay->pre_change_function;
1427 ei->change->change_function = ch_delay->change_function;
1428 ei->change->post_change_function = ch_delay->post_change_function;
1430 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1433 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1438 /* add change events from custom element configuration */
1439 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1441 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1443 for (j = 0; j < ei->num_change_pages; j++)
1445 if (!ei->change_page[j].can_change)
1448 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1450 /* only add event page for the first page found with this event */
1451 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1452 !(ei->change_events & CH_EVENT_BIT(k)))
1454 ei->change_events |= CH_EVENT_BIT(k);
1455 ei->event_page_nr[k] = j;
1456 ei->event_page[k] = &ei->change_page[j];
1464 /* add change events from custom element configuration */
1465 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1467 int element = EL_CUSTOM_START + i;
1469 /* only add custom elements that change after fixed/random frame delay */
1470 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1471 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1475 /* ---------- initialize run-time trigger player and element ------------- */
1477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1479 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1481 for (j = 0; j < ei->num_change_pages; j++)
1483 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1484 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1488 /* ---------- initialize trigger events ---------------------------------- */
1490 /* initialize trigger events information */
1491 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1492 trigger_events[i] = EP_BITMASK_DEFAULT;
1495 /* add trigger events from element change event properties */
1496 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1498 struct ElementInfo *ei = &element_info[i];
1500 for (j = 0; j < ei->num_change_pages; j++)
1502 if (!ei->change_page[j].can_change)
1505 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1507 int trigger_element = ei->change_page[j].trigger_element;
1509 if (IS_GROUP_ELEMENT(trigger_element))
1511 struct ElementGroupInfo *group = element_info[trigger_element].group;
1513 for (k = 0; k < group->num_elements_resolved; k++)
1514 trigger_events[group->element_resolved[k]]
1515 |= ei->change_page[j].events;
1518 trigger_events[trigger_element] |= ei->change_page[j].events;
1523 /* add trigger events from element change event properties */
1524 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1525 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1526 trigger_events[element_info[i].change->trigger_element] |=
1527 element_info[i].change->events;
1530 /* ---------- initialize push delay -------------------------------------- */
1532 /* initialize push delay values to default */
1533 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1535 if (!IS_CUSTOM_ELEMENT(i))
1537 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1538 element_info[i].push_delay_random = game.default_push_delay_random;
1542 /* set push delay value for certain elements from pre-defined list */
1543 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1545 int e = push_delay_list[i].element;
1547 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1548 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1551 /* set push delay value for Supaplex elements for newer engine versions */
1552 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1554 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1556 if (IS_SP_ELEMENT(i))
1558 #if USE_NEW_MOVE_STYLE
1559 /* set SP push delay to just enough to push under a falling zonk */
1560 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1562 element_info[i].push_delay_fixed = delay;
1563 element_info[i].push_delay_random = 0;
1565 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1566 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1572 /* ---------- initialize move stepsize ----------------------------------- */
1574 /* initialize move stepsize values to default */
1575 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1576 if (!IS_CUSTOM_ELEMENT(i))
1577 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1579 /* set move stepsize value for certain elements from pre-defined list */
1580 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1582 int e = move_stepsize_list[i].element;
1584 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1588 /* ---------- initialize move dig/leave ---------------------------------- */
1590 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1592 element_info[i].can_leave_element = FALSE;
1593 element_info[i].can_leave_element_last = FALSE;
1597 /* ---------- initialize gem count --------------------------------------- */
1599 /* initialize gem count values for each element */
1600 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1601 if (!IS_CUSTOM_ELEMENT(i))
1602 element_info[i].collect_count = 0;
1604 /* add gem count values for all elements from pre-defined list */
1605 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1606 element_info[collect_count_list[i].element].collect_count =
1607 collect_count_list[i].count;
1609 /* ---------- initialize access direction -------------------------------- */
1611 /* initialize access direction values to default (access from every side) */
1612 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1613 if (!IS_CUSTOM_ELEMENT(i))
1614 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1616 /* set access direction value for certain elements from pre-defined list */
1617 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1618 element_info[access_direction_list[i].element].access_direction =
1619 access_direction_list[i].direction;
1624 =============================================================================
1626 -----------------------------------------------------------------------------
1627 initialize and start new game
1628 =============================================================================
1633 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1634 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1635 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1642 #if USE_NEW_AMOEBA_CODE
1643 printf("Using new amoeba code.\n");
1645 printf("Using old amoeba code.\n");
1650 /* don't play tapes over network */
1651 network_playing = (options.network && !tape.playing);
1653 for (i = 0; i < MAX_PLAYERS; i++)
1655 struct PlayerInfo *player = &stored_player[i];
1657 player->index_nr = i;
1658 player->index_bit = (1 << i);
1659 player->element_nr = EL_PLAYER_1 + i;
1661 player->present = FALSE;
1662 player->active = FALSE;
1665 player->effective_action = 0;
1666 player->programmed_action = 0;
1669 player->gems_still_needed = level.gems_needed;
1670 player->sokobanfields_still_needed = 0;
1671 player->lights_still_needed = 0;
1672 player->friends_still_needed = 0;
1674 for (j = 0; j < MAX_NUM_KEYS; j++)
1675 player->key[j] = FALSE;
1677 player->dynabomb_count = 0;
1678 player->dynabomb_size = 1;
1679 player->dynabombs_left = 0;
1680 player->dynabomb_xl = FALSE;
1682 player->MovDir = MV_NO_MOVING;
1685 player->GfxDir = MV_NO_MOVING;
1686 player->GfxAction = ACTION_DEFAULT;
1688 player->StepFrame = 0;
1690 player->use_murphy_graphic = FALSE;
1692 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1693 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1695 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1697 player->actual_frame_counter = 0;
1699 player->step_counter = 0;
1701 player->last_move_dir = MV_NO_MOVING;
1703 player->is_waiting = FALSE;
1704 player->is_moving = FALSE;
1705 player->is_auto_moving = FALSE;
1706 player->is_digging = FALSE;
1707 player->is_snapping = FALSE;
1708 player->is_collecting = FALSE;
1709 player->is_pushing = FALSE;
1710 player->is_switching = FALSE;
1711 player->is_dropping = FALSE;
1713 player->is_bored = FALSE;
1714 player->is_sleeping = FALSE;
1716 player->frame_counter_bored = -1;
1717 player->frame_counter_sleeping = -1;
1719 player->anim_delay_counter = 0;
1720 player->post_delay_counter = 0;
1722 player->action_waiting = ACTION_DEFAULT;
1723 player->last_action_waiting = ACTION_DEFAULT;
1724 player->special_action_bored = ACTION_DEFAULT;
1725 player->special_action_sleeping = ACTION_DEFAULT;
1727 player->num_special_action_bored = 0;
1728 player->num_special_action_sleeping = 0;
1730 /* determine number of special actions for bored and sleeping animation */
1731 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1733 boolean found = FALSE;
1735 for (k = 0; k < NUM_DIRECTIONS; k++)
1736 if (el_act_dir2img(player->element_nr, j, k) !=
1737 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1741 player->num_special_action_bored++;
1745 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1747 boolean found = FALSE;
1749 for (k = 0; k < NUM_DIRECTIONS; k++)
1750 if (el_act_dir2img(player->element_nr, j, k) !=
1751 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1755 player->num_special_action_sleeping++;
1760 player->switch_x = -1;
1761 player->switch_y = -1;
1763 player->show_envelope = 0;
1765 player->move_delay = game.initial_move_delay;
1766 player->move_delay_value = game.initial_move_delay_value;
1768 player->move_delay_reset_counter = 0;
1770 #if USE_NEW_PUSH_DELAY
1771 player->push_delay = -1; /* initialized when pushing starts */
1772 player->push_delay_value = game.initial_push_delay_value;
1774 player->push_delay = 0;
1775 player->push_delay_value = game.initial_push_delay_value;
1778 player->drop_delay = 0;
1780 player->last_jx = player->last_jy = 0;
1781 player->jx = player->jy = 0;
1783 player->shield_normal_time_left = 0;
1784 player->shield_deadly_time_left = 0;
1786 player->inventory_infinite_element = EL_UNDEFINED;
1787 player->inventory_size = 0;
1789 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1790 SnapField(player, 0, 0);
1792 player->LevelSolved = FALSE;
1793 player->GameOver = FALSE;
1796 network_player_action_received = FALSE;
1798 #if defined(NETWORK_AVALIABLE)
1799 /* initial null action */
1800 if (network_playing)
1801 SendToServer_MovePlayer(MV_NO_MOVING);
1810 TimeLeft = level.time;
1813 ScreenMovDir = MV_NO_MOVING;
1817 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1819 AllPlayersGone = FALSE;
1821 game.yamyam_content_nr = 0;
1822 game.magic_wall_active = FALSE;
1823 game.magic_wall_time_left = 0;
1824 game.light_time_left = 0;
1825 game.timegate_time_left = 0;
1826 game.switchgate_pos = 0;
1827 game.balloon_dir = MV_NO_MOVING;
1828 game.gravity = level.initial_gravity;
1829 game.explosions_delayed = TRUE;
1831 game.envelope_active = FALSE;
1833 for (i = 0; i < NUM_BELTS; i++)
1835 game.belt_dir[i] = MV_NO_MOVING;
1836 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1839 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1840 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1842 for (x = 0; x < lev_fieldx; x++)
1844 for (y = 0; y < lev_fieldy; y++)
1846 Feld[x][y] = level.field[x][y];
1847 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1848 ChangeDelay[x][y] = 0;
1849 ChangePage[x][y] = -1;
1850 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1852 WasJustMoving[x][y] = 0;
1853 WasJustFalling[x][y] = 0;
1854 CheckCollision[x][y] = 0;
1856 Pushed[x][y] = FALSE;
1858 Changed[x][y] = CE_BITMASK_DEFAULT;
1859 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1861 ExplodePhase[x][y] = 0;
1862 ExplodeDelay[x][y] = 0;
1863 ExplodeField[x][y] = EX_TYPE_NONE;
1865 RunnerVisit[x][y] = 0;
1866 PlayerVisit[x][y] = 0;
1869 GfxRandom[x][y] = INIT_GFX_RANDOM();
1870 GfxElement[x][y] = EL_UNDEFINED;
1871 GfxAction[x][y] = ACTION_DEFAULT;
1872 GfxDir[x][y] = MV_NO_MOVING;
1876 for (y = 0; y < lev_fieldy; y++)
1878 for (x = 0; x < lev_fieldx; x++)
1880 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1882 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1884 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1887 InitField(x, y, TRUE);
1893 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1894 emulate_sb ? EMU_SOKOBAN :
1895 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1897 /* initialize explosion and ignition delay */
1898 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1900 if (!IS_CUSTOM_ELEMENT(i))
1903 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1904 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1905 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1906 int last_phase = (num_phase + 1) * delay;
1907 int half_phase = (num_phase / 2) * delay;
1909 element_info[i].explosion_delay = last_phase - 1;
1910 element_info[i].ignition_delay = half_phase;
1913 if (i == EL_BLACK_ORB)
1914 element_info[i].ignition_delay = 0;
1916 if (i == EL_BLACK_ORB)
1917 element_info[i].ignition_delay = 1;
1922 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1923 element_info[i].explosion_delay = 1;
1925 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1926 element_info[i].ignition_delay = 1;
1930 /* correct non-moving belts to start moving left */
1931 for (i = 0; i < NUM_BELTS; i++)
1932 if (game.belt_dir[i] == MV_NO_MOVING)
1933 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1935 /* check if any connected player was not found in playfield */
1936 for (i = 0; i < MAX_PLAYERS; i++)
1938 struct PlayerInfo *player = &stored_player[i];
1940 if (player->connected && !player->present)
1942 for (j = 0; j < MAX_PLAYERS; j++)
1944 struct PlayerInfo *some_player = &stored_player[j];
1945 int jx = some_player->jx, jy = some_player->jy;
1947 /* assign first free player found that is present in the playfield */
1948 if (some_player->present && !some_player->connected)
1950 player->present = TRUE;
1951 player->active = TRUE;
1953 some_player->present = FALSE;
1954 some_player->active = FALSE;
1957 player->element_nr = some_player->element_nr;
1960 #if USE_NEW_BLOCK_STYLE
1961 player->block_last_field = some_player->block_last_field;
1962 player->block_delay_adjustment = some_player->block_delay_adjustment;
1965 StorePlayer[jx][jy] = player->element_nr;
1966 player->jx = player->last_jx = jx;
1967 player->jy = player->last_jy = jy;
1977 /* when playing a tape, eliminate all players which do not participate */
1979 for (i = 0; i < MAX_PLAYERS; i++)
1981 if (stored_player[i].active && !tape.player_participates[i])
1983 struct PlayerInfo *player = &stored_player[i];
1984 int jx = player->jx, jy = player->jy;
1986 player->active = FALSE;
1987 StorePlayer[jx][jy] = 0;
1988 Feld[jx][jy] = EL_EMPTY;
1992 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1994 /* when in single player mode, eliminate all but the first active player */
1996 for (i = 0; i < MAX_PLAYERS; i++)
1998 if (stored_player[i].active)
2000 for (j = i + 1; j < MAX_PLAYERS; j++)
2002 if (stored_player[j].active)
2004 struct PlayerInfo *player = &stored_player[j];
2005 int jx = player->jx, jy = player->jy;
2007 player->active = FALSE;
2008 player->present = FALSE;
2010 StorePlayer[jx][jy] = 0;
2011 Feld[jx][jy] = EL_EMPTY;
2018 /* when recording the game, store which players take part in the game */
2021 for (i = 0; i < MAX_PLAYERS; i++)
2022 if (stored_player[i].active)
2023 tape.player_participates[i] = TRUE;
2028 for (i = 0; i < MAX_PLAYERS; i++)
2030 struct PlayerInfo *player = &stored_player[i];
2032 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2037 if (local_player == player)
2038 printf("Player %d is local player.\n", i+1);
2042 if (BorderElement == EL_EMPTY)
2045 SBX_Right = lev_fieldx - SCR_FIELDX;
2047 SBY_Lower = lev_fieldy - SCR_FIELDY;
2052 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2054 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2057 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2058 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2060 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2061 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2063 /* if local player not found, look for custom element that might create
2064 the player (make some assumptions about the right custom element) */
2065 if (!local_player->present)
2067 int start_x = 0, start_y = 0;
2068 int found_rating = 0;
2069 int found_element = EL_UNDEFINED;
2071 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2073 int element = Feld[x][y];
2078 if (!IS_CUSTOM_ELEMENT(element))
2081 if (CAN_CHANGE(element))
2083 for (i = 0; i < element_info[element].num_change_pages; i++)
2085 content = element_info[element].change_page[i].target_element;
2086 is_player = ELEM_IS_PLAYER(content);
2088 if (is_player && (found_rating < 3 || element < found_element))
2094 found_element = element;
2099 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2101 content = element_info[element].content[xx][yy];
2102 is_player = ELEM_IS_PLAYER(content);
2104 if (is_player && (found_rating < 2 || element < found_element))
2106 start_x = x + xx - 1;
2107 start_y = y + yy - 1;
2110 found_element = element;
2113 if (!CAN_CHANGE(element))
2116 for (i = 0; i < element_info[element].num_change_pages; i++)
2118 content= element_info[element].change_page[i].target_content[xx][yy];
2119 is_player = ELEM_IS_PLAYER(content);
2121 if (is_player && (found_rating < 1 || element < found_element))
2123 start_x = x + xx - 1;
2124 start_y = y + yy - 1;
2127 found_element = element;
2133 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2134 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2137 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2138 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2144 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2145 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2146 local_player->jx - MIDPOSX);
2148 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2149 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2150 local_player->jy - MIDPOSY);
2152 scroll_x = SBX_Left;
2153 scroll_y = SBY_Upper;
2154 if (local_player->jx >= SBX_Left + MIDPOSX)
2155 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2156 local_player->jx - MIDPOSX :
2158 if (local_player->jy >= SBY_Upper + MIDPOSY)
2159 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2160 local_player->jy - MIDPOSY :
2165 CloseDoor(DOOR_CLOSE_1);
2167 /* !!! FIX THIS (START) !!! */
2168 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2170 InitGameEngine_EM();
2177 /* after drawing the level, correct some elements */
2178 if (game.timegate_time_left == 0)
2179 CloseAllOpenTimegates();
2181 if (setup.soft_scrolling)
2182 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2184 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2187 /* !!! FIX THIS (END) !!! */
2189 /* copy default game door content to main double buffer */
2190 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2191 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2193 DrawGameDoorValues();
2197 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2198 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2199 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2203 /* copy actual game door content to door double buffer for OpenDoor() */
2204 BlitBitmap(drawto, bitmap_db_door,
2205 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2207 OpenDoor(DOOR_OPEN_ALL);
2209 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2211 if (setup.sound_music)
2214 KeyboardAutoRepeatOffUnlessAutoplay();
2218 for (i = 0; i < MAX_PLAYERS; i++)
2219 printf("Player %d %sactive.\n",
2220 i + 1, (stored_player[i].active ? "" : "not "));
2224 printf("::: starting game [%d]\n", FrameCounter);
2228 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2230 /* this is used for non-R'n'D game engines to update certain engine values */
2232 /* needed to determine if sounds are played within the visible screen area */
2233 scroll_x = actual_scroll_x;
2234 scroll_y = actual_scroll_y;
2237 void InitMovDir(int x, int y)
2239 int i, element = Feld[x][y];
2240 static int xy[4][2] =
2247 static int direction[3][4] =
2249 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2250 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2251 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2260 Feld[x][y] = EL_BUG;
2261 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2264 case EL_SPACESHIP_RIGHT:
2265 case EL_SPACESHIP_UP:
2266 case EL_SPACESHIP_LEFT:
2267 case EL_SPACESHIP_DOWN:
2268 Feld[x][y] = EL_SPACESHIP;
2269 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2272 case EL_BD_BUTTERFLY_RIGHT:
2273 case EL_BD_BUTTERFLY_UP:
2274 case EL_BD_BUTTERFLY_LEFT:
2275 case EL_BD_BUTTERFLY_DOWN:
2276 Feld[x][y] = EL_BD_BUTTERFLY;
2277 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2280 case EL_BD_FIREFLY_RIGHT:
2281 case EL_BD_FIREFLY_UP:
2282 case EL_BD_FIREFLY_LEFT:
2283 case EL_BD_FIREFLY_DOWN:
2284 Feld[x][y] = EL_BD_FIREFLY;
2285 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2288 case EL_PACMAN_RIGHT:
2290 case EL_PACMAN_LEFT:
2291 case EL_PACMAN_DOWN:
2292 Feld[x][y] = EL_PACMAN;
2293 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2296 case EL_SP_SNIKSNAK:
2297 MovDir[x][y] = MV_UP;
2300 case EL_SP_ELECTRON:
2301 MovDir[x][y] = MV_LEFT;
2308 Feld[x][y] = EL_MOLE;
2309 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2313 if (IS_CUSTOM_ELEMENT(element))
2315 struct ElementInfo *ei = &element_info[element];
2316 int move_direction_initial = ei->move_direction_initial;
2317 int move_pattern = ei->move_pattern;
2319 if (move_direction_initial == MV_START_PREVIOUS)
2321 if (MovDir[x][y] != MV_NO_MOVING)
2324 move_direction_initial = MV_START_AUTOMATIC;
2327 if (move_direction_initial == MV_START_RANDOM)
2328 MovDir[x][y] = 1 << RND(4);
2329 else if (move_direction_initial & MV_ANY_DIRECTION)
2330 MovDir[x][y] = move_direction_initial;
2331 else if (move_pattern == MV_ALL_DIRECTIONS ||
2332 move_pattern == MV_TURNING_LEFT ||
2333 move_pattern == MV_TURNING_RIGHT ||
2334 move_pattern == MV_TURNING_LEFT_RIGHT ||
2335 move_pattern == MV_TURNING_RIGHT_LEFT ||
2336 move_pattern == MV_TURNING_RANDOM)
2337 MovDir[x][y] = 1 << RND(4);
2338 else if (move_pattern == MV_HORIZONTAL)
2339 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2340 else if (move_pattern == MV_VERTICAL)
2341 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2342 else if (move_pattern & MV_ANY_DIRECTION)
2343 MovDir[x][y] = element_info[element].move_pattern;
2344 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2345 move_pattern == MV_ALONG_RIGHT_SIDE)
2348 /* use random direction as default start direction */
2349 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2350 MovDir[x][y] = 1 << RND(4);
2353 for (i = 0; i < NUM_DIRECTIONS; i++)
2355 int x1 = x + xy[i][0];
2356 int y1 = y + xy[i][1];
2358 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2360 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2361 MovDir[x][y] = direction[0][i];
2363 MovDir[x][y] = direction[1][i];
2372 MovDir[x][y] = 1 << RND(4);
2374 if (element != EL_BUG &&
2375 element != EL_SPACESHIP &&
2376 element != EL_BD_BUTTERFLY &&
2377 element != EL_BD_FIREFLY)
2380 for (i = 0; i < NUM_DIRECTIONS; i++)
2382 int x1 = x + xy[i][0];
2383 int y1 = y + xy[i][1];
2385 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2387 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2389 MovDir[x][y] = direction[0][i];
2392 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2393 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2395 MovDir[x][y] = direction[1][i];
2404 GfxDir[x][y] = MovDir[x][y];
2407 void InitAmoebaNr(int x, int y)
2410 int group_nr = AmoebeNachbarNr(x, y);
2414 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2416 if (AmoebaCnt[i] == 0)
2424 AmoebaNr[x][y] = group_nr;
2425 AmoebaCnt[group_nr]++;
2426 AmoebaCnt2[group_nr]++;
2432 boolean raise_level = FALSE;
2434 if (local_player->MovPos)
2438 if (tape.auto_play) /* tape might already be stopped here */
2439 tape.auto_play_level_solved = TRUE;
2441 if (tape.playing && tape.auto_play)
2442 tape.auto_play_level_solved = TRUE;
2445 local_player->LevelSolved = FALSE;
2447 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2451 if (!tape.playing && setup.sound_loops)
2452 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2453 SND_CTRL_PLAY_LOOP);
2455 while (TimeLeft > 0)
2457 if (!tape.playing && !setup.sound_loops)
2458 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2459 if (TimeLeft > 0 && !(TimeLeft % 10))
2460 RaiseScore(level.score[SC_TIME_BONUS]);
2461 if (TimeLeft > 100 && !(TimeLeft % 10))
2466 DrawGameValue_Time(TimeLeft);
2474 if (!tape.playing && setup.sound_loops)
2475 StopSound(SND_GAME_LEVELTIME_BONUS);
2477 else if (level.time == 0) /* level without time limit */
2479 if (!tape.playing && setup.sound_loops)
2480 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2481 SND_CTRL_PLAY_LOOP);
2483 while (TimePlayed < 999)
2485 if (!tape.playing && !setup.sound_loops)
2486 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2487 if (TimePlayed < 999 && !(TimePlayed % 10))
2488 RaiseScore(level.score[SC_TIME_BONUS]);
2489 if (TimePlayed < 900 && !(TimePlayed % 10))
2494 DrawGameValue_Time(TimePlayed);
2502 if (!tape.playing && setup.sound_loops)
2503 StopSound(SND_GAME_LEVELTIME_BONUS);
2506 /* close exit door after last player */
2507 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2508 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2509 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2511 int element = Feld[ExitX][ExitY];
2513 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2514 EL_SP_EXIT_CLOSING);
2516 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2519 /* Hero disappears */
2520 if (ExitX >= 0 && ExitY >= 0)
2521 DrawLevelField(ExitX, ExitY);
2528 CloseDoor(DOOR_CLOSE_1);
2533 SaveTape(tape.level_nr); /* Ask to save tape */
2536 if (level_nr == leveldir_current->handicap_level)
2538 leveldir_current->handicap_level++;
2539 SaveLevelSetup_SeriesInfo();
2542 if (level_editor_test_game)
2543 local_player->score = -1; /* no highscore when playing from editor */
2544 else if (level_nr < leveldir_current->last_level)
2545 raise_level = TRUE; /* advance to next level */
2547 if ((hi_pos = NewHiScore()) >= 0)
2549 game_status = GAME_MODE_SCORES;
2550 DrawHallOfFame(hi_pos);
2559 game_status = GAME_MODE_MAIN;
2576 LoadScore(level_nr);
2578 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2579 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2582 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2584 if (local_player->score > highscore[k].Score)
2586 /* player has made it to the hall of fame */
2588 if (k < MAX_SCORE_ENTRIES - 1)
2590 int m = MAX_SCORE_ENTRIES - 1;
2593 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2594 if (!strcmp(setup.player_name, highscore[l].Name))
2596 if (m == k) /* player's new highscore overwrites his old one */
2600 for (l = m; l > k; l--)
2602 strcpy(highscore[l].Name, highscore[l - 1].Name);
2603 highscore[l].Score = highscore[l - 1].Score;
2610 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2611 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2612 highscore[k].Score = local_player->score;
2618 else if (!strncmp(setup.player_name, highscore[k].Name,
2619 MAX_PLAYER_NAME_LEN))
2620 break; /* player already there with a higher score */
2626 SaveScore(level_nr);
2631 inline static int getElementMoveStepsize(int x, int y)
2633 int element = Feld[x][y];
2634 int direction = MovDir[x][y];
2635 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2636 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2637 int horiz_move = (dx != 0);
2638 int sign = (horiz_move ? dx : dy);
2639 int step = sign * element_info[element].move_stepsize;
2641 /* special values for move stepsize for spring and things on conveyor belt */
2645 if (element == EL_SPRING)
2646 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2647 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2648 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2649 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2651 if (CAN_FALL(element) &&
2652 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2653 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2654 else if (element == EL_SPRING)
2655 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2662 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2664 if (player->GfxAction != action || player->GfxDir != dir)
2667 printf("Player frame reset! (%d => %d, %d => %d)\n",
2668 player->GfxAction, action, player->GfxDir, dir);
2671 player->GfxAction = action;
2672 player->GfxDir = dir;
2674 player->StepFrame = 0;
2678 static void ResetRandomAnimationValue(int x, int y)
2680 GfxRandom[x][y] = INIT_GFX_RANDOM();
2683 static void ResetGfxAnimation(int x, int y)
2686 GfxAction[x][y] = ACTION_DEFAULT;
2687 GfxDir[x][y] = MovDir[x][y];
2690 void InitMovingField(int x, int y, int direction)
2692 int element = Feld[x][y];
2693 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2694 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2698 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2699 ResetGfxAnimation(x, y);
2701 #if USE_CAN_MOVE_NOT_MOVING
2703 MovDir[x][y] = direction;
2704 GfxDir[x][y] = direction;
2705 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2706 ACTION_FALLING : ACTION_MOVING);
2708 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2710 if (Feld[newx][newy] == EL_EMPTY)
2711 Feld[newx][newy] = EL_BLOCKED;
2713 MovDir[newx][newy] = MovDir[x][y];
2714 GfxFrame[newx][newy] = GfxFrame[x][y];
2715 GfxRandom[newx][newy] = GfxRandom[x][y];
2716 GfxAction[newx][newy] = GfxAction[x][y];
2717 GfxDir[newx][newy] = GfxDir[x][y];
2722 MovDir[newx][newy] = MovDir[x][y] = direction;
2723 GfxDir[x][y] = direction;
2725 if (Feld[newx][newy] == EL_EMPTY)
2726 Feld[newx][newy] = EL_BLOCKED;
2728 if (direction == MV_DOWN && CAN_FALL(element))
2729 GfxAction[x][y] = ACTION_FALLING;
2731 GfxAction[x][y] = ACTION_MOVING;
2733 GfxFrame[newx][newy] = GfxFrame[x][y];
2734 GfxRandom[newx][newy] = GfxRandom[x][y];
2735 GfxAction[newx][newy] = GfxAction[x][y];
2736 GfxDir[newx][newy] = GfxDir[x][y];
2740 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2742 int direction = MovDir[x][y];
2743 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2744 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2750 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2752 int oldx = x, oldy = y;
2753 int direction = MovDir[x][y];
2755 if (direction == MV_LEFT)
2757 else if (direction == MV_RIGHT)
2759 else if (direction == MV_UP)
2761 else if (direction == MV_DOWN)
2764 *comes_from_x = oldx;
2765 *comes_from_y = oldy;
2768 int MovingOrBlocked2Element(int x, int y)
2770 int element = Feld[x][y];
2772 if (element == EL_BLOCKED)
2776 Blocked2Moving(x, y, &oldx, &oldy);
2777 return Feld[oldx][oldy];
2783 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2785 /* like MovingOrBlocked2Element(), but if element is moving
2786 and (x,y) is the field the moving element is just leaving,
2787 return EL_BLOCKED instead of the element value */
2788 int element = Feld[x][y];
2790 if (IS_MOVING(x, y))
2792 if (element == EL_BLOCKED)
2796 Blocked2Moving(x, y, &oldx, &oldy);
2797 return Feld[oldx][oldy];
2806 static void RemoveField(int x, int y)
2808 Feld[x][y] = EL_EMPTY;
2815 ChangeDelay[x][y] = 0;
2816 ChangePage[x][y] = -1;
2817 Pushed[x][y] = FALSE;
2820 ExplodeField[x][y] = EX_TYPE_NONE;
2823 GfxElement[x][y] = EL_UNDEFINED;
2824 GfxAction[x][y] = ACTION_DEFAULT;
2825 GfxDir[x][y] = MV_NO_MOVING;
2828 void RemoveMovingField(int x, int y)
2830 int oldx = x, oldy = y, newx = x, newy = y;
2831 int element = Feld[x][y];
2832 int next_element = EL_UNDEFINED;
2834 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2837 if (IS_MOVING(x, y))
2839 Moving2Blocked(x, y, &newx, &newy);
2841 if (Feld[newx][newy] != EL_BLOCKED)
2844 if (Feld[newx][newy] != EL_BLOCKED)
2846 /* element is moving, but target field is not free (blocked), but
2847 already occupied by something different (example: acid pool);
2848 in this case, only remove the moving field, but not the target */
2850 RemoveField(oldx, oldy);
2852 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2854 DrawLevelField(oldx, oldy);
2860 else if (element == EL_BLOCKED)
2862 Blocked2Moving(x, y, &oldx, &oldy);
2863 if (!IS_MOVING(oldx, oldy))
2867 if (element == EL_BLOCKED &&
2868 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2869 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2870 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2871 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2872 next_element = get_next_element(Feld[oldx][oldy]);
2874 RemoveField(oldx, oldy);
2875 RemoveField(newx, newy);
2877 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2879 if (next_element != EL_UNDEFINED)
2880 Feld[oldx][oldy] = next_element;
2882 DrawLevelField(oldx, oldy);
2883 DrawLevelField(newx, newy);
2886 void DrawDynamite(int x, int y)
2888 int sx = SCREENX(x), sy = SCREENY(y);
2889 int graphic = el2img(Feld[x][y]);
2892 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2895 if (IS_WALKABLE_INSIDE(Back[x][y]))
2899 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2900 else if (Store[x][y])
2901 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2903 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2906 if (Back[x][y] || Store[x][y])
2907 DrawGraphicThruMask(sx, sy, graphic, frame);
2909 DrawGraphic(sx, sy, graphic, frame);
2911 if (game.emulation == EMU_SUPAPLEX)
2912 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2913 else if (Store[x][y])
2914 DrawGraphicThruMask(sx, sy, graphic, frame);
2916 DrawGraphic(sx, sy, graphic, frame);
2920 void CheckDynamite(int x, int y)
2922 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2926 if (MovDelay[x][y] != 0)
2929 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2936 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2938 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2939 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2940 StopSound(SND_DYNAMITE_ACTIVE);
2942 StopSound(SND_DYNABOMB_ACTIVE);
2948 void DrawRelocatePlayer(struct PlayerInfo *player)
2950 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2951 boolean no_delay = (tape.warp_forward);
2952 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2953 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2954 int jx = player->jx;
2955 int jy = player->jy;
2957 if (level.instant_relocation)
2960 int offset = (setup.scroll_delay ? 3 : 0);
2962 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2964 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2965 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2966 local_player->jx - MIDPOSX);
2968 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2969 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2970 local_player->jy - MIDPOSY);
2974 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2975 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2976 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2978 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2979 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2980 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2982 /* don't scroll over playfield boundaries */
2983 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2984 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2986 /* don't scroll over playfield boundaries */
2987 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2988 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2991 scroll_x += (local_player->jx - old_jx);
2992 scroll_y += (local_player->jy - old_jy);
2994 /* don't scroll over playfield boundaries */
2995 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2996 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2998 /* don't scroll over playfield boundaries */
2999 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3000 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3003 RedrawPlayfield(TRUE, 0,0,0,0);
3009 int offset = (setup.scroll_delay ? 3 : 0);
3011 int scroll_xx = -999, scroll_yy = -999;
3013 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3015 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3018 int fx = FX, fy = FY;
3020 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3021 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3022 local_player->jx - MIDPOSX);
3024 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3025 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3026 local_player->jy - MIDPOSY);
3028 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3029 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3032 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3035 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3042 fx += dx * TILEX / 2;
3043 fy += dy * TILEY / 2;
3045 ScrollLevel(dx, dy);
3048 /* scroll in two steps of half tile size to make things smoother */
3049 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3051 Delay(wait_delay_value);
3053 /* scroll second step to align at full tile size */
3055 Delay(wait_delay_value);
3058 int scroll_xx = -999, scroll_yy = -999;
3060 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3062 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3065 int fx = FX, fy = FY;
3067 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3068 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3069 local_player->jx - MIDPOSX);
3071 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3072 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3073 local_player->jy - MIDPOSY);
3075 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3076 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3079 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3082 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3089 fx += dx * TILEX / 2;
3090 fy += dy * TILEY / 2;
3092 ScrollLevel(dx, dy);
3095 /* scroll in two steps of half tile size to make things smoother */
3096 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3098 Delay(wait_delay_value);
3100 /* scroll second step to align at full tile size */
3102 Delay(wait_delay_value);
3108 Delay(wait_delay_value);
3112 void RelocatePlayer(int jx, int jy, int el_player_raw)
3115 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3117 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3119 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3120 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3121 boolean no_delay = (tape.warp_forward);
3122 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3123 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3124 int old_jx = player->jx;
3125 int old_jy = player->jy;
3126 int old_element = Feld[old_jx][old_jy];
3127 int element = Feld[jx][jy];
3128 boolean player_relocated = (old_jx != jx || old_jy != jy);
3130 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3131 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3133 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3134 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3135 int leave_side_horiz = move_dir_horiz;
3136 int leave_side_vert = move_dir_vert;
3138 static int trigger_sides[4][2] =
3140 /* enter side leave side */
3141 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3142 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3143 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3144 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3146 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3147 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3148 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3149 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3151 int enter_side = enter_side_horiz | enter_side_vert;
3152 int leave_side = leave_side_horiz | leave_side_vert;
3154 if (player->GameOver) /* do not reanimate dead player */
3157 if (!player_relocated) /* no need to relocate the player */
3160 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3162 RemoveField(jx, jy); /* temporarily remove newly placed player */
3163 DrawLevelField(jx, jy);
3166 if (player->present)
3168 while (player->MovPos)
3170 ScrollPlayer(player, SCROLL_GO_ON);
3171 ScrollScreen(NULL, SCROLL_GO_ON);
3173 #if USE_NEW_MOVE_DELAY
3174 AdvanceFrameAndPlayerCounters(player->index_nr);
3182 Delay(wait_delay_value);
3185 DrawPlayer(player); /* needed here only to cleanup last field */
3186 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3188 player->is_moving = FALSE;
3192 if (IS_CUSTOM_ELEMENT(old_element))
3193 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3195 player->index_bit, leave_side);
3197 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3199 player->index_bit, leave_side);
3202 Feld[jx][jy] = el_player;
3203 InitPlayerField(jx, jy, el_player, TRUE);
3205 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3207 Feld[jx][jy] = element;
3208 InitField(jx, jy, FALSE);
3212 if (player == local_player) /* only visually relocate local player */
3213 DrawRelocatePlayer(player);
3217 TestIfHeroTouchesBadThing(jx, jy);
3218 TestIfPlayerTouchesCustomElement(jx, jy);
3222 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3227 /* needed to allow change of walkable custom element by entering player */
3228 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3229 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3231 /* needed to allow change of walkable custom element by entering player */
3232 Changed[jx][jy] = 0; /* allow another change */
3237 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3238 enter_side == MV_LEFT ? "left" :
3239 enter_side == MV_RIGHT ? "right" :
3240 enter_side == MV_UP ? "top" :
3241 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3245 if (IS_CUSTOM_ELEMENT(element))
3246 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3247 player->index_bit, enter_side);
3249 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3250 CE_OTHER_GETS_ENTERED,
3251 player->index_bit, enter_side);
3255 void Explode(int ex, int ey, int phase, int mode)
3262 /* !!! eliminate this variable !!! */
3263 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3268 int last_phase = num_phase * delay;
3269 int half_phase = (num_phase / 2) * delay;
3270 int first_phase_after_start = EX_PHASE_START + 1;
3274 if (game.explosions_delayed)
3276 ExplodeField[ex][ey] = mode;
3280 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3282 int center_element = Feld[ex][ey];
3285 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3289 /* --- This is only really needed (and now handled) in "Impact()". --- */
3290 /* do not explode moving elements that left the explode field in time */
3291 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3292 center_element == EL_EMPTY &&
3293 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3298 if (mode == EX_TYPE_NORMAL ||
3299 mode == EX_TYPE_CENTER ||
3300 mode == EX_TYPE_CROSS)
3301 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3303 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3304 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3307 /* remove things displayed in background while burning dynamite */
3308 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3311 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3313 /* put moving element to center field (and let it explode there) */
3314 center_element = MovingOrBlocked2Element(ex, ey);
3315 RemoveMovingField(ex, ey);
3316 Feld[ex][ey] = center_element;
3322 last_phase = element_info[center_element].explosion_delay + 1;
3324 last_phase = element_info[center_element].explosion_delay;
3328 printf("::: %d -> %d\n", center_element, last_phase);
3332 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3334 int xx = x - ex + 1;
3335 int yy = y - ey + 1;
3340 if (!IN_LEV_FIELD(x, y) ||
3341 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3342 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3345 if (!IN_LEV_FIELD(x, y) ||
3346 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3350 if (!IN_LEV_FIELD(x, y) ||
3351 ((mode != EX_TYPE_NORMAL ||
3352 center_element == EL_AMOEBA_TO_DIAMOND) &&
3353 (x != ex || y != ey)))
3357 element = Feld[x][y];
3359 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3361 element = MovingOrBlocked2Element(x, y);
3363 if (!IS_EXPLOSION_PROOF(element))
3364 RemoveMovingField(x, y);
3370 if (IS_EXPLOSION_PROOF(element))
3373 /* indestructible elements can only explode in center (but not flames) */
3375 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3376 mode == EX_TYPE_BORDER)) ||
3377 element == EL_FLAMES)
3380 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3381 element == EL_FLAMES)
3387 if ((IS_INDESTRUCTIBLE(element) &&
3388 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3389 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3390 element == EL_FLAMES)
3395 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3396 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3397 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3399 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3402 if (IS_ACTIVE_BOMB(element))
3404 /* re-activate things under the bomb like gate or penguin */
3406 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3409 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3414 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3415 element_info[Feld[x][y]].token_name,
3416 Store[x][y], Store2[x][y]);
3423 /* save walkable background elements while explosion on same tile */
3425 if (IS_INDESTRUCTIBLE(element))
3426 Back[x][y] = element;
3430 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3431 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3432 Back[x][y] = element;
3434 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3435 (x != ex || y != ey))
3436 Back[x][y] = element;
3439 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3440 Back[x][y] = element;
3444 /* ignite explodable elements reached by other explosion */
3445 if (element == EL_EXPLOSION)
3446 element = Store2[x][y];
3449 if (AmoebaNr[x][y] &&
3450 (element == EL_AMOEBA_FULL ||
3451 element == EL_BD_AMOEBA ||
3452 element == EL_AMOEBA_GROWING))
3454 AmoebaCnt[AmoebaNr[x][y]]--;
3455 AmoebaCnt2[AmoebaNr[x][y]]--;
3461 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3463 switch(StorePlayer[ex][ey])
3466 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3469 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3472 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3476 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3481 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3482 Store[x][y] = EL_EMPTY;
3484 if (game.emulation == EMU_SUPAPLEX)
3485 Store[x][y] = EL_EMPTY;
3488 else if (center_element == EL_MOLE)
3489 Store[x][y] = EL_EMERALD_RED;
3490 else if (center_element == EL_PENGUIN)
3491 Store[x][y] = EL_EMERALD_PURPLE;
3492 else if (center_element == EL_BUG)
3493 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3494 else if (center_element == EL_BD_BUTTERFLY)
3495 Store[x][y] = EL_BD_DIAMOND;
3496 else if (center_element == EL_SP_ELECTRON)
3497 Store[x][y] = EL_SP_INFOTRON;
3498 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3499 Store[x][y] = level.amoeba_content;
3500 else if (center_element == EL_YAMYAM)
3501 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3502 else if (IS_CUSTOM_ELEMENT(center_element) &&
3503 element_info[center_element].content[xx][yy] != EL_EMPTY)
3504 Store[x][y] = element_info[center_element].content[xx][yy];
3505 else if (element == EL_WALL_EMERALD)
3506 Store[x][y] = EL_EMERALD;
3507 else if (element == EL_WALL_DIAMOND)
3508 Store[x][y] = EL_DIAMOND;
3509 else if (element == EL_WALL_BD_DIAMOND)
3510 Store[x][y] = EL_BD_DIAMOND;
3511 else if (element == EL_WALL_EMERALD_YELLOW)
3512 Store[x][y] = EL_EMERALD_YELLOW;
3513 else if (element == EL_WALL_EMERALD_RED)
3514 Store[x][y] = EL_EMERALD_RED;
3515 else if (element == EL_WALL_EMERALD_PURPLE)
3516 Store[x][y] = EL_EMERALD_PURPLE;
3517 else if (element == EL_WALL_PEARL)
3518 Store[x][y] = EL_PEARL;
3519 else if (element == EL_WALL_CRYSTAL)
3520 Store[x][y] = EL_CRYSTAL;
3521 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3522 Store[x][y] = element_info[element].content[1][1];
3524 Store[x][y] = EL_EMPTY;
3526 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3527 center_element == EL_AMOEBA_TO_DIAMOND)
3528 Store2[x][y] = element;
3531 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3532 element_info[Store2[x][y]].token_name);
3536 if (AmoebaNr[x][y] &&
3537 (element == EL_AMOEBA_FULL ||
3538 element == EL_BD_AMOEBA ||
3539 element == EL_AMOEBA_GROWING))
3541 AmoebaCnt[AmoebaNr[x][y]]--;
3542 AmoebaCnt2[AmoebaNr[x][y]]--;
3548 MovDir[x][y] = MovPos[x][y] = 0;
3549 GfxDir[x][y] = MovDir[x][y];
3554 Feld[x][y] = EL_EXPLOSION;
3556 GfxElement[x][y] = center_element;
3558 GfxElement[x][y] = EL_UNDEFINED;
3561 ExplodePhase[x][y] = 1;
3563 ExplodeDelay[x][y] = last_phase;
3568 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3570 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3577 if (center_element == EL_YAMYAM)
3578 game.yamyam_content_nr =
3579 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3582 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3583 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3597 GfxFrame[x][y] = 0; /* restart explosion animation */
3601 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3605 last_phase = ExplodeDelay[x][y];
3608 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3612 /* activate this even in non-DEBUG version until cause for crash in
3613 getGraphicAnimationFrame() (see below) is found and eliminated */
3617 if (GfxElement[x][y] == EL_UNDEFINED)
3620 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3621 printf("Explode(): This should never happen!\n");
3624 GfxElement[x][y] = EL_EMPTY;
3630 border_element = Store2[x][y];
3632 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3633 border_element = StorePlayer[x][y];
3635 if (IS_PLAYER(x, y))
3636 border_element = StorePlayer[x][y];
3640 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3641 element_info[border_element].token_name, Store2[x][y]);
3645 printf("::: phase == %d\n", phase);
3648 if (phase == element_info[border_element].ignition_delay ||
3649 phase == last_phase)
3651 boolean border_explosion = FALSE;
3655 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3656 !PLAYER_EXPLOSION_PROTECTED(x, y))
3658 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3661 if (IS_PLAYER(x, y))
3664 KillHeroUnlessExplosionProtected(x, y);
3665 border_explosion = TRUE;
3668 if (phase == last_phase)
3669 printf("::: IS_PLAYER\n");
3672 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3675 printf("::: %d,%d: %d %s\n", x, y, border_element,
3676 element_info[border_element].token_name);
3679 Feld[x][y] = Store2[x][y];
3682 border_explosion = TRUE;
3685 if (phase == last_phase)
3686 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3689 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3691 AmoebeUmwandeln(x, y);
3693 border_explosion = TRUE;
3696 if (phase == last_phase)
3697 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3698 element_info[border_element].explosion_delay,
3699 element_info[border_element].ignition_delay,
3705 /* if an element just explodes due to another explosion (chain-reaction),
3706 do not immediately end the new explosion when it was the last frame of
3707 the explosion (as it would be done in the following "if"-statement!) */
3708 if (border_explosion && phase == last_phase)
3715 if (phase == first_phase_after_start)
3717 int element = Store2[x][y];
3719 if (element == EL_BLACK_ORB)
3721 Feld[x][y] = Store2[x][y];
3726 else if (phase == half_phase)
3728 int element = Store2[x][y];
3730 if (IS_PLAYER(x, y))
3731 KillHeroUnlessExplosionProtected(x, y);
3732 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3734 Feld[x][y] = Store2[x][y];
3738 else if (element == EL_AMOEBA_TO_DIAMOND)
3739 AmoebeUmwandeln(x, y);
3743 if (phase == last_phase)
3748 printf("::: done: phase == %d\n", phase);
3752 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3755 element = Feld[x][y] = Store[x][y];
3756 Store[x][y] = Store2[x][y] = 0;
3757 GfxElement[x][y] = EL_UNDEFINED;
3759 /* player can escape from explosions and might therefore be still alive */
3760 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3761 element <= EL_PLAYER_IS_EXPLODING_4)
3762 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3764 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3765 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3766 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3769 /* restore probably existing indestructible background element */
3770 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3771 element = Feld[x][y] = Back[x][y];
3774 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3775 GfxDir[x][y] = MV_NO_MOVING;
3776 ChangeDelay[x][y] = 0;
3777 ChangePage[x][y] = -1;
3780 InitField_WithBug2(x, y, FALSE);
3782 InitField(x, y, FALSE);
3784 /* !!! not needed !!! */
3786 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3787 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3790 if (CAN_MOVE(element))
3795 DrawLevelField(x, y);
3797 TestIfElementTouchesCustomElement(x, y);
3799 if (GFX_CRUMBLED(element))
3800 DrawLevelFieldCrumbledSandNeighbours(x, y);
3802 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3803 StorePlayer[x][y] = 0;
3805 if (ELEM_IS_PLAYER(element))
3806 RelocatePlayer(x, y, element);
3809 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3811 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3815 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3817 int stored = Store[x][y];
3818 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3819 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3823 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3825 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3829 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3833 printf("::: %d / %d [%d - %d]\n",
3834 GfxFrame[x][y], phase - delay, phase, delay);
3838 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3839 element_info[GfxElement[x][y]].token_name,
3844 DrawLevelFieldCrumbledSand(x, y);
3846 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3848 DrawLevelElement(x, y, Back[x][y]);
3849 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3851 else if (IS_WALKABLE_UNDER(Back[x][y]))
3853 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3854 DrawLevelElementThruMask(x, y, Back[x][y]);
3856 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3857 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3861 void DynaExplode(int ex, int ey)
3864 int dynabomb_element = Feld[ex][ey];
3865 int dynabomb_size = 1;
3866 boolean dynabomb_xl = FALSE;
3867 struct PlayerInfo *player;
3868 static int xy[4][2] =
3876 if (IS_ACTIVE_BOMB(dynabomb_element))
3878 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3879 dynabomb_size = player->dynabomb_size;
3880 dynabomb_xl = player->dynabomb_xl;
3881 player->dynabombs_left++;
3884 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3886 for (i = 0; i < NUM_DIRECTIONS; i++)
3888 for (j = 1; j <= dynabomb_size; j++)
3890 int x = ex + j * xy[i][0];
3891 int y = ey + j * xy[i][1];
3894 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3897 element = Feld[x][y];
3899 /* do not restart explosions of fields with active bombs */
3900 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3903 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3907 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3908 !IS_DIGGABLE(element) && !dynabomb_xl)
3911 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3912 !CAN_GROW_INTO(element) && !dynabomb_xl)
3916 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3917 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3918 element != EL_SAND && !dynabomb_xl)
3925 void Bang(int x, int y)
3928 int element = MovingOrBlocked2Element(x, y);
3930 int element = Feld[x][y];
3934 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3936 if (IS_PLAYER(x, y))
3939 struct PlayerInfo *player = PLAYERINFO(x, y);
3941 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3942 player->element_nr);
3947 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3949 if (game.emulation == EMU_SUPAPLEX)
3950 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3952 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3957 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3965 case EL_BD_BUTTERFLY:
3968 case EL_DARK_YAMYAM:
3972 RaiseScoreElement(element);
3973 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3975 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3976 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3977 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3978 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3979 case EL_DYNABOMB_INCREASE_NUMBER:
3980 case EL_DYNABOMB_INCREASE_SIZE:
3981 case EL_DYNABOMB_INCREASE_POWER:
3986 case EL_LAMP_ACTIVE:
3988 case EL_AMOEBA_TO_DIAMOND:
3990 if (IS_PLAYER(x, y))
3991 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3993 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3997 if (element_info[element].explosion_type == EXPLODES_CROSS)
3999 if (CAN_EXPLODE_CROSS(element))
4002 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4007 else if (element_info[element].explosion_type == EXPLODES_1X1)
4009 else if (CAN_EXPLODE_1X1(element))
4011 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4013 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4017 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
4020 void SplashAcid(int x, int y)
4023 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4024 (!IN_LEV_FIELD(x - 1, y - 2) ||
4025 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4026 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4028 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4029 (!IN_LEV_FIELD(x + 1, y - 2) ||
4030 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4031 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4033 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4035 /* input: position of element entering acid (obsolete) */
4037 int element = Feld[x][y];
4039 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4042 if (element != EL_ACID_SPLASH_LEFT &&
4043 element != EL_ACID_SPLASH_RIGHT)
4045 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4047 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4048 (!IN_LEV_FIELD(x - 1, y - 1) ||
4049 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4050 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4052 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4053 (!IN_LEV_FIELD(x + 1, y - 1) ||
4054 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4055 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4060 static void InitBeltMovement()
4062 static int belt_base_element[4] =
4064 EL_CONVEYOR_BELT_1_LEFT,
4065 EL_CONVEYOR_BELT_2_LEFT,
4066 EL_CONVEYOR_BELT_3_LEFT,
4067 EL_CONVEYOR_BELT_4_LEFT
4069 static int belt_base_active_element[4] =
4071 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4072 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4073 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4074 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4079 /* set frame order for belt animation graphic according to belt direction */
4080 for (i = 0; i < NUM_BELTS; i++)
4084 for (j = 0; j < NUM_BELT_PARTS; j++)
4086 int element = belt_base_active_element[belt_nr] + j;
4087 int graphic = el2img(element);
4089 if (game.belt_dir[i] == MV_LEFT)
4090 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4092 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4096 for (y = 0; y < lev_fieldy; y++)
4098 for (x = 0; x < lev_fieldx; x++)
4100 int element = Feld[x][y];
4102 for (i = 0; i < NUM_BELTS; i++)
4104 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4106 int e_belt_nr = getBeltNrFromBeltElement(element);
4109 if (e_belt_nr == belt_nr)
4111 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4113 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4121 static void ToggleBeltSwitch(int x, int y)
4123 static int belt_base_element[4] =
4125 EL_CONVEYOR_BELT_1_LEFT,
4126 EL_CONVEYOR_BELT_2_LEFT,
4127 EL_CONVEYOR_BELT_3_LEFT,
4128 EL_CONVEYOR_BELT_4_LEFT
4130 static int belt_base_active_element[4] =
4132 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4133 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4134 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4135 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4137 static int belt_base_switch_element[4] =
4139 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4140 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4141 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4142 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4144 static int belt_move_dir[4] =
4152 int element = Feld[x][y];
4153 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4154 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4155 int belt_dir = belt_move_dir[belt_dir_nr];
4158 if (!IS_BELT_SWITCH(element))
4161 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4162 game.belt_dir[belt_nr] = belt_dir;
4164 if (belt_dir_nr == 3)
4167 /* set frame order for belt animation graphic according to belt direction */
4168 for (i = 0; i < NUM_BELT_PARTS; i++)
4170 int element = belt_base_active_element[belt_nr] + i;
4171 int graphic = el2img(element);
4173 if (belt_dir == MV_LEFT)
4174 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4176 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4179 for (yy = 0; yy < lev_fieldy; yy++)
4181 for (xx = 0; xx < lev_fieldx; xx++)
4183 int element = Feld[xx][yy];
4185 if (IS_BELT_SWITCH(element))
4187 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4189 if (e_belt_nr == belt_nr)
4191 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4192 DrawLevelField(xx, yy);
4195 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4197 int e_belt_nr = getBeltNrFromBeltElement(element);
4199 if (e_belt_nr == belt_nr)
4201 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4203 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4204 DrawLevelField(xx, yy);
4207 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4209 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4211 if (e_belt_nr == belt_nr)
4213 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4215 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4216 DrawLevelField(xx, yy);
4223 static void ToggleSwitchgateSwitch(int x, int y)
4227 game.switchgate_pos = !game.switchgate_pos;
4229 for (yy = 0; yy < lev_fieldy; yy++)
4231 for (xx = 0; xx < lev_fieldx; xx++)
4233 int element = Feld[xx][yy];
4235 if (element == EL_SWITCHGATE_SWITCH_UP ||
4236 element == EL_SWITCHGATE_SWITCH_DOWN)
4238 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4239 DrawLevelField(xx, yy);
4241 else if (element == EL_SWITCHGATE_OPEN ||
4242 element == EL_SWITCHGATE_OPENING)
4244 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4246 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4248 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4251 else if (element == EL_SWITCHGATE_CLOSED ||
4252 element == EL_SWITCHGATE_CLOSING)
4254 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4256 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4258 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4265 static int getInvisibleActiveFromInvisibleElement(int element)
4267 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4268 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4269 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4273 static int getInvisibleFromInvisibleActiveElement(int element)
4275 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4276 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4277 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4281 static void RedrawAllLightSwitchesAndInvisibleElements()
4285 for (y = 0; y < lev_fieldy; y++)
4287 for (x = 0; x < lev_fieldx; x++)
4289 int element = Feld[x][y];
4291 if (element == EL_LIGHT_SWITCH &&
4292 game.light_time_left > 0)
4294 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4295 DrawLevelField(x, y);
4297 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4298 game.light_time_left == 0)
4300 Feld[x][y] = EL_LIGHT_SWITCH;
4301 DrawLevelField(x, y);
4303 else if (element == EL_INVISIBLE_STEELWALL ||
4304 element == EL_INVISIBLE_WALL ||
4305 element == EL_INVISIBLE_SAND)
4307 if (game.light_time_left > 0)
4308 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4310 DrawLevelField(x, y);
4312 /* uncrumble neighbour fields, if needed */
4313 if (element == EL_INVISIBLE_SAND)
4314 DrawLevelFieldCrumbledSandNeighbours(x, y);
4316 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4317 element == EL_INVISIBLE_WALL_ACTIVE ||
4318 element == EL_INVISIBLE_SAND_ACTIVE)
4320 if (game.light_time_left == 0)
4321 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4323 DrawLevelField(x, y);
4325 /* re-crumble neighbour fields, if needed */
4326 if (element == EL_INVISIBLE_SAND)
4327 DrawLevelFieldCrumbledSandNeighbours(x, y);
4333 static void ToggleLightSwitch(int x, int y)
4335 int element = Feld[x][y];
4337 game.light_time_left =
4338 (element == EL_LIGHT_SWITCH ?
4339 level.time_light * FRAMES_PER_SECOND : 0);
4341 RedrawAllLightSwitchesAndInvisibleElements();
4344 static void ActivateTimegateSwitch(int x, int y)
4348 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4350 for (yy = 0; yy < lev_fieldy; yy++)
4352 for (xx = 0; xx < lev_fieldx; xx++)
4354 int element = Feld[xx][yy];
4356 if (element == EL_TIMEGATE_CLOSED ||
4357 element == EL_TIMEGATE_CLOSING)
4359 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4360 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4364 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4366 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4367 DrawLevelField(xx, yy);
4374 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4377 void Impact(int x, int y)
4379 boolean lastline = (y == lev_fieldy-1);
4380 boolean object_hit = FALSE;
4381 boolean impact = (lastline || object_hit);
4382 int element = Feld[x][y];
4383 int smashed = EL_STEELWALL;
4385 if (!lastline) /* check if element below was hit */
4387 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4390 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4391 MovDir[x][y + 1] != MV_DOWN ||
4392 MovPos[x][y + 1] <= TILEY / 2));
4395 object_hit = !IS_FREE(x, y + 1);
4398 /* do not smash moving elements that left the smashed field in time */
4399 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4400 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4404 smashed = MovingOrBlocked2Element(x, y + 1);
4406 impact = (lastline || object_hit);
4409 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4411 SplashAcid(x, y + 1);
4415 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4416 /* only reset graphic animation if graphic really changes after impact */
4418 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4420 ResetGfxAnimation(x, y);
4421 DrawLevelField(x, y);
4424 if (impact && CAN_EXPLODE_IMPACT(element))
4429 else if (impact && element == EL_PEARL)
4431 ResetGfxAnimation(x, y);
4433 Feld[x][y] = EL_PEARL_BREAKING;
4434 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4437 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4439 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4444 if (impact && element == EL_AMOEBA_DROP)
4446 if (object_hit && IS_PLAYER(x, y + 1))
4447 KillHeroUnlessEnemyProtected(x, y + 1);
4448 else if (object_hit && smashed == EL_PENGUIN)
4452 Feld[x][y] = EL_AMOEBA_GROWING;
4453 Store[x][y] = EL_AMOEBA_WET;
4455 ResetRandomAnimationValue(x, y);
4460 if (object_hit) /* check which object was hit */
4462 if (CAN_PASS_MAGIC_WALL(element) &&
4463 (smashed == EL_MAGIC_WALL ||
4464 smashed == EL_BD_MAGIC_WALL))
4467 int activated_magic_wall =
4468 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4469 EL_BD_MAGIC_WALL_ACTIVE);
4471 /* activate magic wall / mill */
4472 for (yy = 0; yy < lev_fieldy; yy++)
4473 for (xx = 0; xx < lev_fieldx; xx++)
4474 if (Feld[xx][yy] == smashed)
4475 Feld[xx][yy] = activated_magic_wall;
4477 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4478 game.magic_wall_active = TRUE;
4480 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4481 SND_MAGIC_WALL_ACTIVATING :
4482 SND_BD_MAGIC_WALL_ACTIVATING));
4485 if (IS_PLAYER(x, y + 1))
4487 if (CAN_SMASH_PLAYER(element))
4489 KillHeroUnlessEnemyProtected(x, y + 1);
4493 else if (smashed == EL_PENGUIN)
4495 if (CAN_SMASH_PLAYER(element))
4501 else if (element == EL_BD_DIAMOND)
4503 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4509 else if (((element == EL_SP_INFOTRON ||
4510 element == EL_SP_ZONK) &&
4511 (smashed == EL_SP_SNIKSNAK ||
4512 smashed == EL_SP_ELECTRON ||
4513 smashed == EL_SP_DISK_ORANGE)) ||
4514 (element == EL_SP_INFOTRON &&
4515 smashed == EL_SP_DISK_YELLOW))
4521 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4527 else if (CAN_SMASH_EVERYTHING(element))
4529 if (IS_CLASSIC_ENEMY(smashed) ||
4530 CAN_EXPLODE_SMASHED(smashed))
4535 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4537 if (smashed == EL_LAMP ||
4538 smashed == EL_LAMP_ACTIVE)
4543 else if (smashed == EL_NUT)
4545 Feld[x][y + 1] = EL_NUT_BREAKING;
4546 PlayLevelSound(x, y, SND_NUT_BREAKING);
4547 RaiseScoreElement(EL_NUT);
4550 else if (smashed == EL_PEARL)
4552 ResetGfxAnimation(x, y);
4554 Feld[x][y + 1] = EL_PEARL_BREAKING;
4555 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4558 else if (smashed == EL_DIAMOND)
4560 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4561 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4564 else if (IS_BELT_SWITCH(smashed))
4566 ToggleBeltSwitch(x, y + 1);
4568 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4569 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4571 ToggleSwitchgateSwitch(x, y + 1);
4573 else if (smashed == EL_LIGHT_SWITCH ||
4574 smashed == EL_LIGHT_SWITCH_ACTIVE)
4576 ToggleLightSwitch(x, y + 1);
4581 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4584 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4587 /* !!! TEST ONLY !!! */
4588 CheckElementChangeBySide(x, y + 1, smashed, element,
4589 CE_SWITCHED, CH_SIDE_TOP);
4590 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4591 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4593 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4594 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4595 CheckElementChangeBySide(x, y + 1, smashed, element,
4596 CE_SWITCHED, CH_SIDE_TOP);
4602 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4607 /* play sound of magic wall / mill */
4609 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4610 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4612 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4613 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4614 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4615 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4620 /* play sound of object that hits the ground */
4621 if (lastline || object_hit)
4622 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4625 inline static void TurnRoundExt(int x, int y)
4637 { 0, 0 }, { 0, 0 }, { 0, 0 },
4642 int left, right, back;
4646 { MV_DOWN, MV_UP, MV_RIGHT },
4647 { MV_UP, MV_DOWN, MV_LEFT },
4649 { MV_LEFT, MV_RIGHT, MV_DOWN },
4653 { MV_RIGHT, MV_LEFT, MV_UP }
4656 int element = Feld[x][y];
4657 int move_pattern = element_info[element].move_pattern;
4659 int old_move_dir = MovDir[x][y];
4660 int left_dir = turn[old_move_dir].left;
4661 int right_dir = turn[old_move_dir].right;
4662 int back_dir = turn[old_move_dir].back;
4664 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4665 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4666 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4667 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4669 int left_x = x + left_dx, left_y = y + left_dy;
4670 int right_x = x + right_dx, right_y = y + right_dy;
4671 int move_x = x + move_dx, move_y = y + move_dy;
4675 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4677 TestIfBadThingTouchesOtherBadThing(x, y);
4679 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4680 MovDir[x][y] = right_dir;
4681 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4682 MovDir[x][y] = left_dir;
4684 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4686 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4690 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4691 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4693 TestIfBadThingTouchesOtherBadThing(x, y);
4695 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4696 MovDir[x][y] = left_dir;
4697 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4698 MovDir[x][y] = right_dir;
4700 if ((element == EL_SPACESHIP ||
4701 element == EL_SP_SNIKSNAK ||
4702 element == EL_SP_ELECTRON)
4703 && MovDir[x][y] != old_move_dir)
4705 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4709 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4711 TestIfBadThingTouchesOtherBadThing(x, y);
4713 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4714 MovDir[x][y] = left_dir;
4715 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4716 MovDir[x][y] = right_dir;
4718 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4720 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4723 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4725 TestIfBadThingTouchesOtherBadThing(x, y);
4727 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4728 MovDir[x][y] = left_dir;
4729 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4730 MovDir[x][y] = right_dir;
4732 if (MovDir[x][y] != old_move_dir)
4736 else if (element == EL_YAMYAM)
4738 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4739 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4741 if (can_turn_left && can_turn_right)
4742 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4743 else if (can_turn_left)
4744 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4745 else if (can_turn_right)
4746 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4748 MovDir[x][y] = back_dir;
4750 MovDelay[x][y] = 16 + 16 * RND(3);
4752 else if (element == EL_DARK_YAMYAM)
4754 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4756 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4759 if (can_turn_left && can_turn_right)
4760 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4761 else if (can_turn_left)
4762 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4763 else if (can_turn_right)
4764 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4766 MovDir[x][y] = back_dir;
4768 MovDelay[x][y] = 16 + 16 * RND(3);
4770 else if (element == EL_PACMAN)
4772 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4773 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4775 if (can_turn_left && can_turn_right)
4776 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4777 else if (can_turn_left)
4778 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4779 else if (can_turn_right)
4780 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4782 MovDir[x][y] = back_dir;
4784 MovDelay[x][y] = 6 + RND(40);
4786 else if (element == EL_PIG)
4788 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4789 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4790 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4791 boolean should_turn_left, should_turn_right, should_move_on;
4793 int rnd = RND(rnd_value);
4795 should_turn_left = (can_turn_left &&
4797 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4798 y + back_dy + left_dy)));
4799 should_turn_right = (can_turn_right &&
4801 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4802 y + back_dy + right_dy)));
4803 should_move_on = (can_move_on &&
4806 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4807 y + move_dy + left_dy) ||
4808 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4809 y + move_dy + right_dy)));
4811 if (should_turn_left || should_turn_right || should_move_on)
4813 if (should_turn_left && should_turn_right && should_move_on)
4814 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4815 rnd < 2 * rnd_value / 3 ? right_dir :
4817 else if (should_turn_left && should_turn_right)
4818 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4819 else if (should_turn_left && should_move_on)
4820 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4821 else if (should_turn_right && should_move_on)
4822 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4823 else if (should_turn_left)
4824 MovDir[x][y] = left_dir;
4825 else if (should_turn_right)
4826 MovDir[x][y] = right_dir;
4827 else if (should_move_on)
4828 MovDir[x][y] = old_move_dir;
4830 else if (can_move_on && rnd > rnd_value / 8)
4831 MovDir[x][y] = old_move_dir;
4832 else if (can_turn_left && can_turn_right)
4833 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4834 else if (can_turn_left && rnd > rnd_value / 8)
4835 MovDir[x][y] = left_dir;
4836 else if (can_turn_right && rnd > rnd_value/8)
4837 MovDir[x][y] = right_dir;
4839 MovDir[x][y] = back_dir;
4841 xx = x + move_xy[MovDir[x][y]].x;
4842 yy = y + move_xy[MovDir[x][y]].y;
4845 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4846 if (!IN_LEV_FIELD(xx, yy) ||
4847 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4848 MovDir[x][y] = old_move_dir;
4850 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4851 MovDir[x][y] = old_move_dir;
4856 else if (element == EL_DRAGON)
4858 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4859 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4860 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4862 int rnd = RND(rnd_value);
4865 if (FrameCounter < 1 && x == 0 && y == 29)
4866 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4869 if (can_move_on && rnd > rnd_value / 8)
4870 MovDir[x][y] = old_move_dir;
4871 else if (can_turn_left && can_turn_right)
4872 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4873 else if (can_turn_left && rnd > rnd_value / 8)
4874 MovDir[x][y] = left_dir;
4875 else if (can_turn_right && rnd > rnd_value / 8)
4876 MovDir[x][y] = right_dir;
4878 MovDir[x][y] = back_dir;
4880 xx = x + move_xy[MovDir[x][y]].x;
4881 yy = y + move_xy[MovDir[x][y]].y;
4884 if (FrameCounter < 1 && x == 0 && y == 29)
4885 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4886 xx, yy, Feld[xx][yy],
4891 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4892 MovDir[x][y] = old_move_dir;
4894 if (!IS_FREE(xx, yy))
4895 MovDir[x][y] = old_move_dir;
4899 if (FrameCounter < 1 && x == 0 && y == 29)
4900 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4905 else if (element == EL_MOLE)
4907 boolean can_move_on =
4908 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4909 IS_AMOEBOID(Feld[move_x][move_y]) ||
4910 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4913 boolean can_turn_left =
4914 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4915 IS_AMOEBOID(Feld[left_x][left_y])));
4917 boolean can_turn_right =
4918 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4919 IS_AMOEBOID(Feld[right_x][right_y])));
4921 if (can_turn_left && can_turn_right)
4922 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4923 else if (can_turn_left)
4924 MovDir[x][y] = left_dir;
4926 MovDir[x][y] = right_dir;
4929 if (MovDir[x][y] != old_move_dir)
4932 else if (element == EL_BALLOON)
4934 MovDir[x][y] = game.balloon_dir;
4937 else if (element == EL_SPRING)
4940 if (MovDir[x][y] & MV_HORIZONTAL &&
4941 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4942 MovDir[x][y] = MV_NO_MOVING;
4944 if (MovDir[x][y] & MV_HORIZONTAL &&
4945 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4946 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4947 MovDir[x][y] = MV_NO_MOVING;
4952 else if (element == EL_ROBOT ||
4953 element == EL_SATELLITE ||
4954 element == EL_PENGUIN)
4956 int attr_x = -1, attr_y = -1;
4967 for (i = 0; i < MAX_PLAYERS; i++)
4969 struct PlayerInfo *player = &stored_player[i];
4970 int jx = player->jx, jy = player->jy;
4972 if (!player->active)
4976 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4985 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4986 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4987 game.engine_version < VERSION_IDENT(3,1,0,0)))
4989 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4996 if (element == EL_PENGUIN)
4999 static int xy[4][2] =
5007 for (i = 0; i < NUM_DIRECTIONS; i++)
5009 int ex = x + xy[i][0];
5010 int ey = y + xy[i][1];
5012 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5021 MovDir[x][y] = MV_NO_MOVING;
5023 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5024 else if (attr_x > x)
5025 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5027 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5028 else if (attr_y > y)
5029 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5031 if (element == EL_ROBOT)
5035 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5036 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5037 Moving2Blocked(x, y, &newx, &newy);
5039 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5040 MovDelay[x][y] = 8 + 8 * !RND(3);
5042 MovDelay[x][y] = 16;
5044 else if (element == EL_PENGUIN)
5050 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5052 boolean first_horiz = RND(2);
5053 int new_move_dir = MovDir[x][y];
5056 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5057 Moving2Blocked(x, y, &newx, &newy);
5059 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5063 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5064 Moving2Blocked(x, y, &newx, &newy);
5066 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5069 MovDir[x][y] = old_move_dir;
5073 else /* (element == EL_SATELLITE) */
5079 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5081 boolean first_horiz = RND(2);
5082 int new_move_dir = MovDir[x][y];
5085 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5086 Moving2Blocked(x, y, &newx, &newy);
5088 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5092 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5093 Moving2Blocked(x, y, &newx, &newy);
5095 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5098 MovDir[x][y] = old_move_dir;
5103 else if (move_pattern == MV_TURNING_LEFT ||
5104 move_pattern == MV_TURNING_RIGHT ||
5105 move_pattern == MV_TURNING_LEFT_RIGHT ||
5106 move_pattern == MV_TURNING_RIGHT_LEFT ||
5107 move_pattern == MV_TURNING_RANDOM ||
5108 move_pattern == MV_ALL_DIRECTIONS)
5110 boolean can_turn_left =
5111 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5112 boolean can_turn_right =
5113 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5115 #if USE_CAN_MOVE_NOT_MOVING
5116 if (element_info[element].move_stepsize == 0) /* not moving */
5120 if (move_pattern == MV_TURNING_LEFT)
5121 MovDir[x][y] = left_dir;
5122 else if (move_pattern == MV_TURNING_RIGHT)
5123 MovDir[x][y] = right_dir;
5124 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5125 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5126 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5127 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5128 else if (move_pattern == MV_TURNING_RANDOM)
5129 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5130 can_turn_right && !can_turn_left ? right_dir :
5131 RND(2) ? left_dir : right_dir);
5132 else if (can_turn_left && can_turn_right)
5133 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5134 else if (can_turn_left)
5135 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5136 else if (can_turn_right)
5137 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5139 MovDir[x][y] = back_dir;
5141 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5143 else if (move_pattern == MV_HORIZONTAL ||
5144 move_pattern == MV_VERTICAL)
5146 if (move_pattern & old_move_dir)
5147 MovDir[x][y] = back_dir;
5148 else if (move_pattern == MV_HORIZONTAL)
5149 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5150 else if (move_pattern == MV_VERTICAL)
5151 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5153 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5155 else if (move_pattern & MV_ANY_DIRECTION)
5157 MovDir[x][y] = move_pattern;
5158 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5160 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5162 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5163 MovDir[x][y] = left_dir;
5164 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5165 MovDir[x][y] = right_dir;
5167 if (MovDir[x][y] != old_move_dir)
5168 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5170 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5172 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5173 MovDir[x][y] = right_dir;
5174 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5175 MovDir[x][y] = left_dir;
5177 if (MovDir[x][y] != old_move_dir)
5178 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5180 else if (move_pattern == MV_TOWARDS_PLAYER ||
5181 move_pattern == MV_AWAY_FROM_PLAYER)
5183 int attr_x = -1, attr_y = -1;
5185 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5196 for (i = 0; i < MAX_PLAYERS; i++)
5198 struct PlayerInfo *player = &stored_player[i];
5199 int jx = player->jx, jy = player->jy;
5201 if (!player->active)
5205 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5213 MovDir[x][y] = MV_NO_MOVING;
5215 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5216 else if (attr_x > x)
5217 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5219 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5220 else if (attr_y > y)
5221 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5223 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5225 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5227 boolean first_horiz = RND(2);
5228 int new_move_dir = MovDir[x][y];
5230 #if USE_CAN_MOVE_NOT_MOVING
5231 if (element_info[element].move_stepsize == 0) /* not moving */
5233 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5234 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5241 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5242 Moving2Blocked(x, y, &newx, &newy);
5244 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5248 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5249 Moving2Blocked(x, y, &newx, &newy);
5251 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5254 MovDir[x][y] = old_move_dir;
5257 else if (move_pattern == MV_WHEN_PUSHED ||
5258 move_pattern == MV_WHEN_DROPPED)
5260 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5261 MovDir[x][y] = MV_NO_MOVING;
5265 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5267 static int test_xy[7][2] =
5277 static int test_dir[7] =
5287 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5288 int move_preference = -1000000; /* start with very low preference */
5289 int new_move_dir = MV_NO_MOVING;
5290 int start_test = RND(4);
5293 for (i = 0; i < NUM_DIRECTIONS; i++)
5295 int move_dir = test_dir[start_test + i];
5296 int move_dir_preference;
5298 xx = x + test_xy[start_test + i][0];
5299 yy = y + test_xy[start_test + i][1];
5301 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5302 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5304 new_move_dir = move_dir;
5309 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5312 move_dir_preference = -1 * RunnerVisit[xx][yy];
5313 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5314 move_dir_preference = PlayerVisit[xx][yy];
5316 if (move_dir_preference > move_preference)
5318 /* prefer field that has not been visited for the longest time */
5319 move_preference = move_dir_preference;
5320 new_move_dir = move_dir;
5322 else if (move_dir_preference == move_preference &&
5323 move_dir == old_move_dir)
5325 /* prefer last direction when all directions are preferred equally */
5326 move_preference = move_dir_preference;
5327 new_move_dir = move_dir;
5331 MovDir[x][y] = new_move_dir;
5332 if (old_move_dir != new_move_dir)
5335 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5343 static void TurnRound(int x, int y)
5345 int direction = MovDir[x][y];
5348 GfxDir[x][y] = MovDir[x][y];
5354 GfxDir[x][y] = MovDir[x][y];
5357 if (direction != MovDir[x][y])
5362 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5365 GfxAction[x][y] = ACTION_WAITING;
5369 static boolean JustBeingPushed(int x, int y)
5373 for (i = 0; i < MAX_PLAYERS; i++)
5375 struct PlayerInfo *player = &stored_player[i];
5377 if (player->active && player->is_pushing && player->MovPos)
5379 int next_jx = player->jx + (player->jx - player->last_jx);
5380 int next_jy = player->jy + (player->jy - player->last_jy);
5382 if (x == next_jx && y == next_jy)
5390 void StartMoving(int x, int y)
5393 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5395 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5396 int element = Feld[x][y];
5402 if (MovDelay[x][y] == 0)
5403 GfxAction[x][y] = ACTION_DEFAULT;
5405 /* !!! this should be handled more generic (not only for mole) !!! */
5406 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5407 GfxAction[x][y] = ACTION_DEFAULT;
5410 if (CAN_FALL(element) && y < lev_fieldy - 1)
5412 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5413 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5414 if (JustBeingPushed(x, y))
5417 if (element == EL_QUICKSAND_FULL)
5419 if (IS_FREE(x, y + 1))
5421 InitMovingField(x, y, MV_DOWN);
5422 started_moving = TRUE;
5424 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5425 Store[x][y] = EL_ROCK;
5427 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5429 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5432 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5434 if (!MovDelay[x][y])
5435 MovDelay[x][y] = TILEY + 1;
5444 Feld[x][y] = EL_QUICKSAND_EMPTY;
5445 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5446 Store[x][y + 1] = Store[x][y];
5449 PlayLevelSoundAction(x, y, ACTION_FILLING);
5451 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5455 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5456 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5458 InitMovingField(x, y, MV_DOWN);
5459 started_moving = TRUE;
5461 Feld[x][y] = EL_QUICKSAND_FILLING;
5462 Store[x][y] = element;
5464 PlayLevelSoundAction(x, y, ACTION_FILLING);
5466 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5469 else if (element == EL_MAGIC_WALL_FULL)
5471 if (IS_FREE(x, y + 1))
5473 InitMovingField(x, y, MV_DOWN);
5474 started_moving = TRUE;
5476 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5477 Store[x][y] = EL_CHANGED(Store[x][y]);
5479 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5481 if (!MovDelay[x][y])
5482 MovDelay[x][y] = TILEY/4 + 1;
5491 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5492 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5493 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5497 else if (element == EL_BD_MAGIC_WALL_FULL)
5499 if (IS_FREE(x, y + 1))
5501 InitMovingField(x, y, MV_DOWN);
5502 started_moving = TRUE;
5504 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5505 Store[x][y] = EL_CHANGED2(Store[x][y]);
5507 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5509 if (!MovDelay[x][y])
5510 MovDelay[x][y] = TILEY/4 + 1;
5519 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5520 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5521 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5525 else if (CAN_PASS_MAGIC_WALL(element) &&
5526 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5527 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5529 InitMovingField(x, y, MV_DOWN);
5530 started_moving = TRUE;
5533 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5534 EL_BD_MAGIC_WALL_FILLING);
5535 Store[x][y] = element;
5538 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5540 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5543 SplashAcid(x, y + 1);
5545 InitMovingField(x, y, MV_DOWN);
5546 started_moving = TRUE;
5548 Store[x][y] = EL_ACID;
5550 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5551 GfxAction[x][y + 1] = ACTION_ACTIVE;
5555 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5556 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5558 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5559 CAN_SMASH(element) && WasJustFalling[x][y] &&
5560 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5562 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5563 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5564 (Feld[x][y + 1] == EL_BLOCKED)))
5568 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5569 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5570 WasJustMoving[x][y] && !Pushed[x][y + 1])
5572 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5573 WasJustMoving[x][y])
5578 /* this is needed for a special case not covered by calling "Impact()"
5579 from "ContinueMoving()": if an element moves to a tile directly below
5580 another element which was just falling on that tile (which was empty
5581 in the previous frame), the falling element above would just stop
5582 instead of smashing the element below (in previous version, the above
5583 element was just checked for "moving" instead of "falling", resulting
5584 in incorrect smashes caused by horizontal movement of the above
5585 element; also, the case of the player being the element to smash was
5586 simply not covered here... :-/ ) */
5589 WasJustMoving[x][y] = 0;
5590 WasJustFalling[x][y] = 0;
5593 CheckCollision[x][y] = 0;
5596 if (IS_PLAYER(x, y + 1))
5597 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5602 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5604 if (MovDir[x][y] == MV_NO_MOVING)
5606 InitMovingField(x, y, MV_DOWN);
5607 started_moving = TRUE;
5610 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5612 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5613 MovDir[x][y] = MV_DOWN;
5615 InitMovingField(x, y, MV_DOWN);
5616 started_moving = TRUE;
5618 else if (element == EL_AMOEBA_DROP)
5620 Feld[x][y] = EL_AMOEBA_GROWING;
5621 Store[x][y] = EL_AMOEBA_WET;
5623 /* Store[x][y + 1] must be zero, because:
5624 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5627 #if OLD_GAME_BEHAVIOUR
5628 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5630 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5631 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5632 element != EL_DX_SUPABOMB)
5635 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5636 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5637 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5638 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5641 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5642 (IS_FREE(x - 1, y + 1) ||
5643 Feld[x - 1][y + 1] == EL_ACID));
5644 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5645 (IS_FREE(x + 1, y + 1) ||
5646 Feld[x + 1][y + 1] == EL_ACID));
5647 boolean can_fall_any = (can_fall_left || can_fall_right);
5648 boolean can_fall_both = (can_fall_left && can_fall_right);
5650 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5652 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5654 if (slippery_type == SLIPPERY_ONLY_LEFT)
5655 can_fall_right = FALSE;
5656 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5657 can_fall_left = FALSE;
5658 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5659 can_fall_right = FALSE;
5660 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5661 can_fall_left = FALSE;
5663 can_fall_any = (can_fall_left || can_fall_right);
5664 can_fall_both = (can_fall_left && can_fall_right);
5667 #if USE_NEW_SP_SLIPPERY
5668 /* !!! better use the same properties as for custom elements here !!! */
5669 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5670 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5672 can_fall_right = FALSE; /* slip down on left side */
5673 can_fall_both = FALSE;
5680 if (game.emulation == EMU_BOULDERDASH ||
5681 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5682 can_fall_right = FALSE; /* slip down on left side */
5684 can_fall_left = !(can_fall_right = RND(2));
5686 can_fall_both = FALSE;
5693 if (can_fall_both &&
5694 (game.emulation != EMU_BOULDERDASH &&
5695 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5696 can_fall_left = !(can_fall_right = RND(2));
5699 /* if not determined otherwise, prefer left side for slipping down */
5700 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5701 started_moving = TRUE;
5705 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5707 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5710 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5711 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5712 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5713 int belt_dir = game.belt_dir[belt_nr];
5715 if ((belt_dir == MV_LEFT && left_is_free) ||
5716 (belt_dir == MV_RIGHT && right_is_free))
5719 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5722 InitMovingField(x, y, belt_dir);
5723 started_moving = TRUE;
5726 Pushed[x][y] = TRUE;
5727 Pushed[nextx][y] = TRUE;
5730 GfxAction[x][y] = ACTION_DEFAULT;
5734 MovDir[x][y] = 0; /* if element was moving, stop it */
5739 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5741 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5743 if (CAN_MOVE(element) && !started_moving)
5746 int move_pattern = element_info[element].move_pattern;
5751 if (MovDir[x][y] == MV_NO_MOVING)
5753 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5754 x, y, element, element_info[element].token_name);
5755 printf("StartMoving(): This should never happen!\n");
5760 Moving2Blocked(x, y, &newx, &newy);
5763 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5766 if ((element == EL_SATELLITE ||
5767 element == EL_BALLOON ||
5768 element == EL_SPRING)
5769 && JustBeingPushed(x, y))
5776 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5777 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5779 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5780 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5781 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5785 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5786 element, element_info[element].token_name,
5787 WasJustMoving[x][y],
5788 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5789 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5790 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5791 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5795 WasJustMoving[x][y] = 0;
5798 CheckCollision[x][y] = 0;
5800 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5803 if (Feld[x][y] != element) /* element has changed */
5805 element = Feld[x][y];
5806 move_pattern = element_info[element].move_pattern;
5808 if (!CAN_MOVE(element))
5812 if (Feld[x][y] != element) /* element has changed */
5820 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5821 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5823 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5825 Moving2Blocked(x, y, &newx, &newy);
5826 if (Feld[newx][newy] == EL_BLOCKED)
5827 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5833 if (FrameCounter < 1 && x == 0 && y == 29)
5834 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5837 if (!MovDelay[x][y]) /* start new movement phase */
5839 /* all objects that can change their move direction after each step
5840 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5842 if (element != EL_YAMYAM &&
5843 element != EL_DARK_YAMYAM &&
5844 element != EL_PACMAN &&
5845 !(move_pattern & MV_ANY_DIRECTION) &&
5846 move_pattern != MV_TURNING_LEFT &&
5847 move_pattern != MV_TURNING_RIGHT &&
5848 move_pattern != MV_TURNING_LEFT_RIGHT &&
5849 move_pattern != MV_TURNING_RIGHT_LEFT &&
5850 move_pattern != MV_TURNING_RANDOM)
5855 if (FrameCounter < 1 && x == 0 && y == 29)
5856 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5859 if (MovDelay[x][y] && (element == EL_BUG ||
5860 element == EL_SPACESHIP ||
5861 element == EL_SP_SNIKSNAK ||
5862 element == EL_SP_ELECTRON ||
5863 element == EL_MOLE))
5864 DrawLevelField(x, y);
5868 if (MovDelay[x][y]) /* wait some time before next movement */
5873 if (element == EL_YAMYAM)
5876 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5877 DrawLevelElementAnimation(x, y, element);
5881 if (MovDelay[x][y]) /* element still has to wait some time */
5884 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5885 ResetGfxAnimation(x, y);
5889 if (GfxAction[x][y] != ACTION_WAITING)
5890 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5892 GfxAction[x][y] = ACTION_WAITING;
5896 if (element == EL_ROBOT ||
5898 element == EL_PACMAN ||
5900 element == EL_YAMYAM ||
5901 element == EL_DARK_YAMYAM)
5904 DrawLevelElementAnimation(x, y, element);
5906 DrawLevelElementAnimationIfNeeded(x, y, element);
5908 PlayLevelSoundAction(x, y, ACTION_WAITING);
5910 else if (element == EL_SP_ELECTRON)
5911 DrawLevelElementAnimationIfNeeded(x, y, element);
5912 else if (element == EL_DRAGON)
5915 int dir = MovDir[x][y];
5916 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5917 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5918 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5919 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5920 dir == MV_UP ? IMG_FLAMES_1_UP :
5921 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5922 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5925 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5928 GfxAction[x][y] = ACTION_ATTACKING;
5930 if (IS_PLAYER(x, y))
5931 DrawPlayerField(x, y);
5933 DrawLevelField(x, y);
5935 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5937 for (i = 1; i <= 3; i++)
5939 int xx = x + i * dx;
5940 int yy = y + i * dy;
5941 int sx = SCREENX(xx);
5942 int sy = SCREENY(yy);
5943 int flame_graphic = graphic + (i - 1);
5945 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5950 int flamed = MovingOrBlocked2Element(xx, yy);
5954 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5956 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5957 RemoveMovingField(xx, yy);
5959 RemoveField(xx, yy);
5961 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5964 RemoveMovingField(xx, yy);
5968 if (ChangeDelay[xx][yy])
5969 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5970 Feld[xx][yy] == EL_BLOCKED));
5974 ChangeDelay[xx][yy] = 0;
5976 Feld[xx][yy] = EL_FLAMES;
5977 if (IN_SCR_FIELD(sx, sy))
5979 DrawLevelFieldCrumbledSand(xx, yy);
5980 DrawGraphic(sx, sy, flame_graphic, frame);
5985 if (Feld[xx][yy] == EL_FLAMES)
5986 Feld[xx][yy] = EL_EMPTY;
5987 DrawLevelField(xx, yy);
5992 if (MovDelay[x][y]) /* element still has to wait some time */
5994 PlayLevelSoundAction(x, y, ACTION_WAITING);
6000 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6001 for all other elements GfxAction will be set by InitMovingField() */
6002 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6003 GfxAction[x][y] = ACTION_MOVING;
6007 /* now make next step */
6009 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6011 if (DONT_COLLIDE_WITH(element) &&
6012 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6013 !PLAYER_ENEMY_PROTECTED(newx, newy))
6016 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6020 /* player killed by element which is deadly when colliding with */
6022 KillHero(PLAYERINFO(newx, newy));
6029 else if (CAN_MOVE_INTO_ACID(element) &&
6030 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6031 (MovDir[x][y] == MV_DOWN ||
6032 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6034 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6035 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6039 else if ((element == EL_PENGUIN ||
6040 element == EL_ROBOT ||
6041 element == EL_SATELLITE ||
6042 element == EL_BALLOON ||
6043 IS_CUSTOM_ELEMENT(element)) &&
6044 IN_LEV_FIELD(newx, newy) &&
6045 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6048 SplashAcid(newx, newy);
6049 Store[x][y] = EL_ACID;
6051 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6053 if (Feld[newx][newy] == EL_EXIT_OPEN)
6057 DrawLevelField(x, y);
6059 Feld[x][y] = EL_EMPTY;
6060 DrawLevelField(x, y);
6063 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6064 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6065 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6067 local_player->friends_still_needed--;
6068 if (!local_player->friends_still_needed &&
6069 !local_player->GameOver && AllPlayersGone)
6070 local_player->LevelSolved = local_player->GameOver = TRUE;
6074 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6076 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6077 DrawLevelField(newx, newy);
6079 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6081 else if (!IS_FREE(newx, newy))
6083 GfxAction[x][y] = ACTION_WAITING;
6085 if (IS_PLAYER(x, y))
6086 DrawPlayerField(x, y);
6088 DrawLevelField(x, y);
6093 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6095 if (IS_FOOD_PIG(Feld[newx][newy]))
6097 if (IS_MOVING(newx, newy))
6098 RemoveMovingField(newx, newy);
6101 Feld[newx][newy] = EL_EMPTY;
6102 DrawLevelField(newx, newy);
6105 PlayLevelSound(x, y, SND_PIG_DIGGING);
6107 else if (!IS_FREE(newx, newy))
6109 if (IS_PLAYER(x, y))
6110 DrawPlayerField(x, y);
6112 DrawLevelField(x, y);
6121 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6124 else if (IS_CUSTOM_ELEMENT(element) &&
6125 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6129 !IS_FREE(newx, newy)
6134 int new_element = Feld[newx][newy];
6137 printf("::: '%s' digs '%s' [%d]\n",
6138 element_info[element].token_name,
6139 element_info[Feld[newx][newy]].token_name,
6140 StorePlayer[newx][newy]);
6143 if (!IS_FREE(newx, newy))
6145 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6146 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6149 /* no element can dig solid indestructible elements */
6150 if (IS_INDESTRUCTIBLE(new_element) &&
6151 !IS_DIGGABLE(new_element) &&
6152 !IS_COLLECTIBLE(new_element))
6155 if (AmoebaNr[newx][newy] &&
6156 (new_element == EL_AMOEBA_FULL ||
6157 new_element == EL_BD_AMOEBA ||
6158 new_element == EL_AMOEBA_GROWING))
6160 AmoebaCnt[AmoebaNr[newx][newy]]--;
6161 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6164 if (IS_MOVING(newx, newy))
6165 RemoveMovingField(newx, newy);
6168 RemoveField(newx, newy);
6169 DrawLevelField(newx, newy);
6172 /* if digged element was about to explode, prevent the explosion */
6173 ExplodeField[newx][newy] = EX_TYPE_NONE;
6175 PlayLevelSoundAction(x, y, action);
6180 Store[newx][newy] = EL_EMPTY;
6181 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6182 Store[newx][newy] = element_info[element].move_leave_element;
6184 Store[newx][newy] = EL_EMPTY;
6185 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6186 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6187 Store[newx][newy] = element_info[element].move_leave_element;
6190 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6191 element_info[element].can_leave_element = TRUE;
6194 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6196 RunnerVisit[x][y] = FrameCounter;
6197 PlayerVisit[x][y] /= 8; /* expire player visit path */
6203 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6205 if (!IS_FREE(newx, newy))
6207 if (IS_PLAYER(x, y))
6208 DrawPlayerField(x, y);
6210 DrawLevelField(x, y);
6216 boolean wanna_flame = !RND(10);
6217 int dx = newx - x, dy = newy - y;
6218 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6219 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6220 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6221 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6222 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6223 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6226 IS_CLASSIC_ENEMY(element1) ||
6227 IS_CLASSIC_ENEMY(element2)) &&
6228 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6229 element1 != EL_FLAMES && element2 != EL_FLAMES)
6232 ResetGfxAnimation(x, y);
6233 GfxAction[x][y] = ACTION_ATTACKING;
6236 if (IS_PLAYER(x, y))
6237 DrawPlayerField(x, y);
6239 DrawLevelField(x, y);
6241 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6243 MovDelay[x][y] = 50;
6247 RemoveField(newx, newy);
6249 Feld[newx][newy] = EL_FLAMES;
6250 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6253 RemoveField(newx1, newy1);
6255 Feld[newx1][newy1] = EL_FLAMES;
6257 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6260 RemoveField(newx2, newy2);
6262 Feld[newx2][newy2] = EL_FLAMES;
6269 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6270 Feld[newx][newy] == EL_DIAMOND)
6272 if (IS_MOVING(newx, newy))
6273 RemoveMovingField(newx, newy);
6276 Feld[newx][newy] = EL_EMPTY;
6277 DrawLevelField(newx, newy);
6280 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6282 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6283 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6285 if (AmoebaNr[newx][newy])
6287 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6288 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6289 Feld[newx][newy] == EL_BD_AMOEBA)
6290 AmoebaCnt[AmoebaNr[newx][newy]]--;
6295 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6297 if (IS_MOVING(newx, newy))
6300 RemoveMovingField(newx, newy);
6304 Feld[newx][newy] = EL_EMPTY;
6305 DrawLevelField(newx, newy);
6308 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6310 else if ((element == EL_PACMAN || element == EL_MOLE)
6311 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6313 if (AmoebaNr[newx][newy])
6315 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6316 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6317 Feld[newx][newy] == EL_BD_AMOEBA)
6318 AmoebaCnt[AmoebaNr[newx][newy]]--;
6321 if (element == EL_MOLE)
6323 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6324 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6326 ResetGfxAnimation(x, y);
6327 GfxAction[x][y] = ACTION_DIGGING;
6328 DrawLevelField(x, y);
6330 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6332 return; /* wait for shrinking amoeba */
6334 else /* element == EL_PACMAN */
6336 Feld[newx][newy] = EL_EMPTY;
6337 DrawLevelField(newx, newy);
6338 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6341 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6342 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6343 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6345 /* wait for shrinking amoeba to completely disappear */
6348 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6350 /* object was running against a wall */
6355 if (move_pattern & MV_ANY_DIRECTION &&
6356 move_pattern == MovDir[x][y])
6358 int blocking_element =
6359 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6362 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6363 element_info[element].token_name,
6364 element_info[blocking_element].token_name,
6368 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6371 element = Feld[x][y]; /* element might have changed */
6376 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6377 DrawLevelElementAnimation(x, y, element);
6379 if (element == EL_BUG ||
6380 element == EL_SPACESHIP ||
6381 element == EL_SP_SNIKSNAK)
6382 DrawLevelField(x, y);
6383 else if (element == EL_MOLE)
6384 DrawLevelField(x, y);
6385 else if (element == EL_BD_BUTTERFLY ||
6386 element == EL_BD_FIREFLY)
6387 DrawLevelElementAnimationIfNeeded(x, y, element);
6388 else if (element == EL_SATELLITE)
6389 DrawLevelElementAnimationIfNeeded(x, y, element);
6390 else if (element == EL_SP_ELECTRON)
6391 DrawLevelElementAnimationIfNeeded(x, y, element);
6394 if (DONT_TOUCH(element))
6395 TestIfBadThingTouchesHero(x, y);
6398 PlayLevelSoundAction(x, y, ACTION_WAITING);
6404 InitMovingField(x, y, MovDir[x][y]);
6406 PlayLevelSoundAction(x, y, ACTION_MOVING);
6410 ContinueMoving(x, y);
6413 void ContinueMoving(int x, int y)
6415 int element = Feld[x][y];
6416 int stored = Store[x][y];
6417 struct ElementInfo *ei = &element_info[element];
6418 int direction = MovDir[x][y];
6419 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6420 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6421 int newx = x + dx, newy = y + dy;
6423 int nextx = newx + dx, nexty = newy + dy;
6426 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6427 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6429 boolean pushed_by_player = Pushed[x][y];
6432 MovPos[x][y] += getElementMoveStepsize(x, y);
6435 if (pushed_by_player && IS_PLAYER(x, y))
6437 /* special case: moving object pushed by player */
6438 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6441 if (pushed_by_player) /* special case: moving object pushed by player */
6442 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6445 if (ABS(MovPos[x][y]) < TILEX)
6447 DrawLevelField(x, y);
6449 return; /* element is still moving */
6452 /* element reached destination field */
6454 Feld[x][y] = EL_EMPTY;
6455 Feld[newx][newy] = element;
6456 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6459 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6461 element = Feld[newx][newy] = EL_ACID;
6464 else if (element == EL_MOLE)
6466 Feld[x][y] = EL_SAND;
6468 DrawLevelFieldCrumbledSandNeighbours(x, y);
6470 else if (element == EL_QUICKSAND_FILLING)
6472 element = Feld[newx][newy] = get_next_element(element);
6473 Store[newx][newy] = Store[x][y];
6475 else if (element == EL_QUICKSAND_EMPTYING)
6477 Feld[x][y] = get_next_element(element);
6478 element = Feld[newx][newy] = Store[x][y];
6480 else if (element == EL_MAGIC_WALL_FILLING)
6482 element = Feld[newx][newy] = get_next_element(element);
6483 if (!game.magic_wall_active)
6484 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6485 Store[newx][newy] = Store[x][y];
6487 else if (element == EL_MAGIC_WALL_EMPTYING)
6489 Feld[x][y] = get_next_element(element);
6490 if (!game.magic_wall_active)
6491 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6492 element = Feld[newx][newy] = Store[x][y];
6494 else if (element == EL_BD_MAGIC_WALL_FILLING)
6496 element = Feld[newx][newy] = get_next_element(element);
6497 if (!game.magic_wall_active)
6498 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6499 Store[newx][newy] = Store[x][y];
6501 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6503 Feld[x][y] = get_next_element(element);
6504 if (!game.magic_wall_active)
6505 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6506 element = Feld[newx][newy] = Store[x][y];
6508 else if (element == EL_AMOEBA_DROPPING)
6510 Feld[x][y] = get_next_element(element);
6511 element = Feld[newx][newy] = Store[x][y];
6513 else if (element == EL_SOKOBAN_OBJECT)
6516 Feld[x][y] = Back[x][y];
6518 if (Back[newx][newy])
6519 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6521 Back[x][y] = Back[newx][newy] = 0;
6524 else if (Store[x][y] == EL_ACID)
6526 element = Feld[newx][newy] = EL_ACID;
6530 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6531 ei->move_leave_element != EL_EMPTY &&
6532 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6533 Store[x][y] != EL_EMPTY))
6535 /* some elements can leave other elements behind after moving */
6537 Feld[x][y] = ei->move_leave_element;
6538 InitField(x, y, FALSE);
6540 if (GFX_CRUMBLED(Feld[x][y]))
6541 DrawLevelFieldCrumbledSandNeighbours(x, y);
6545 Store[x][y] = EL_EMPTY;
6549 MovDelay[newx][newy] = 0;
6551 if (CAN_CHANGE(element))
6553 /* copy element change control values to new field */
6554 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6555 ChangePage[newx][newy] = ChangePage[x][y];
6556 Changed[newx][newy] = Changed[x][y];
6557 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6560 ChangeDelay[x][y] = 0;
6561 ChangePage[x][y] = -1;
6562 Changed[x][y] = CE_BITMASK_DEFAULT;
6563 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6565 /* copy animation control values to new field */
6566 GfxFrame[newx][newy] = GfxFrame[x][y];
6567 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6568 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6569 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6571 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6574 /* do this after checking for left-behind element */
6575 ResetGfxAnimation(x, y); /* reset animation values for old field */
6579 /* some elements can leave other elements behind after moving */
6581 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6582 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6583 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6585 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6586 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6590 int move_leave_element = ei->move_leave_element;
6592 Feld[x][y] = move_leave_element;
6594 #if USE_PREVIOUS_MOVE_DIR
6595 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6596 MovDir[x][y] = direction;
6599 InitField(x, y, FALSE);
6601 if (GFX_CRUMBLED(Feld[x][y]))
6602 DrawLevelFieldCrumbledSandNeighbours(x, y);
6604 if (ELEM_IS_PLAYER(move_leave_element))
6605 RelocatePlayer(x, y, move_leave_element);
6610 /* some elements can leave other elements behind after moving */
6611 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6612 ei->move_leave_element != EL_EMPTY &&
6613 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6614 ei->can_leave_element_last))
6616 Feld[x][y] = ei->move_leave_element;
6617 InitField(x, y, FALSE);
6619 if (GFX_CRUMBLED(Feld[x][y]))
6620 DrawLevelFieldCrumbledSandNeighbours(x, y);
6623 ei->can_leave_element_last = ei->can_leave_element;
6624 ei->can_leave_element = FALSE;
6628 /* do this after checking for left-behind element */
6629 ResetGfxAnimation(x, y); /* reset animation values for old field */
6633 /* 2.1.1 (does not work correctly for spring) */
6634 if (!CAN_MOVE(element))
6635 MovDir[newx][newy] = 0;
6639 /* (does not work for falling objects that slide horizontally) */
6640 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6641 MovDir[newx][newy] = 0;
6644 if (!CAN_MOVE(element) ||
6645 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6646 MovDir[newx][newy] = 0;
6650 if (!CAN_MOVE(element) ||
6651 (CAN_FALL(element) && direction == MV_DOWN))
6652 GfxDir[x][y] = MovDir[newx][newy] = 0;
6654 if (!CAN_MOVE(element) ||
6655 (CAN_FALL(element) && direction == MV_DOWN &&
6656 (element == EL_SPRING ||
6657 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6658 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6659 GfxDir[x][y] = MovDir[newx][newy] = 0;
6665 DrawLevelField(x, y);
6666 DrawLevelField(newx, newy);
6668 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6670 /* prevent pushed element from moving on in pushed direction */
6671 if (pushed_by_player && CAN_MOVE(element) &&
6672 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6673 !(element_info[element].move_pattern & direction))
6674 TurnRound(newx, newy);
6677 /* prevent elements on conveyor belt from moving on in last direction */
6678 if (pushed_by_conveyor && CAN_FALL(element) &&
6679 direction & MV_HORIZONTAL)
6682 if (CAN_MOVE(element))
6683 InitMovDir(newx, newy);
6685 MovDir[newx][newy] = 0;
6687 MovDir[newx][newy] = 0;
6692 if (!pushed_by_player)
6694 int nextx = newx + dx, nexty = newy + dy;
6695 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6697 WasJustMoving[newx][newy] = 3;
6699 if (CAN_FALL(element) && direction == MV_DOWN)
6700 WasJustFalling[newx][newy] = 3;
6702 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6703 CheckCollision[newx][newy] = 2;
6706 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6708 TestIfBadThingTouchesHero(newx, newy);
6709 TestIfBadThingTouchesFriend(newx, newy);
6711 if (!IS_CUSTOM_ELEMENT(element))
6712 TestIfBadThingTouchesOtherBadThing(newx, newy);
6714 else if (element == EL_PENGUIN)
6715 TestIfFriendTouchesBadThing(newx, newy);
6717 #if USE_NEW_MOVE_STYLE
6719 if (CAN_FALL(element) && direction == MV_DOWN &&
6720 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6721 IS_PLAYER(x, newy + 1))
6722 printf("::: we would now kill the player [%d]\n", FrameCounter);
6725 /* give the player one last chance (one more frame) to move away */
6726 if (CAN_FALL(element) && direction == MV_DOWN &&
6727 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6728 (!IS_PLAYER(x, newy + 1) ||
6729 game.engine_version < VERSION_IDENT(3,1,1,0)))
6732 if (CAN_FALL(element) && direction == MV_DOWN &&
6733 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6741 if (pushed_by_player && !game.use_change_when_pushing_bug)
6743 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6746 if (pushed_by_player)
6751 int dig_side = MV_DIR_OPPOSITE(direction);
6753 static int trigger_sides[4] =
6755 CH_SIDE_RIGHT, /* moving left */
6756 CH_SIDE_LEFT, /* moving right */
6757 CH_SIDE_BOTTOM, /* moving up */
6758 CH_SIDE_TOP, /* moving down */
6760 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6762 struct PlayerInfo *player = PLAYERINFO(x, y);
6764 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6765 player->index_bit, dig_side);
6766 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6767 player->index_bit, dig_side);
6772 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6776 if (ChangePage[newx][newy] != -1) /* delayed change */
6777 ChangeElement(newx, newy, ChangePage[newx][newy]);
6782 TestIfElementHitsCustomElement(newx, newy, direction);
6786 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6788 int hitting_element = Feld[newx][newy];
6790 /* !!! fix side (direction) orientation here and elsewhere !!! */
6791 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6795 if (IN_LEV_FIELD(nextx, nexty))
6797 int opposite_direction = MV_DIR_OPPOSITE(direction);
6798 int hitting_side = direction;
6799 int touched_side = opposite_direction;
6800 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6801 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6802 MovDir[nextx][nexty] != direction ||
6803 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6809 CheckElementChangeBySide(nextx, nexty, touched_element,
6810 CE_HIT_BY_SOMETHING, opposite_direction);
6812 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6813 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6815 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6817 struct ElementChangeInfo *change =
6818 &element_info[hitting_element].change_page[i];
6820 if (change->can_change &&
6821 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6822 change->trigger_side & touched_side &&
6823 change->trigger_element == touched_element)
6825 CheckElementChangeByPage(newx, newy, hitting_element,
6826 touched_element, CE_OTHER_IS_HITTING,i);
6832 if (IS_CUSTOM_ELEMENT(touched_element) &&
6833 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6835 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6837 struct ElementChangeInfo *change =
6838 &element_info[touched_element].change_page[i];
6840 if (change->can_change &&
6841 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6842 change->trigger_side & hitting_side &&
6843 change->trigger_element == hitting_element)
6845 CheckElementChangeByPage(nextx, nexty, touched_element,
6846 hitting_element, CE_OTHER_GETS_HIT, i);
6857 TestIfPlayerTouchesCustomElement(newx, newy);
6858 TestIfElementTouchesCustomElement(newx, newy);
6861 int AmoebeNachbarNr(int ax, int ay)
6864 int element = Feld[ax][ay];
6866 static int xy[4][2] =
6874 for (i = 0; i < NUM_DIRECTIONS; i++)
6876 int x = ax + xy[i][0];
6877 int y = ay + xy[i][1];
6879 if (!IN_LEV_FIELD(x, y))
6882 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6883 group_nr = AmoebaNr[x][y];
6889 void AmoebenVereinigen(int ax, int ay)
6891 int i, x, y, xx, yy;
6892 int new_group_nr = AmoebaNr[ax][ay];
6893 static int xy[4][2] =
6901 if (new_group_nr == 0)
6904 for (i = 0; i < NUM_DIRECTIONS; i++)
6909 if (!IN_LEV_FIELD(x, y))
6912 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6913 Feld[x][y] == EL_BD_AMOEBA ||
6914 Feld[x][y] == EL_AMOEBA_DEAD) &&
6915 AmoebaNr[x][y] != new_group_nr)
6917 int old_group_nr = AmoebaNr[x][y];
6919 if (old_group_nr == 0)
6922 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6923 AmoebaCnt[old_group_nr] = 0;
6924 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6925 AmoebaCnt2[old_group_nr] = 0;
6927 for (yy = 0; yy < lev_fieldy; yy++)
6929 for (xx = 0; xx < lev_fieldx; xx++)
6931 if (AmoebaNr[xx][yy] == old_group_nr)
6932 AmoebaNr[xx][yy] = new_group_nr;
6939 void AmoebeUmwandeln(int ax, int ay)
6943 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6945 int group_nr = AmoebaNr[ax][ay];
6950 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6951 printf("AmoebeUmwandeln(): This should never happen!\n");
6956 for (y = 0; y < lev_fieldy; y++)
6958 for (x = 0; x < lev_fieldx; x++)
6960 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6963 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6967 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6968 SND_AMOEBA_TURNING_TO_GEM :
6969 SND_AMOEBA_TURNING_TO_ROCK));
6974 static int xy[4][2] =
6982 for (i = 0; i < NUM_DIRECTIONS; i++)
6987 if (!IN_LEV_FIELD(x, y))
6990 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6992 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6993 SND_AMOEBA_TURNING_TO_GEM :
6994 SND_AMOEBA_TURNING_TO_ROCK));
7001 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7004 int group_nr = AmoebaNr[ax][ay];
7005 boolean done = FALSE;
7010 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7011 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7016 for (y = 0; y < lev_fieldy; y++)
7018 for (x = 0; x < lev_fieldx; x++)
7020 if (AmoebaNr[x][y] == group_nr &&
7021 (Feld[x][y] == EL_AMOEBA_DEAD ||
7022 Feld[x][y] == EL_BD_AMOEBA ||
7023 Feld[x][y] == EL_AMOEBA_GROWING))
7026 Feld[x][y] = new_element;
7027 InitField(x, y, FALSE);
7028 DrawLevelField(x, y);
7035 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7036 SND_BD_AMOEBA_TURNING_TO_ROCK :
7037 SND_BD_AMOEBA_TURNING_TO_GEM));
7040 void AmoebeWaechst(int x, int y)
7042 static unsigned long sound_delay = 0;
7043 static unsigned long sound_delay_value = 0;
7045 if (!MovDelay[x][y]) /* start new growing cycle */
7049 if (DelayReached(&sound_delay, sound_delay_value))
7052 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7054 if (Store[x][y] == EL_BD_AMOEBA)
7055 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7057 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7059 sound_delay_value = 30;
7063 if (MovDelay[x][y]) /* wait some time before growing bigger */
7066 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7068 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7069 6 - MovDelay[x][y]);
7071 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7074 if (!MovDelay[x][y])
7076 Feld[x][y] = Store[x][y];
7078 DrawLevelField(x, y);
7083 void AmoebaDisappearing(int x, int y)
7085 static unsigned long sound_delay = 0;
7086 static unsigned long sound_delay_value = 0;
7088 if (!MovDelay[x][y]) /* start new shrinking cycle */
7092 if (DelayReached(&sound_delay, sound_delay_value))
7093 sound_delay_value = 30;
7096 if (MovDelay[x][y]) /* wait some time before shrinking */
7099 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7101 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7102 6 - MovDelay[x][y]);
7104 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7107 if (!MovDelay[x][y])
7109 Feld[x][y] = EL_EMPTY;
7110 DrawLevelField(x, y);
7112 /* don't let mole enter this field in this cycle;
7113 (give priority to objects falling to this field from above) */
7119 void AmoebeAbleger(int ax, int ay)
7122 int element = Feld[ax][ay];
7123 int graphic = el2img(element);
7124 int newax = ax, neway = ay;
7125 static int xy[4][2] =
7133 if (!level.amoeba_speed)
7135 Feld[ax][ay] = EL_AMOEBA_DEAD;
7136 DrawLevelField(ax, ay);
7140 if (IS_ANIMATED(graphic))
7141 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7143 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7144 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7146 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7149 if (MovDelay[ax][ay])
7153 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7156 int x = ax + xy[start][0];
7157 int y = ay + xy[start][1];
7159 if (!IN_LEV_FIELD(x, y))
7163 if (IS_FREE(x, y) ||
7164 CAN_GROW_INTO(Feld[x][y]) ||
7165 Feld[x][y] == EL_QUICKSAND_EMPTY)
7171 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7172 if (IS_FREE(x, y) ||
7173 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7180 if (newax == ax && neway == ay)
7183 else /* normal or "filled" (BD style) amoeba */
7186 boolean waiting_for_player = FALSE;
7188 for (i = 0; i < NUM_DIRECTIONS; i++)
7190 int j = (start + i) % 4;
7191 int x = ax + xy[j][0];
7192 int y = ay + xy[j][1];
7194 if (!IN_LEV_FIELD(x, y))
7198 if (IS_FREE(x, y) ||
7199 CAN_GROW_INTO(Feld[x][y]) ||
7200 Feld[x][y] == EL_QUICKSAND_EMPTY)
7207 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7208 if (IS_FREE(x, y) ||
7209 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7216 else if (IS_PLAYER(x, y))
7217 waiting_for_player = TRUE;
7220 if (newax == ax && neway == ay) /* amoeba cannot grow */
7223 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7225 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7228 Feld[ax][ay] = EL_AMOEBA_DEAD;
7229 DrawLevelField(ax, ay);
7230 AmoebaCnt[AmoebaNr[ax][ay]]--;
7232 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7234 if (element == EL_AMOEBA_FULL)
7235 AmoebeUmwandeln(ax, ay);
7236 else if (element == EL_BD_AMOEBA)
7237 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7242 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7244 /* amoeba gets larger by growing in some direction */
7246 int new_group_nr = AmoebaNr[ax][ay];
7249 if (new_group_nr == 0)
7251 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7252 printf("AmoebeAbleger(): This should never happen!\n");
7257 AmoebaNr[newax][neway] = new_group_nr;
7258 AmoebaCnt[new_group_nr]++;
7259 AmoebaCnt2[new_group_nr]++;
7261 /* if amoeba touches other amoeba(s) after growing, unify them */
7262 AmoebenVereinigen(newax, neway);
7264 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7266 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7272 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7273 (neway == lev_fieldy - 1 && newax != ax))
7275 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7276 Store[newax][neway] = element;
7278 else if (neway == ay)
7280 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7282 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7284 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7289 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7290 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7291 Store[ax][ay] = EL_AMOEBA_DROP;
7292 ContinueMoving(ax, ay);
7296 DrawLevelField(newax, neway);
7299 void Life(int ax, int ay)
7302 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7304 int element = Feld[ax][ay];
7305 int graphic = el2img(element);
7306 boolean changed = FALSE;
7308 if (IS_ANIMATED(graphic))
7309 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7314 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7315 MovDelay[ax][ay] = life_time;
7317 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7320 if (MovDelay[ax][ay])
7324 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7326 int xx = ax+x1, yy = ay+y1;
7329 if (!IN_LEV_FIELD(xx, yy))
7332 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7334 int x = xx+x2, y = yy+y2;
7336 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7339 if (((Feld[x][y] == element ||
7340 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7342 (IS_FREE(x, y) && Stop[x][y]))
7346 if (xx == ax && yy == ay) /* field in the middle */
7348 if (nachbarn < life[0] || nachbarn > life[1])
7350 Feld[xx][yy] = EL_EMPTY;
7352 DrawLevelField(xx, yy);
7353 Stop[xx][yy] = TRUE;
7358 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7359 { /* free border field */
7360 if (nachbarn >= life[2] && nachbarn <= life[3])
7362 Feld[xx][yy] = element;
7363 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7365 DrawLevelField(xx, yy);
7366 Stop[xx][yy] = TRUE;
7371 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7372 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7373 { /* free border field */
7374 if (nachbarn >= life[2] && nachbarn <= life[3])
7376 Feld[xx][yy] = element;
7377 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7379 DrawLevelField(xx, yy);
7380 Stop[xx][yy] = TRUE;
7388 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7389 SND_GAME_OF_LIFE_GROWING);
7392 static void InitRobotWheel(int x, int y)
7394 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7397 static void RunRobotWheel(int x, int y)
7399 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7402 static void StopRobotWheel(int x, int y)
7404 if (ZX == x && ZY == y)
7408 static void InitTimegateWheel(int x, int y)
7411 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7413 /* another brainless, "type style" bug ... :-( */
7414 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7418 static void RunTimegateWheel(int x, int y)
7420 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7423 void CheckExit(int x, int y)
7425 if (local_player->gems_still_needed > 0 ||
7426 local_player->sokobanfields_still_needed > 0 ||
7427 local_player->lights_still_needed > 0)
7429 int element = Feld[x][y];
7430 int graphic = el2img(element);
7432 if (IS_ANIMATED(graphic))
7433 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7438 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7441 Feld[x][y] = EL_EXIT_OPENING;
7443 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7446 void CheckExitSP(int x, int y)
7448 if (local_player->gems_still_needed > 0)
7450 int element = Feld[x][y];
7451 int graphic = el2img(element);
7453 if (IS_ANIMATED(graphic))
7454 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7459 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7462 Feld[x][y] = EL_SP_EXIT_OPENING;
7464 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7467 static void CloseAllOpenTimegates()
7471 for (y = 0; y < lev_fieldy; y++)
7473 for (x = 0; x < lev_fieldx; x++)
7475 int element = Feld[x][y];
7477 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7479 Feld[x][y] = EL_TIMEGATE_CLOSING;
7481 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7483 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7490 void EdelsteinFunkeln(int x, int y)
7492 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7495 if (Feld[x][y] == EL_BD_DIAMOND)
7498 if (MovDelay[x][y] == 0) /* next animation frame */
7499 MovDelay[x][y] = 11 * !SimpleRND(500);
7501 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7505 if (setup.direct_draw && MovDelay[x][y])
7506 SetDrawtoField(DRAW_BUFFERED);
7508 DrawLevelElementAnimation(x, y, Feld[x][y]);
7510 if (MovDelay[x][y] != 0)
7512 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7513 10 - MovDelay[x][y]);
7515 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7517 if (setup.direct_draw)
7521 dest_x = FX + SCREENX(x) * TILEX;
7522 dest_y = FY + SCREENY(y) * TILEY;
7524 BlitBitmap(drawto_field, window,
7525 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7526 SetDrawtoField(DRAW_DIRECT);
7532 void MauerWaechst(int x, int y)
7536 if (!MovDelay[x][y]) /* next animation frame */
7537 MovDelay[x][y] = 3 * delay;
7539 if (MovDelay[x][y]) /* wait some time before next frame */
7543 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7545 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7546 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7548 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7551 if (!MovDelay[x][y])
7553 if (MovDir[x][y] == MV_LEFT)
7555 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7556 DrawLevelField(x - 1, y);
7558 else if (MovDir[x][y] == MV_RIGHT)
7560 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7561 DrawLevelField(x + 1, y);
7563 else if (MovDir[x][y] == MV_UP)
7565 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7566 DrawLevelField(x, y - 1);
7570 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7571 DrawLevelField(x, y + 1);
7574 Feld[x][y] = Store[x][y];
7576 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7577 DrawLevelField(x, y);
7582 void MauerAbleger(int ax, int ay)
7584 int element = Feld[ax][ay];
7585 int graphic = el2img(element);
7586 boolean oben_frei = FALSE, unten_frei = FALSE;
7587 boolean links_frei = FALSE, rechts_frei = FALSE;
7588 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7589 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7590 boolean new_wall = FALSE;
7592 if (IS_ANIMATED(graphic))
7593 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7595 if (!MovDelay[ax][ay]) /* start building new wall */
7596 MovDelay[ax][ay] = 6;
7598 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7601 if (MovDelay[ax][ay])
7605 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7607 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7609 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7611 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7614 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7615 element == EL_EXPANDABLE_WALL_ANY)
7619 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7620 Store[ax][ay-1] = element;
7621 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7622 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7623 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7624 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7629 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7630 Store[ax][ay+1] = element;
7631 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7632 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7633 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7634 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7639 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7640 element == EL_EXPANDABLE_WALL_ANY ||
7641 element == EL_EXPANDABLE_WALL)
7645 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7646 Store[ax-1][ay] = element;
7647 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7648 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7649 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7650 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7656 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7657 Store[ax+1][ay] = element;
7658 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7659 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7660 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7661 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7666 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7667 DrawLevelField(ax, ay);
7669 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7671 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7672 unten_massiv = TRUE;
7673 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7674 links_massiv = TRUE;
7675 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7676 rechts_massiv = TRUE;
7678 if (((oben_massiv && unten_massiv) ||
7679 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7680 element == EL_EXPANDABLE_WALL) &&
7681 ((links_massiv && rechts_massiv) ||
7682 element == EL_EXPANDABLE_WALL_VERTICAL))
7683 Feld[ax][ay] = EL_WALL;
7687 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7689 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7693 void CheckForDragon(int x, int y)
7696 boolean dragon_found = FALSE;
7697 static int xy[4][2] =
7705 for (i = 0; i < NUM_DIRECTIONS; i++)
7707 for (j = 0; j < 4; j++)
7709 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7711 if (IN_LEV_FIELD(xx, yy) &&
7712 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7714 if (Feld[xx][yy] == EL_DRAGON)
7715 dragon_found = TRUE;
7724 for (i = 0; i < NUM_DIRECTIONS; i++)
7726 for (j = 0; j < 3; j++)
7728 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7730 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7732 Feld[xx][yy] = EL_EMPTY;
7733 DrawLevelField(xx, yy);
7742 static void InitBuggyBase(int x, int y)
7744 int element = Feld[x][y];
7745 int activating_delay = FRAMES_PER_SECOND / 4;
7748 (element == EL_SP_BUGGY_BASE ?
7749 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7750 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7752 element == EL_SP_BUGGY_BASE_ACTIVE ?
7753 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7756 static void WarnBuggyBase(int x, int y)
7759 static int xy[4][2] =
7767 for (i = 0; i < NUM_DIRECTIONS; i++)
7769 int xx = x + xy[i][0], yy = y + xy[i][1];
7771 if (IS_PLAYER(xx, yy))
7773 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7780 static void InitTrap(int x, int y)
7782 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7785 static void ActivateTrap(int x, int y)
7787 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7790 static void ChangeActiveTrap(int x, int y)
7792 int graphic = IMG_TRAP_ACTIVE;
7794 /* if new animation frame was drawn, correct crumbled sand border */
7795 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7796 DrawLevelFieldCrumbledSand(x, y);
7799 static void ChangeElementNowExt(int x, int y, int target_element)
7801 int previous_move_direction = MovDir[x][y];
7803 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7804 IS_WALKABLE(Feld[x][y]));
7806 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7807 IS_WALKABLE(Feld[x][y]) &&
7811 /* check if element under player changes from accessible to unaccessible
7812 (needed for special case of dropping element which then changes) */
7813 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7814 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7817 printf("::: BOOOM! [%d, '%s']\n", target_element,
7818 element_info[target_element].token_name);
7830 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7831 RemoveMovingField(x, y);
7835 Feld[x][y] = target_element;
7838 Feld[x][y] = target_element;
7841 ResetGfxAnimation(x, y);
7842 ResetRandomAnimationValue(x, y);
7844 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7845 MovDir[x][y] = previous_move_direction;
7848 InitField_WithBug1(x, y, FALSE);
7850 InitField(x, y, FALSE);
7851 if (CAN_MOVE(Feld[x][y]))
7855 DrawLevelField(x, y);
7857 if (GFX_CRUMBLED(Feld[x][y]))
7858 DrawLevelFieldCrumbledSandNeighbours(x, y);
7862 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7866 TestIfBadThingTouchesHero(x, y);
7867 TestIfPlayerTouchesCustomElement(x, y);
7868 TestIfElementTouchesCustomElement(x, y);
7871 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7872 if (ELEM_IS_PLAYER(target_element))
7873 RelocatePlayer(x, y, target_element);
7876 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7880 TestIfBadThingTouchesHero(x, y);
7881 TestIfPlayerTouchesCustomElement(x, y);
7882 TestIfElementTouchesCustomElement(x, y);
7886 static boolean ChangeElementNow(int x, int y, int element, int page)
7888 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7890 int old_element = Feld[x][y];
7892 /* always use default change event to prevent running into a loop */
7893 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7894 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7896 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7898 /* reset actual trigger element and player */
7899 change->actual_trigger_element = EL_EMPTY;
7900 change->actual_trigger_player = EL_PLAYER_1;
7903 /* do not change already changed elements with same change event */
7905 if (Changed[x][y] & ChangeEvent[x][y])
7912 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7915 /* !!! indirect change before direct change !!! */
7916 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7919 if (change->explode)
7926 if (change->use_target_content)
7928 boolean complete_replace = TRUE;
7929 boolean can_replace[3][3];
7932 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7935 boolean is_walkable;
7936 boolean is_diggable;
7937 boolean is_collectible;
7938 boolean is_removable;
7939 boolean is_destructible;
7940 int ex = x + xx - 1;
7941 int ey = y + yy - 1;
7942 int content_element = change->target_content[xx][yy];
7945 can_replace[xx][yy] = TRUE;
7947 if (ex == x && ey == y) /* do not check changing element itself */
7950 if (content_element == EL_EMPTY_SPACE)
7952 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7957 if (!IN_LEV_FIELD(ex, ey))
7959 can_replace[xx][yy] = FALSE;
7960 complete_replace = FALSE;
7966 if (Changed[ex][ey]) /* do not change already changed elements */
7968 can_replace[xx][yy] = FALSE;
7969 complete_replace = FALSE;
7977 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7978 e = MovingOrBlocked2Element(ex, ey);
7983 is_empty = (IS_FREE(ex, ey) ||
7984 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7985 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7986 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7990 is_empty = (IS_FREE(ex, ey) ||
7991 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7993 is_empty = (IS_FREE(ex, ey) ||
7994 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7999 is_walkable = (is_empty || IS_WALKABLE(e));
8000 is_diggable = (is_empty || IS_DIGGABLE(e));
8001 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8002 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8003 is_removable = (is_diggable || is_collectible);
8005 can_replace[xx][yy] =
8006 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8007 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8008 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8009 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8010 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8011 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8012 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8014 if (!can_replace[xx][yy])
8015 complete_replace = FALSE;
8017 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8018 IS_WALKABLE(content_element)));
8020 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8022 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8025 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8026 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8027 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8029 can_replace[xx][yy] = FALSE;
8030 complete_replace = FALSE;
8035 if (!change->only_if_complete || complete_replace)
8037 boolean something_has_changed = FALSE;
8039 if (change->only_if_complete && change->use_random_replace &&
8040 RND(100) < change->random_percentage)
8043 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8045 int ex = x + xx - 1;
8046 int ey = y + yy - 1;
8047 int content_element;
8049 if (can_replace[xx][yy] && (!change->use_random_replace ||
8050 RND(100) < change->random_percentage))
8052 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8053 RemoveMovingField(ex, ey);
8055 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8057 content_element = change->target_content[xx][yy];
8058 target_element = GET_TARGET_ELEMENT(content_element, change);
8060 ChangeElementNowExt(ex, ey, target_element);
8062 something_has_changed = TRUE;
8064 /* for symmetry reasons, freeze newly created border elements */
8065 if (ex != x || ey != y)
8066 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8070 if (something_has_changed)
8071 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8076 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8078 ChangeElementNowExt(x, y, target_element);
8080 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8084 /* this uses direct change before indirect change */
8085 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
8091 static void ChangeElement(int x, int y, int page)
8093 int element = MovingOrBlocked2Element(x, y);
8094 struct ElementInfo *ei = &element_info[element];
8095 struct ElementChangeInfo *change = &ei->change_page[page];
8098 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8101 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8102 x, y, element, element_info[element].token_name);
8103 printf("ChangeElement(): This should never happen!\n");
8108 /* this can happen with classic bombs on walkable, changing elements */
8109 if (!CAN_CHANGE(element))
8112 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8113 ChangeDelay[x][y] = 0;
8119 if (ChangeDelay[x][y] == 0) /* initialize element change */
8121 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8122 RND(change->delay_random * change->delay_frames)) + 1;
8124 ResetGfxAnimation(x, y);
8125 ResetRandomAnimationValue(x, y);
8127 if (change->pre_change_function)
8128 change->pre_change_function(x, y);
8131 ChangeDelay[x][y]--;
8133 if (ChangeDelay[x][y] != 0) /* continue element change */
8135 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8137 if (IS_ANIMATED(graphic))
8138 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8140 if (change->change_function)
8141 change->change_function(x, y);
8143 else /* finish element change */
8145 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8147 page = ChangePage[x][y];
8148 ChangePage[x][y] = -1;
8150 change = &ei->change_page[page];
8154 if (IS_MOVING(x, y) && !change->explode)
8156 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8159 ChangeDelay[x][y] = 1; /* try change after next move step */
8160 ChangePage[x][y] = page; /* remember page to use for change */
8165 if (ChangeElementNow(x, y, element, page))
8167 if (change->post_change_function)
8168 change->post_change_function(x, y);
8173 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8174 int trigger_element,
8181 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8183 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8186 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8188 int element = EL_CUSTOM_START + i;
8190 boolean change_element = FALSE;
8193 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8196 for (j = 0; j < element_info[element].num_change_pages; j++)
8198 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8200 if (change->can_change &&
8201 change->events & CH_EVENT_BIT(trigger_event) &&
8202 change->trigger_side & trigger_side &&
8203 change->trigger_player & trigger_player &&
8204 change->trigger_page & trigger_page_bits &&
8205 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8208 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8209 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8210 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8213 change_element = TRUE;
8216 change->actual_trigger_element = trigger_element;
8217 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8223 if (!change_element)
8226 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8229 if (x == lx && y == ly) /* do not change trigger element itself */
8233 if (Feld[x][y] == element)
8235 ChangeDelay[x][y] = 1;
8236 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8237 ChangeElement(x, y, page);
8245 static boolean CheckElementChangeExt(int x, int y,
8247 int trigger_element,
8253 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8256 if (Feld[x][y] == EL_BLOCKED)
8258 Blocked2Moving(x, y, &x, &y);
8259 element = Feld[x][y];
8263 if (Feld[x][y] != element) /* check if element has already changed */
8266 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8267 Feld[x][y], element_info[Feld[x][y]].token_name,
8268 element, element_info[element].token_name,
8277 if (trigger_page < 0)
8279 boolean change_element = FALSE;
8282 for (i = 0; i < element_info[element].num_change_pages; i++)
8284 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8286 if (change->can_change &&
8287 change->events & CH_EVENT_BIT(trigger_event) &&
8288 change->trigger_side & trigger_side &&
8289 change->trigger_player & trigger_player)
8291 change_element = TRUE;
8294 change->actual_trigger_element = trigger_element;
8295 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8301 if (!change_element)
8306 struct ElementInfo *ei = &element_info[element];
8307 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8309 change->actual_trigger_element = trigger_element;
8310 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8315 /* !!! this check misses pages with same event, but different side !!! */
8317 if (trigger_page < 0)
8318 trigger_page = element_info[element].event_page_nr[trigger_event];
8320 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8324 ChangeDelay[x][y] = 1;
8325 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8326 ChangeElement(x, y, trigger_page);
8331 static void PlayPlayerSound(struct PlayerInfo *player)
8333 int jx = player->jx, jy = player->jy;
8334 int element = player->element_nr;
8335 int last_action = player->last_action_waiting;
8336 int action = player->action_waiting;
8338 if (player->is_waiting)
8340 if (action != last_action)
8341 PlayLevelSoundElementAction(jx, jy, element, action);
8343 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8347 if (action != last_action)
8348 StopSound(element_info[element].sound[last_action]);
8350 if (last_action == ACTION_SLEEPING)
8351 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8355 static void PlayAllPlayersSound()
8359 for (i = 0; i < MAX_PLAYERS; i++)
8360 if (stored_player[i].active)
8361 PlayPlayerSound(&stored_player[i]);
8364 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8366 boolean last_waiting = player->is_waiting;
8367 int move_dir = player->MovDir;
8369 player->last_action_waiting = player->action_waiting;
8373 if (!last_waiting) /* not waiting -> waiting */
8375 player->is_waiting = TRUE;
8377 player->frame_counter_bored =
8379 game.player_boring_delay_fixed +
8380 SimpleRND(game.player_boring_delay_random);
8381 player->frame_counter_sleeping =
8383 game.player_sleeping_delay_fixed +
8384 SimpleRND(game.player_sleeping_delay_random);
8386 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8389 if (game.player_sleeping_delay_fixed +
8390 game.player_sleeping_delay_random > 0 &&
8391 player->anim_delay_counter == 0 &&
8392 player->post_delay_counter == 0 &&
8393 FrameCounter >= player->frame_counter_sleeping)
8394 player->is_sleeping = TRUE;
8395 else if (game.player_boring_delay_fixed +
8396 game.player_boring_delay_random > 0 &&
8397 FrameCounter >= player->frame_counter_bored)
8398 player->is_bored = TRUE;
8400 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8401 player->is_bored ? ACTION_BORING :
8404 if (player->is_sleeping)
8406 if (player->num_special_action_sleeping > 0)
8408 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8410 int last_special_action = player->special_action_sleeping;
8411 int num_special_action = player->num_special_action_sleeping;
8412 int special_action =
8413 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8414 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8415 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8416 last_special_action + 1 : ACTION_SLEEPING);
8417 int special_graphic =
8418 el_act_dir2img(player->element_nr, special_action, move_dir);
8420 player->anim_delay_counter =
8421 graphic_info[special_graphic].anim_delay_fixed +
8422 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8423 player->post_delay_counter =
8424 graphic_info[special_graphic].post_delay_fixed +
8425 SimpleRND(graphic_info[special_graphic].post_delay_random);
8427 player->special_action_sleeping = special_action;
8430 if (player->anim_delay_counter > 0)
8432 player->action_waiting = player->special_action_sleeping;
8433 player->anim_delay_counter--;
8435 else if (player->post_delay_counter > 0)
8437 player->post_delay_counter--;
8441 else if (player->is_bored)
8443 if (player->num_special_action_bored > 0)
8445 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8447 int special_action =
8448 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8449 int special_graphic =
8450 el_act_dir2img(player->element_nr, special_action, move_dir);
8452 player->anim_delay_counter =
8453 graphic_info[special_graphic].anim_delay_fixed +
8454 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8455 player->post_delay_counter =
8456 graphic_info[special_graphic].post_delay_fixed +
8457 SimpleRND(graphic_info[special_graphic].post_delay_random);
8459 player->special_action_bored = special_action;
8462 if (player->anim_delay_counter > 0)
8464 player->action_waiting = player->special_action_bored;
8465 player->anim_delay_counter--;
8467 else if (player->post_delay_counter > 0)
8469 player->post_delay_counter--;
8474 else if (last_waiting) /* waiting -> not waiting */
8476 player->is_waiting = FALSE;
8477 player->is_bored = FALSE;
8478 player->is_sleeping = FALSE;
8480 player->frame_counter_bored = -1;
8481 player->frame_counter_sleeping = -1;
8483 player->anim_delay_counter = 0;
8484 player->post_delay_counter = 0;
8486 player->action_waiting = ACTION_DEFAULT;
8488 player->special_action_bored = ACTION_DEFAULT;
8489 player->special_action_sleeping = ACTION_DEFAULT;
8494 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8497 static byte stored_player_action[MAX_PLAYERS];
8498 static int num_stored_actions = 0;
8500 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8501 int left = player_action & JOY_LEFT;
8502 int right = player_action & JOY_RIGHT;
8503 int up = player_action & JOY_UP;
8504 int down = player_action & JOY_DOWN;
8505 int button1 = player_action & JOY_BUTTON_1;
8506 int button2 = player_action & JOY_BUTTON_2;
8507 int dx = (left ? -1 : right ? 1 : 0);
8508 int dy = (up ? -1 : down ? 1 : 0);
8511 stored_player_action[player->index_nr] = 0;
8512 num_stored_actions++;
8516 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8519 if (!player->active || tape.pausing)
8523 printf("::: [%d %d %d %d] [%d %d]\n",
8524 left, right, up, down, button1, button2);
8530 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8535 if (player->MovPos == 0)
8536 CheckGravityMovement(player);
8539 snapped = SnapField(player, dx, dy);
8543 dropped = DropElement(player);
8545 moved = MovePlayer(player, dx, dy);
8548 if (tape.single_step && tape.recording && !tape.pausing)
8550 if (button1 || (dropped && !moved))
8552 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8553 SnapField(player, 0, 0); /* stop snapping */
8557 SetPlayerWaiting(player, FALSE);
8560 return player_action;
8562 stored_player_action[player->index_nr] = player_action;
8568 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8571 /* no actions for this player (no input at player's configured device) */
8573 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8574 SnapField(player, 0, 0);
8575 CheckGravityMovementWhenNotMoving(player);
8577 if (player->MovPos == 0)
8578 SetPlayerWaiting(player, TRUE);
8580 if (player->MovPos == 0) /* needed for tape.playing */
8581 player->is_moving = FALSE;
8583 player->is_dropping = FALSE;
8589 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8591 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8593 TapeRecordAction(stored_player_action);
8594 num_stored_actions = 0;
8601 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8603 static byte stored_player_action[MAX_PLAYERS];
8604 static int num_stored_actions = 0;
8605 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8606 int left = player_action & JOY_LEFT;
8607 int right = player_action & JOY_RIGHT;
8608 int up = player_action & JOY_UP;
8609 int down = player_action & JOY_DOWN;
8610 int button1 = player_action & JOY_BUTTON_1;
8611 int button2 = player_action & JOY_BUTTON_2;
8612 int dx = (left ? -1 : right ? 1 : 0);
8613 int dy = (up ? -1 : down ? 1 : 0);
8615 stored_player_action[player->index_nr] = 0;
8616 num_stored_actions++;
8618 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8620 if (!player->active || tape.pausing)
8625 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8628 snapped = SnapField(player, dx, dy);
8632 dropped = DropElement(player);
8634 moved = MovePlayer(player, dx, dy);
8637 if (tape.single_step && tape.recording && !tape.pausing)
8639 if (button1 || (dropped && !moved))
8641 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8642 SnapField(player, 0, 0); /* stop snapping */
8646 stored_player_action[player->index_nr] = player_action;
8650 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8652 /* no actions for this player (no input at player's configured device) */
8654 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8655 SnapField(player, 0, 0);
8656 CheckGravityMovementWhenNotMoving(player);
8658 if (player->MovPos == 0)
8659 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8661 if (player->MovPos == 0) /* needed for tape.playing */
8662 player->is_moving = FALSE;
8665 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8667 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8669 TapeRecordAction(stored_player_action);
8670 num_stored_actions = 0;
8675 void AdvanceFrameAndPlayerCounters(int player_nr)
8679 /* advance frame counters (global frame counter and time frame counter) */
8683 /* advance player counters (counters for move delay, move animation etc.) */
8684 for (i = 0; i < MAX_PLAYERS; i++)
8686 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8688 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8690 if (!advance_player_counters) /* not all players may be affected */
8693 stored_player[i].Frame += move_frames;
8695 if (stored_player[i].MovPos != 0)
8696 stored_player[i].StepFrame += move_frames;
8698 #if USE_NEW_MOVE_DELAY
8699 if (stored_player[i].move_delay > 0)
8700 stored_player[i].move_delay--;
8703 #if USE_NEW_PUSH_DELAY
8704 /* due to bugs in previous versions, counter must count up, not down */
8705 if (stored_player[i].push_delay != -1)
8706 stored_player[i].push_delay++;
8709 if (stored_player[i].drop_delay > 0)
8710 stored_player[i].drop_delay--;
8716 static unsigned long game_frame_delay = 0;
8717 unsigned long game_frame_delay_value;
8718 int magic_wall_x = 0, magic_wall_y = 0;
8719 int i, x, y, element, graphic;
8720 byte *recorded_player_action;
8721 byte summarized_player_action = 0;
8723 byte tape_action[MAX_PLAYERS];
8726 if (game_status != GAME_MODE_PLAYING)
8729 game_frame_delay_value =
8730 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8732 if (tape.playing && tape.warp_forward && !tape.pausing)
8733 game_frame_delay_value = 0;
8735 /* ---------- main game synchronization point ---------- */
8737 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8739 if (network_playing && !network_player_action_received)
8743 printf("DEBUG: try to get network player actions in time\n");
8747 #if defined(NETWORK_AVALIABLE)
8748 /* last chance to get network player actions without main loop delay */
8752 if (game_status != GAME_MODE_PLAYING)
8755 if (!network_player_action_received)
8759 printf("DEBUG: failed to get network player actions in time\n");
8770 printf("::: getting new tape action [%d]\n", FrameCounter);
8773 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8776 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8777 if (recorded_player_action == NULL && tape.pausing)
8782 printf("::: %d\n", stored_player[0].action);
8786 if (recorded_player_action != NULL)
8787 for (i = 0; i < MAX_PLAYERS; i++)
8788 stored_player[i].action = recorded_player_action[i];
8791 for (i = 0; i < MAX_PLAYERS; i++)
8793 summarized_player_action |= stored_player[i].action;
8795 if (!network_playing)
8796 stored_player[i].effective_action = stored_player[i].action;
8799 #if defined(NETWORK_AVALIABLE)
8800 if (network_playing)
8801 SendToServer_MovePlayer(summarized_player_action);
8804 if (!options.network && !setup.team_mode)
8805 local_player->effective_action = summarized_player_action;
8808 if (recorded_player_action != NULL)
8809 for (i = 0; i < MAX_PLAYERS; i++)
8810 stored_player[i].effective_action = recorded_player_action[i];
8814 for (i = 0; i < MAX_PLAYERS; i++)
8816 tape_action[i] = stored_player[i].effective_action;
8818 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8819 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8822 /* only save actions from input devices, but not programmed actions */
8824 TapeRecordAction(tape_action);
8827 for (i = 0; i < MAX_PLAYERS; i++)
8829 int actual_player_action = stored_player[i].effective_action;
8832 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8833 - rnd_equinox_tetrachloride 048
8834 - rnd_equinox_tetrachloride_ii 096
8835 - rnd_emanuel_schmieg 002
8836 - doctor_sloan_ww 001, 020
8838 if (stored_player[i].MovPos == 0)
8839 CheckGravityMovement(&stored_player[i]);
8843 /* overwrite programmed action with tape action */
8844 if (stored_player[i].programmed_action)
8845 actual_player_action = stored_player[i].programmed_action;
8849 if (stored_player[i].programmed_action)
8850 printf("::: %d\n", stored_player[i].programmed_action);
8853 if (recorded_player_action)
8856 if (stored_player[i].programmed_action &&
8857 stored_player[i].programmed_action != recorded_player_action[i])
8858 printf("::: %d: %d <-> %d\n", i,
8859 stored_player[i].programmed_action, recorded_player_action[i]);
8863 actual_player_action = recorded_player_action[i];
8868 /* overwrite tape action with programmed action */
8869 if (stored_player[i].programmed_action)
8870 actual_player_action = stored_player[i].programmed_action;
8875 printf("::: action: %d: %x [%d]\n",
8876 stored_player[i].MovPos, actual_player_action, FrameCounter);
8880 PlayerActions(&stored_player[i], actual_player_action);
8882 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8884 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8885 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8888 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8893 TapeRecordAction(tape_action);
8896 network_player_action_received = FALSE;
8898 ScrollScreen(NULL, SCROLL_GO_ON);
8904 for (i = 0; i < MAX_PLAYERS; i++)
8905 stored_player[i].Frame++;
8909 /* for backwards compatibility, the following code emulates a fixed bug that
8910 occured when pushing elements (causing elements that just made their last
8911 pushing step to already (if possible) make their first falling step in the
8912 same game frame, which is bad); this code is also needed to use the famous
8913 "spring push bug" which is used in older levels and might be wanted to be
8914 used also in newer levels, but in this case the buggy pushing code is only
8915 affecting the "spring" element and no other elements */
8918 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8920 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8923 for (i = 0; i < MAX_PLAYERS; i++)
8925 struct PlayerInfo *player = &stored_player[i];
8930 if (player->active && player->is_pushing && player->is_moving &&
8932 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8933 Feld[x][y] == EL_SPRING))
8935 if (player->active && player->is_pushing && player->is_moving &&
8939 ContinueMoving(x, y);
8941 /* continue moving after pushing (this is actually a bug) */
8942 if (!IS_MOVING(x, y))
8951 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8953 Changed[x][y] = CE_BITMASK_DEFAULT;
8954 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8956 #if USE_NEW_BLOCK_STYLE
8957 /* this must be handled before main playfield loop */
8958 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8961 if (MovDelay[x][y] <= 0)
8967 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8969 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8970 printf("GameActions(): This should never happen!\n");
8972 ChangePage[x][y] = -1;
8977 if (WasJustMoving[x][y] > 0)
8978 WasJustMoving[x][y]--;
8979 if (WasJustFalling[x][y] > 0)
8980 WasJustFalling[x][y]--;
8981 if (CheckCollision[x][y] > 0)
8982 CheckCollision[x][y]--;
8987 /* reset finished pushing action (not done in ContinueMoving() to allow
8988 continous pushing animation for elements with zero push delay) */
8989 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8991 ResetGfxAnimation(x, y);
8992 DrawLevelField(x, y);
8997 if (IS_BLOCKED(x, y))
9001 Blocked2Moving(x, y, &oldx, &oldy);
9002 if (!IS_MOVING(oldx, oldy))
9004 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9005 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9006 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9007 printf("GameActions(): This should never happen!\n");
9013 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9015 element = Feld[x][y];
9017 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9019 graphic = el2img(element);
9025 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9027 element = graphic = 0;
9031 if (graphic_info[graphic].anim_global_sync)
9032 GfxFrame[x][y] = FrameCounter;
9034 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9035 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9036 ResetRandomAnimationValue(x, y);
9038 SetRandomAnimationValue(x, y);
9041 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9044 if (IS_INACTIVE(element))
9046 if (IS_ANIMATED(graphic))
9047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9053 /* this may take place after moving, so 'element' may have changed */
9055 if (IS_CHANGING(x, y))
9057 if (IS_CHANGING(x, y) &&
9058 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9062 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9063 element_info[element].event_page_nr[CE_DELAY]);
9065 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9068 element = Feld[x][y];
9069 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9073 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9078 element = Feld[x][y];
9079 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9081 if (element == EL_MOLE)
9082 printf("::: %d, %d, %d [%d]\n",
9083 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9087 if (element == EL_YAMYAM)
9088 printf("::: %d, %d, %d\n",
9089 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9093 if (IS_ANIMATED(graphic) &&
9097 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9100 if (element == EL_BUG)
9101 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9105 if (element == EL_MOLE)
9106 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9110 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9111 EdelsteinFunkeln(x, y);
9113 else if ((element == EL_ACID ||
9114 element == EL_EXIT_OPEN ||
9115 element == EL_SP_EXIT_OPEN ||
9116 element == EL_SP_TERMINAL ||
9117 element == EL_SP_TERMINAL_ACTIVE ||
9118 element == EL_EXTRA_TIME ||
9119 element == EL_SHIELD_NORMAL ||
9120 element == EL_SHIELD_DEADLY) &&
9121 IS_ANIMATED(graphic))
9122 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9123 else if (IS_MOVING(x, y))
9124 ContinueMoving(x, y);
9125 else if (IS_ACTIVE_BOMB(element))
9126 CheckDynamite(x, y);
9128 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9129 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9131 else if (element == EL_AMOEBA_GROWING)
9132 AmoebeWaechst(x, y);
9133 else if (element == EL_AMOEBA_SHRINKING)
9134 AmoebaDisappearing(x, y);
9136 #if !USE_NEW_AMOEBA_CODE
9137 else if (IS_AMOEBALIVE(element))
9138 AmoebeAbleger(x, y);
9141 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9143 else if (element == EL_EXIT_CLOSED)
9145 else if (element == EL_SP_EXIT_CLOSED)
9147 else if (element == EL_EXPANDABLE_WALL_GROWING)
9149 else if (element == EL_EXPANDABLE_WALL ||
9150 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9151 element == EL_EXPANDABLE_WALL_VERTICAL ||
9152 element == EL_EXPANDABLE_WALL_ANY)
9154 else if (element == EL_FLAMES)
9155 CheckForDragon(x, y);
9157 else if (IS_AUTO_CHANGING(element))
9158 ChangeElement(x, y);
9160 else if (element == EL_EXPLOSION)
9161 ; /* drawing of correct explosion animation is handled separately */
9162 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9163 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9166 /* this may take place after moving, so 'element' may have changed */
9167 if (IS_AUTO_CHANGING(Feld[x][y]))
9168 ChangeElement(x, y);
9171 if (IS_BELT_ACTIVE(element))
9172 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9174 if (game.magic_wall_active)
9176 int jx = local_player->jx, jy = local_player->jy;
9178 /* play the element sound at the position nearest to the player */
9179 if ((element == EL_MAGIC_WALL_FULL ||
9180 element == EL_MAGIC_WALL_ACTIVE ||
9181 element == EL_MAGIC_WALL_EMPTYING ||
9182 element == EL_BD_MAGIC_WALL_FULL ||
9183 element == EL_BD_MAGIC_WALL_ACTIVE ||
9184 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9185 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9193 #if USE_NEW_AMOEBA_CODE
9194 /* new experimental amoeba growth stuff */
9196 if (!(FrameCounter % 8))
9199 static unsigned long random = 1684108901;
9201 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9204 x = (random >> 10) % lev_fieldx;
9205 y = (random >> 20) % lev_fieldy;
9207 x = RND(lev_fieldx);
9208 y = RND(lev_fieldy);
9210 element = Feld[x][y];
9213 if (!IS_PLAYER(x,y) &&
9214 (element == EL_EMPTY ||
9215 CAN_GROW_INTO(element) ||
9216 element == EL_QUICKSAND_EMPTY ||
9217 element == EL_ACID_SPLASH_LEFT ||
9218 element == EL_ACID_SPLASH_RIGHT))
9220 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9221 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9222 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9223 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9224 Feld[x][y] = EL_AMOEBA_DROP;
9227 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9228 if (!IS_PLAYER(x,y) &&
9229 (element == EL_EMPTY ||
9230 element == EL_SAND ||
9231 element == EL_QUICKSAND_EMPTY ||
9232 element == EL_ACID_SPLASH_LEFT ||
9233 element == EL_ACID_SPLASH_RIGHT))
9235 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9236 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9237 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9238 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9239 Feld[x][y] = EL_AMOEBA_DROP;
9243 random = random * 129 + 1;
9249 if (game.explosions_delayed)
9252 game.explosions_delayed = FALSE;
9254 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9256 element = Feld[x][y];
9258 if (ExplodeField[x][y])
9259 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9260 else if (element == EL_EXPLOSION)
9261 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9263 ExplodeField[x][y] = EX_TYPE_NONE;
9266 game.explosions_delayed = TRUE;
9269 if (game.magic_wall_active)
9271 if (!(game.magic_wall_time_left % 4))
9273 int element = Feld[magic_wall_x][magic_wall_y];
9275 if (element == EL_BD_MAGIC_WALL_FULL ||
9276 element == EL_BD_MAGIC_WALL_ACTIVE ||
9277 element == EL_BD_MAGIC_WALL_EMPTYING)
9278 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9280 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9283 if (game.magic_wall_time_left > 0)
9285 game.magic_wall_time_left--;
9286 if (!game.magic_wall_time_left)
9288 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9290 element = Feld[x][y];
9292 if (element == EL_MAGIC_WALL_ACTIVE ||
9293 element == EL_MAGIC_WALL_FULL)
9295 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9296 DrawLevelField(x, y);
9298 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9299 element == EL_BD_MAGIC_WALL_FULL)
9301 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9302 DrawLevelField(x, y);
9306 game.magic_wall_active = FALSE;
9311 if (game.light_time_left > 0)
9313 game.light_time_left--;
9315 if (game.light_time_left == 0)
9316 RedrawAllLightSwitchesAndInvisibleElements();
9319 if (game.timegate_time_left > 0)
9321 game.timegate_time_left--;
9323 if (game.timegate_time_left == 0)
9324 CloseAllOpenTimegates();
9327 for (i = 0; i < MAX_PLAYERS; i++)
9329 struct PlayerInfo *player = &stored_player[i];
9331 if (SHIELD_ON(player))
9333 if (player->shield_deadly_time_left)
9334 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9335 else if (player->shield_normal_time_left)
9336 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9340 if (TimeFrames >= FRAMES_PER_SECOND)
9345 for (i = 0; i < MAX_PLAYERS; i++)
9347 struct PlayerInfo *player = &stored_player[i];
9349 if (SHIELD_ON(player))
9351 player->shield_normal_time_left--;
9353 if (player->shield_deadly_time_left > 0)
9354 player->shield_deadly_time_left--;
9358 if (!level.use_step_counter)
9366 if (TimeLeft <= 10 && setup.time_limit)
9367 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9369 DrawGameValue_Time(TimeLeft);
9371 if (!TimeLeft && setup.time_limit)
9372 for (i = 0; i < MAX_PLAYERS; i++)
9373 KillHero(&stored_player[i]);
9375 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9376 DrawGameValue_Time(TimePlayed);
9379 if (tape.recording || tape.playing)
9380 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9384 PlayAllPlayersSound();
9386 if (options.debug) /* calculate frames per second */
9388 static unsigned long fps_counter = 0;
9389 static int fps_frames = 0;
9390 unsigned long fps_delay_ms = Counter() - fps_counter;
9394 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9396 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9399 fps_counter = Counter();
9402 redraw_mask |= REDRAW_FPS;
9406 if (stored_player[0].jx != stored_player[0].last_jx ||
9407 stored_player[0].jy != stored_player[0].last_jy)
9408 printf("::: %d, %d, %d, %d, %d\n",
9409 stored_player[0].MovDir,
9410 stored_player[0].MovPos,
9411 stored_player[0].GfxPos,
9412 stored_player[0].Frame,
9413 stored_player[0].StepFrame);
9416 #if USE_NEW_MOVE_DELAY
9417 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9422 for (i = 0; i < MAX_PLAYERS; i++)
9425 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9427 stored_player[i].Frame += move_frames;
9429 if (stored_player[i].MovPos != 0)
9430 stored_player[i].StepFrame += move_frames;
9432 #if USE_NEW_MOVE_DELAY
9433 if (stored_player[i].move_delay > 0)
9434 stored_player[i].move_delay--;
9437 if (stored_player[i].drop_delay > 0)
9438 stored_player[i].drop_delay--;
9443 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9445 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9447 local_player->show_envelope = 0;
9451 #if USE_NEW_RANDOMIZE
9452 /* use random number generator in every frame to make it less predictable */
9453 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9458 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9460 int min_x = x, min_y = y, max_x = x, max_y = y;
9463 for (i = 0; i < MAX_PLAYERS; i++)
9465 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9467 if (!stored_player[i].active || &stored_player[i] == player)
9470 min_x = MIN(min_x, jx);
9471 min_y = MIN(min_y, jy);
9472 max_x = MAX(max_x, jx);
9473 max_y = MAX(max_y, jy);
9476 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9479 static boolean AllPlayersInVisibleScreen()
9483 for (i = 0; i < MAX_PLAYERS; i++)
9485 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9487 if (!stored_player[i].active)
9490 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9497 void ScrollLevel(int dx, int dy)
9499 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9502 BlitBitmap(drawto_field, drawto_field,
9503 FX + TILEX * (dx == -1) - softscroll_offset,
9504 FY + TILEY * (dy == -1) - softscroll_offset,
9505 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9506 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9507 FX + TILEX * (dx == 1) - softscroll_offset,
9508 FY + TILEY * (dy == 1) - softscroll_offset);
9512 x = (dx == 1 ? BX1 : BX2);
9513 for (y = BY1; y <= BY2; y++)
9514 DrawScreenField(x, y);
9519 y = (dy == 1 ? BY1 : BY2);
9520 for (x = BX1; x <= BX2; x++)
9521 DrawScreenField(x, y);
9524 redraw_mask |= REDRAW_FIELD;
9528 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9530 int nextx = x + dx, nexty = y + dy;
9531 int element = Feld[x][y];
9534 element != EL_SP_PORT_LEFT &&
9535 element != EL_SP_GRAVITY_PORT_LEFT &&
9536 element != EL_SP_PORT_HORIZONTAL &&
9537 element != EL_SP_PORT_ANY) ||
9539 element != EL_SP_PORT_RIGHT &&
9540 element != EL_SP_GRAVITY_PORT_RIGHT &&
9541 element != EL_SP_PORT_HORIZONTAL &&
9542 element != EL_SP_PORT_ANY) ||
9544 element != EL_SP_PORT_UP &&
9545 element != EL_SP_GRAVITY_PORT_UP &&
9546 element != EL_SP_PORT_VERTICAL &&
9547 element != EL_SP_PORT_ANY) ||
9549 element != EL_SP_PORT_DOWN &&
9550 element != EL_SP_GRAVITY_PORT_DOWN &&
9551 element != EL_SP_PORT_VERTICAL &&
9552 element != EL_SP_PORT_ANY) ||
9553 !IN_LEV_FIELD(nextx, nexty) ||
9554 !IS_FREE(nextx, nexty))
9561 static boolean canFallDown(struct PlayerInfo *player)
9563 int jx = player->jx, jy = player->jy;
9565 return (IN_LEV_FIELD(jx, jy + 1) &&
9566 (IS_FREE(jx, jy + 1) ||
9567 #if USE_NEW_BLOCK_STYLE
9568 #if USE_GRAVITY_BUGFIX_OLD
9569 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9572 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9573 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9574 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9577 static boolean canPassField(int x, int y, int move_dir)
9579 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9580 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9581 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9584 int element = Feld[x][y];
9586 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9587 !CAN_MOVE(element) &&
9588 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9589 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9590 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9593 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9595 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9596 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9597 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9601 int nextx = newx + dx;
9602 int nexty = newy + dy;
9606 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9607 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9609 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9611 (IS_DIGGABLE(Feld[newx][newy]) ||
9612 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9613 canPassField(newx, newy, move_dir)));
9616 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9617 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9618 (IS_DIGGABLE(Feld[newx][newy]) ||
9619 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9620 canPassField(newx, newy, move_dir)));
9623 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9624 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9625 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9626 canPassField(newx, newy, move_dir)));
9628 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9629 (IS_DIGGABLE(Feld[newx][newy]) ||
9630 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9631 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9632 !CAN_MOVE(Feld[newx][newy]) &&
9633 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9634 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9635 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9641 static void CheckGravityMovement(struct PlayerInfo *player)
9643 if (game.gravity && !player->programmed_action)
9646 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9647 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9649 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9650 int move_dir_vertical = player->action & MV_VERTICAL;
9654 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9656 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9659 int jx = player->jx, jy = player->jy;
9661 boolean player_is_moving_to_valid_field =
9662 (!player_is_snapping &&
9663 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9664 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9668 (player->last_move_dir & MV_HORIZONTAL ?
9669 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9670 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9674 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9675 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9676 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9677 int new_jx = jx + dx, new_jy = jy + dy;
9678 int nextx = new_jx + dx, nexty = new_jy + dy;
9684 boolean player_can_fall_down = canFallDown(player);
9686 boolean player_can_fall_down =
9687 (IN_LEV_FIELD(jx, jy + 1) &&
9688 (IS_FREE(jx, jy + 1) ||
9689 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9693 boolean player_can_fall_down =
9694 (IN_LEV_FIELD(jx, jy + 1) &&
9695 (IS_FREE(jx, jy + 1)));
9699 boolean player_is_moving_to_valid_field =
9702 !player_is_snapping &&
9706 IN_LEV_FIELD(new_jx, new_jy) &&
9707 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9708 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9709 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9710 IN_LEV_FIELD(nextx, nexty) &&
9711 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9713 IN_LEV_FIELD(new_jx, new_jy) &&
9714 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9715 Feld[new_jx][new_jy] == EL_SAND ||
9716 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9717 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9718 /* !!! extend EL_SAND to anything diggable !!! */
9724 boolean player_is_standing_on_valid_field =
9725 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9726 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9730 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9731 player_can_fall_down,
9732 player_is_standing_on_valid_field,
9733 player_is_moving_to_valid_field,
9734 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9735 player->effective_action,
9736 player->can_fall_into_acid);
9739 if (player_can_fall_down &&
9741 !player_is_standing_on_valid_field &&
9743 !player_is_moving_to_valid_field)
9746 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9747 jx, jy, FrameCounter);
9750 player->programmed_action = MV_DOWN;
9755 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9758 return CheckGravityMovement(player);
9761 if (game.gravity && !player->programmed_action)
9763 int jx = player->jx, jy = player->jy;
9764 boolean field_under_player_is_free =
9765 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9766 boolean player_is_standing_on_valid_field =
9767 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9768 (IS_WALKABLE(Feld[jx][jy]) &&
9769 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9771 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9772 player->programmed_action = MV_DOWN;
9778 -----------------------------------------------------------------------------
9779 dx, dy: direction (non-diagonal) to try to move the player to
9780 real_dx, real_dy: direction as read from input device (can be diagonal)
9783 boolean MovePlayerOneStep(struct PlayerInfo *player,
9784 int dx, int dy, int real_dx, int real_dy)
9787 static int trigger_sides[4][2] =
9789 /* enter side leave side */
9790 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9791 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9792 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9793 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9795 int move_direction = (dx == -1 ? MV_LEFT :
9796 dx == +1 ? MV_RIGHT :
9798 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9799 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9800 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9802 int jx = player->jx, jy = player->jy;
9803 int new_jx = jx + dx, new_jy = jy + dy;
9807 if (!player->active || (!dx && !dy))
9808 return MF_NO_ACTION;
9810 player->MovDir = (dx < 0 ? MV_LEFT :
9813 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9815 if (!IN_LEV_FIELD(new_jx, new_jy))
9816 return MF_NO_ACTION;
9818 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9819 return MF_NO_ACTION;
9822 element = MovingOrBlocked2Element(new_jx, new_jy);
9824 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9827 if (DONT_RUN_INTO(element))
9829 if (element == EL_ACID && dx == 0 && dy == 1)
9831 SplashAcid(new_jx, new_jy);
9832 Feld[jx][jy] = EL_PLAYER_1;
9833 InitMovingField(jx, jy, MV_DOWN);
9834 Store[jx][jy] = EL_ACID;
9835 ContinueMoving(jx, jy);
9839 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9844 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9845 if (can_move != MF_MOVING)
9848 /* check if DigField() has caused relocation of the player */
9849 if (player->jx != jx || player->jy != jy)
9850 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9852 StorePlayer[jx][jy] = 0;
9853 player->last_jx = jx;
9854 player->last_jy = jy;
9855 player->jx = new_jx;
9856 player->jy = new_jy;
9857 StorePlayer[new_jx][new_jy] = player->element_nr;
9860 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9862 player->step_counter++;
9865 player->drop_delay = 0;
9868 PlayerVisit[jx][jy] = FrameCounter;
9870 ScrollPlayer(player, SCROLL_INIT);
9873 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9875 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9877 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9880 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9882 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9883 CE_OTHER_GETS_ENTERED, enter_side);
9884 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9885 CE_ENTERED_BY_PLAYER, enter_side);
9892 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9894 int jx = player->jx, jy = player->jy;
9895 int old_jx = jx, old_jy = jy;
9896 int moved = MF_NO_ACTION;
9899 if (!player->active)
9904 if (player->MovPos == 0)
9906 player->is_moving = FALSE;
9907 player->is_digging = FALSE;
9908 player->is_collecting = FALSE;
9909 player->is_snapping = FALSE;
9910 player->is_pushing = FALSE;
9916 if (!player->active || (!dx && !dy))
9921 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9929 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9930 player->move_delay + player->move_delay_value);
9933 #if USE_NEW_MOVE_DELAY
9934 if (player->move_delay > 0)
9936 if (!FrameReached(&player->move_delay, player->move_delay_value))
9940 printf("::: can NOT move\n");
9946 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9947 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9954 printf("::: COULD move now\n");
9957 #if USE_NEW_MOVE_DELAY
9958 player->move_delay = -1; /* set to "uninitialized" value */
9961 /* store if player is automatically moved to next field */
9962 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9964 /* remove the last programmed player action */
9965 player->programmed_action = 0;
9969 /* should only happen if pre-1.2 tape recordings are played */
9970 /* this is only for backward compatibility */
9972 int original_move_delay_value = player->move_delay_value;
9975 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9979 /* scroll remaining steps with finest movement resolution */
9980 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9982 while (player->MovPos)
9984 ScrollPlayer(player, SCROLL_GO_ON);
9985 ScrollScreen(NULL, SCROLL_GO_ON);
9987 #if USE_NEW_MOVE_DELAY
9988 AdvanceFrameAndPlayerCounters(player->index_nr);
9997 player->move_delay_value = original_move_delay_value;
10000 if (player->last_move_dir & MV_HORIZONTAL)
10002 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10003 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10007 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10008 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10014 if (moved & MF_MOVING && !ScreenMovPos &&
10015 (player == local_player || !options.network))
10017 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10018 int offset = (setup.scroll_delay ? 3 : 0);
10020 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10022 /* actual player has left the screen -- scroll in that direction */
10023 if (jx != old_jx) /* player has moved horizontally */
10024 scroll_x += (jx - old_jx);
10025 else /* player has moved vertically */
10026 scroll_y += (jy - old_jy);
10030 if (jx != old_jx) /* player has moved horizontally */
10032 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10033 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10034 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10036 /* don't scroll over playfield boundaries */
10037 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10038 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10040 /* don't scroll more than one field at a time */
10041 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10043 /* don't scroll against the player's moving direction */
10044 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10045 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10046 scroll_x = old_scroll_x;
10048 else /* player has moved vertically */
10050 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10051 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10052 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10054 /* don't scroll over playfield boundaries */
10055 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10056 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10058 /* don't scroll more than one field at a time */
10059 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10061 /* don't scroll against the player's moving direction */
10062 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10063 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10064 scroll_y = old_scroll_y;
10068 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10070 if (!options.network && !AllPlayersInVisibleScreen())
10072 scroll_x = old_scroll_x;
10073 scroll_y = old_scroll_y;
10077 ScrollScreen(player, SCROLL_INIT);
10078 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10085 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10087 if (!(moved & MF_MOVING) && !player->is_pushing)
10092 player->StepFrame = 0;
10094 if (moved & MF_MOVING)
10097 printf("::: REALLY moves now\n");
10100 if (old_jx != jx && old_jy == jy)
10101 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10102 else if (old_jx == jx && old_jy != jy)
10103 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10105 DrawLevelField(jx, jy); /* for "crumbled sand" */
10107 player->last_move_dir = player->MovDir;
10108 player->is_moving = TRUE;
10110 player->is_snapping = FALSE;
10114 player->is_switching = FALSE;
10117 player->is_dropping = FALSE;
10121 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10124 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10127 int move_direction = player->MovDir;
10129 int enter_side = MV_DIR_OPPOSITE(move_direction);
10130 int leave_side = move_direction;
10132 static int trigger_sides[4][2] =
10134 /* enter side leave side */
10135 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10136 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10137 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10138 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10140 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10141 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10143 int old_element = Feld[old_jx][old_jy];
10144 int new_element = Feld[jx][jy];
10147 /* !!! TEST ONLY !!! */
10148 if (IS_CUSTOM_ELEMENT(old_element))
10149 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10151 player->index_bit, leave_side);
10153 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10154 CE_OTHER_GETS_LEFT,
10155 player->index_bit, leave_side);
10157 if (IS_CUSTOM_ELEMENT(new_element))
10158 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10159 player->index_bit, enter_side);
10161 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10162 CE_OTHER_GETS_ENTERED,
10163 player->index_bit, enter_side);
10173 CheckGravityMovementWhenNotMoving(player);
10176 player->last_move_dir = MV_NO_MOVING;
10178 player->is_moving = FALSE;
10180 #if USE_NEW_MOVE_STYLE
10181 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10182 /* ensure that the player is also allowed to move in the next frame */
10183 /* (currently, the player is forced to wait eight frames before he can try
10186 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10187 player->move_delay = 0; /* allow direct movement in the next frame */
10191 #if USE_NEW_MOVE_DELAY
10192 if (player->move_delay == -1) /* not yet initialized by DigField() */
10193 player->move_delay = player->move_delay_value;
10196 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10198 TestIfHeroTouchesBadThing(jx, jy);
10199 TestIfPlayerTouchesCustomElement(jx, jy);
10202 if (!player->active)
10203 RemoveHero(player);
10208 void ScrollPlayer(struct PlayerInfo *player, int mode)
10210 int jx = player->jx, jy = player->jy;
10211 int last_jx = player->last_jx, last_jy = player->last_jy;
10212 int move_stepsize = TILEX / player->move_delay_value;
10214 if (!player->active || !player->MovPos)
10217 if (mode == SCROLL_INIT)
10219 player->actual_frame_counter = FrameCounter;
10220 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10223 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10225 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10226 player->block_delay);
10229 #if USE_NEW_BLOCK_STYLE
10232 if (player->block_delay <= 0)
10233 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10236 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10237 Feld[last_jx][last_jy] == EL_EMPTY)
10239 int last_field_block_delay = 0; /* start with no blocking at all */
10240 int block_delay_adjustment = player->block_delay_adjustment;
10242 /* if player blocks last field, add delay for exactly one move */
10243 if (player->block_last_field)
10245 last_field_block_delay += player->move_delay_value;
10247 #if USE_GRAVITY_BUGFIX_NEW
10248 /* when blocking enabled, prevent moving up despite gravity */
10249 if (game.gravity && player->MovDir == MV_UP)
10250 block_delay_adjustment = -1;
10254 /* add block delay adjustment (also possible when not blocking) */
10255 last_field_block_delay += block_delay_adjustment;
10258 #if USE_BLOCK_DELAY_BUGFIX
10259 /* when blocking enabled, correct block delay for fast movement */
10260 if (player->block_last_field &&
10261 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10262 last_field_block_delay =
10263 player->move_delay_value + player->block_delay_adjustment;
10268 #if USE_GRAVITY_BUGFIX_NEW
10269 /* when blocking enabled, correct block delay for gravity movement */
10270 if (player->block_last_field &&
10271 game.gravity && player->MovDir == MV_UP)
10272 last_field_block_delay = player->move_delay_value - 1;
10276 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10277 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10280 #if USE_NEW_MOVE_STYLE
10281 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10282 player->block_last_field) &&
10283 Feld[last_jx][last_jy] == EL_EMPTY)
10284 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10286 if (Feld[last_jx][last_jy] == EL_EMPTY)
10287 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10292 DrawPlayer(player);
10297 else if (!FrameReached(&player->actual_frame_counter, 1))
10300 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10301 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10303 #if USE_NEW_BLOCK_STYLE
10305 if (!player->block_last_field &&
10306 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10308 RemoveField(last_jx, last_jy);
10310 Feld[last_jx][last_jy] = EL_EMPTY;
10314 /* before DrawPlayer() to draw correct player graphic for this case */
10315 if (player->MovPos == 0)
10316 CheckGravityMovement(player);
10319 DrawPlayer(player); /* needed here only to cleanup last field */
10322 if (player->MovPos == 0) /* player reached destination field */
10325 if (player->move_delay_reset_counter > 0)
10327 player->move_delay_reset_counter--;
10329 if (player->move_delay_reset_counter == 0)
10331 /* continue with normal speed after quickly moving through gate */
10332 HALVE_PLAYER_SPEED(player);
10334 /* be able to make the next move without delay */
10335 player->move_delay = 0;
10339 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10341 /* continue with normal speed after quickly moving through gate */
10342 HALVE_PLAYER_SPEED(player);
10344 /* be able to make the next move without delay */
10345 player->move_delay = 0;
10349 #if USE_NEW_BLOCK_STYLE
10351 if (player->block_last_field &&
10352 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10354 RemoveField(last_jx, last_jy);
10356 Feld[last_jx][last_jy] = EL_EMPTY;
10360 player->last_jx = jx;
10361 player->last_jy = jy;
10363 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10364 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10365 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10367 DrawPlayer(player); /* needed here only to cleanup last field */
10368 RemoveHero(player);
10370 if (local_player->friends_still_needed == 0 ||
10371 IS_SP_ELEMENT(Feld[jx][jy]))
10372 player->LevelSolved = player->GameOver = TRUE;
10376 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10377 /* this breaks one level: "machine", level 000 */
10379 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10382 int move_direction = player->MovDir;
10384 int enter_side = MV_DIR_OPPOSITE(move_direction);
10385 int leave_side = move_direction;
10387 static int trigger_sides[4][2] =
10389 /* enter side leave side */
10390 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10391 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10392 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10393 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10395 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10396 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10398 int old_jx = last_jx;
10399 int old_jy = last_jy;
10400 int old_element = Feld[old_jx][old_jy];
10401 int new_element = Feld[jx][jy];
10404 /* !!! TEST ONLY !!! */
10405 if (IS_CUSTOM_ELEMENT(old_element))
10406 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10408 player->index_bit, leave_side);
10410 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10411 CE_OTHER_GETS_LEFT,
10412 player->index_bit, leave_side);
10414 if (IS_CUSTOM_ELEMENT(new_element))
10415 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10416 player->index_bit, enter_side);
10418 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10419 CE_OTHER_GETS_ENTERED,
10420 player->index_bit, enter_side);
10426 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10428 TestIfHeroTouchesBadThing(jx, jy);
10429 TestIfPlayerTouchesCustomElement(jx, jy);
10432 /* needed because pushed element has not yet reached its destination,
10433 so it would trigger a change event at its previous field location */
10434 if (!player->is_pushing)
10436 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10439 if (!player->active)
10440 RemoveHero(player);
10443 if (level.use_step_counter)
10453 if (TimeLeft <= 10 && setup.time_limit)
10454 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10456 DrawGameValue_Time(TimeLeft);
10458 if (!TimeLeft && setup.time_limit)
10459 for (i = 0; i < MAX_PLAYERS; i++)
10460 KillHero(&stored_player[i]);
10462 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10463 DrawGameValue_Time(TimePlayed);
10466 if (tape.single_step && tape.recording && !tape.pausing &&
10467 !player->programmed_action)
10468 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10472 void ScrollScreen(struct PlayerInfo *player, int mode)
10474 static unsigned long screen_frame_counter = 0;
10476 if (mode == SCROLL_INIT)
10478 /* set scrolling step size according to actual player's moving speed */
10479 ScrollStepSize = TILEX / player->move_delay_value;
10481 screen_frame_counter = FrameCounter;
10482 ScreenMovDir = player->MovDir;
10483 ScreenMovPos = player->MovPos;
10484 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10487 else if (!FrameReached(&screen_frame_counter, 1))
10492 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10493 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10494 redraw_mask |= REDRAW_FIELD;
10497 ScreenMovDir = MV_NO_MOVING;
10500 void TestIfPlayerTouchesCustomElement(int x, int y)
10502 static int xy[4][2] =
10509 static int trigger_sides[4][2] =
10511 /* center side border side */
10512 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10513 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10514 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10515 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10517 static int touch_dir[4] =
10519 MV_LEFT | MV_RIGHT,
10524 int center_element = Feld[x][y]; /* should always be non-moving! */
10527 for (i = 0; i < NUM_DIRECTIONS; i++)
10529 int xx = x + xy[i][0];
10530 int yy = y + xy[i][1];
10531 int center_side = trigger_sides[i][0];
10532 int border_side = trigger_sides[i][1];
10533 int border_element;
10535 if (!IN_LEV_FIELD(xx, yy))
10538 if (IS_PLAYER(x, y))
10540 struct PlayerInfo *player = PLAYERINFO(x, y);
10542 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10543 border_element = Feld[xx][yy]; /* may be moving! */
10544 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10545 border_element = Feld[xx][yy];
10546 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10547 border_element = MovingOrBlocked2Element(xx, yy);
10549 continue; /* center and border element do not touch */
10552 /* !!! TEST ONLY !!! */
10553 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10554 player->index_bit, border_side);
10555 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10556 CE_OTHER_GETS_TOUCHED,
10557 player->index_bit, border_side);
10559 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10560 CE_OTHER_GETS_TOUCHED,
10561 player->index_bit, border_side);
10562 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10563 player->index_bit, border_side);
10566 else if (IS_PLAYER(xx, yy))
10568 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10570 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10572 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10573 continue; /* center and border element do not touch */
10577 /* !!! TEST ONLY !!! */
10578 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10579 player->index_bit, center_side);
10580 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10581 CE_OTHER_GETS_TOUCHED,
10582 player->index_bit, center_side);
10584 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10585 CE_OTHER_GETS_TOUCHED,
10586 player->index_bit, center_side);
10587 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10588 player->index_bit, center_side);
10596 void TestIfElementTouchesCustomElement(int x, int y)
10598 static int xy[4][2] =
10605 static int trigger_sides[4][2] =
10607 /* center side border side */
10608 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10609 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10610 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10611 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10613 static int touch_dir[4] =
10615 MV_LEFT | MV_RIGHT,
10620 boolean change_center_element = FALSE;
10621 int center_element_change_page = 0;
10622 int center_element = Feld[x][y]; /* should always be non-moving! */
10623 int border_trigger_element = EL_UNDEFINED;
10626 for (i = 0; i < NUM_DIRECTIONS; i++)
10628 int xx = x + xy[i][0];
10629 int yy = y + xy[i][1];
10630 int center_side = trigger_sides[i][0];
10631 int border_side = trigger_sides[i][1];
10632 int border_element;
10634 if (!IN_LEV_FIELD(xx, yy))
10637 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10638 border_element = Feld[xx][yy]; /* may be moving! */
10639 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10640 border_element = Feld[xx][yy];
10641 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10642 border_element = MovingOrBlocked2Element(xx, yy);
10644 continue; /* center and border element do not touch */
10646 /* check for change of center element (but change it only once) */
10647 if (IS_CUSTOM_ELEMENT(center_element) &&
10648 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10649 !change_center_element)
10651 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10653 struct ElementChangeInfo *change =
10654 &element_info[center_element].change_page[j];
10656 if (change->can_change &&
10657 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10658 change->trigger_side & border_side &&
10660 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10662 change->trigger_element == border_element
10666 change_center_element = TRUE;
10667 center_element_change_page = j;
10668 border_trigger_element = border_element;
10675 /* check for change of border element */
10676 if (IS_CUSTOM_ELEMENT(border_element) &&
10677 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10679 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10681 struct ElementChangeInfo *change =
10682 &element_info[border_element].change_page[j];
10684 if (change->can_change &&
10685 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10686 change->trigger_side & center_side &&
10688 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10690 change->trigger_element == center_element
10695 printf("::: border_element %d, %d\n", x, y);
10698 CheckElementChangeByPage(xx, yy, border_element, center_element,
10699 CE_OTHER_IS_TOUCHING, j);
10706 if (change_center_element)
10709 printf("::: center_element %d, %d\n", x, y);
10712 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10713 CE_OTHER_IS_TOUCHING, center_element_change_page);
10717 void TestIfElementHitsCustomElement(int x, int y, int direction)
10719 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10720 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10721 int hitx = x + dx, hity = y + dy;
10722 int hitting_element = Feld[x][y];
10723 int touched_element;
10725 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10726 !IS_FREE(hitx, hity) &&
10727 (!IS_MOVING(hitx, hity) ||
10728 MovDir[hitx][hity] != direction ||
10729 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10732 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10736 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10740 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10741 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10743 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10744 CE_HITTING_SOMETHING, direction);
10746 if (IN_LEV_FIELD(hitx, hity))
10748 int opposite_direction = MV_DIR_OPPOSITE(direction);
10749 int hitting_side = direction;
10750 int touched_side = opposite_direction;
10752 int touched_element = MovingOrBlocked2Element(hitx, hity);
10755 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10756 MovDir[hitx][hity] != direction ||
10757 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10766 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10767 CE_HIT_BY_SOMETHING, opposite_direction);
10769 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10770 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10772 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10774 struct ElementChangeInfo *change =
10775 &element_info[hitting_element].change_page[i];
10777 if (change->can_change &&
10778 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10779 change->trigger_side & touched_side &&
10782 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10784 change->trigger_element == touched_element
10788 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10789 CE_OTHER_IS_HITTING, i);
10795 if (IS_CUSTOM_ELEMENT(touched_element) &&
10796 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10798 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10800 struct ElementChangeInfo *change =
10801 &element_info[touched_element].change_page[i];
10803 if (change->can_change &&
10804 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10805 change->trigger_side & hitting_side &&
10807 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10809 change->trigger_element == hitting_element
10813 CheckElementChangeByPage(hitx, hity, touched_element,
10814 hitting_element, CE_OTHER_GETS_HIT, i);
10824 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10826 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10827 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10828 int hitx = x + dx, hity = y + dy;
10829 int hitting_element = Feld[x][y];
10830 int touched_element;
10832 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10833 !IS_FREE(hitx, hity) &&
10834 (!IS_MOVING(hitx, hity) ||
10835 MovDir[hitx][hity] != direction ||
10836 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10839 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10843 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10847 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10848 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10850 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10851 EP_CAN_SMASH_EVERYTHING, direction);
10853 if (IN_LEV_FIELD(hitx, hity))
10855 int opposite_direction = MV_DIR_OPPOSITE(direction);
10856 int hitting_side = direction;
10857 int touched_side = opposite_direction;
10859 int touched_element = MovingOrBlocked2Element(hitx, hity);
10862 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10863 MovDir[hitx][hity] != direction ||
10864 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10873 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10874 CE_SMASHED_BY_SOMETHING, opposite_direction);
10876 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10877 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10879 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10881 struct ElementChangeInfo *change =
10882 &element_info[hitting_element].change_page[i];
10884 if (change->can_change &&
10885 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10886 change->trigger_side & touched_side &&
10889 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10891 change->trigger_element == touched_element
10895 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10896 CE_OTHER_IS_SMASHING, i);
10902 if (IS_CUSTOM_ELEMENT(touched_element) &&
10903 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10905 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10907 struct ElementChangeInfo *change =
10908 &element_info[touched_element].change_page[i];
10910 if (change->can_change &&
10911 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10912 change->trigger_side & hitting_side &&
10914 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10916 change->trigger_element == hitting_element
10920 CheckElementChangeByPage(hitx, hity, touched_element,
10921 hitting_element, CE_OTHER_GETS_SMASHED,i);
10931 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10933 int i, kill_x = -1, kill_y = -1;
10934 int bad_element = -1;
10935 static int test_xy[4][2] =
10942 static int test_dir[4] =
10950 for (i = 0; i < NUM_DIRECTIONS; i++)
10952 int test_x, test_y, test_move_dir, test_element;
10954 test_x = good_x + test_xy[i][0];
10955 test_y = good_y + test_xy[i][1];
10957 if (!IN_LEV_FIELD(test_x, test_y))
10961 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10964 test_element = Feld[test_x][test_y];
10966 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10969 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10970 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10972 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10973 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10977 bad_element = test_element;
10983 if (kill_x != -1 || kill_y != -1)
10985 if (IS_PLAYER(good_x, good_y))
10987 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10990 if (player->shield_deadly_time_left > 0 &&
10991 !IS_INDESTRUCTIBLE(bad_element))
10992 Bang(kill_x, kill_y);
10993 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10996 if (player->shield_deadly_time_left > 0)
10997 Bang(kill_x, kill_y);
10998 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11003 Bang(good_x, good_y);
11007 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11009 int i, kill_x = -1, kill_y = -1;
11010 int bad_element = Feld[bad_x][bad_y];
11011 static int test_xy[4][2] =
11018 static int touch_dir[4] =
11020 MV_LEFT | MV_RIGHT,
11025 static int test_dir[4] =
11033 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11036 for (i = 0; i < NUM_DIRECTIONS; i++)
11038 int test_x, test_y, test_move_dir, test_element;
11040 test_x = bad_x + test_xy[i][0];
11041 test_y = bad_y + test_xy[i][1];
11042 if (!IN_LEV_FIELD(test_x, test_y))
11046 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11048 test_element = Feld[test_x][test_y];
11050 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11051 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11053 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11054 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11056 /* good thing is player or penguin that does not move away */
11057 if (IS_PLAYER(test_x, test_y))
11059 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11061 if (bad_element == EL_ROBOT && player->is_moving)
11062 continue; /* robot does not kill player if he is moving */
11064 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11066 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11067 continue; /* center and border element do not touch */
11074 else if (test_element == EL_PENGUIN)
11083 if (kill_x != -1 || kill_y != -1)
11085 if (IS_PLAYER(kill_x, kill_y))
11087 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11090 if (player->shield_deadly_time_left > 0 &&
11091 !IS_INDESTRUCTIBLE(bad_element))
11092 Bang(bad_x, bad_y);
11093 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11096 if (player->shield_deadly_time_left > 0)
11097 Bang(bad_x, bad_y);
11098 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11103 Bang(kill_x, kill_y);
11107 void TestIfHeroTouchesBadThing(int x, int y)
11109 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11112 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11114 TestIfGoodThingHitsBadThing(x, y, move_dir);
11117 void TestIfBadThingTouchesHero(int x, int y)
11119 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11122 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11124 TestIfBadThingHitsGoodThing(x, y, move_dir);
11127 void TestIfFriendTouchesBadThing(int x, int y)
11129 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11132 void TestIfBadThingTouchesFriend(int x, int y)
11134 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11137 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11139 int i, kill_x = bad_x, kill_y = bad_y;
11140 static int xy[4][2] =
11148 for (i = 0; i < NUM_DIRECTIONS; i++)
11152 x = bad_x + xy[i][0];
11153 y = bad_y + xy[i][1];
11154 if (!IN_LEV_FIELD(x, y))
11157 element = Feld[x][y];
11158 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11159 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11167 if (kill_x != bad_x || kill_y != bad_y)
11168 Bang(bad_x, bad_y);
11171 void KillHero(struct PlayerInfo *player)
11173 int jx = player->jx, jy = player->jy;
11175 if (!player->active)
11178 /* remove accessible field at the player's position */
11179 Feld[jx][jy] = EL_EMPTY;
11181 /* deactivate shield (else Bang()/Explode() would not work right) */
11182 player->shield_normal_time_left = 0;
11183 player->shield_deadly_time_left = 0;
11189 static void KillHeroUnlessEnemyProtected(int x, int y)
11191 if (!PLAYER_ENEMY_PROTECTED(x, y))
11192 KillHero(PLAYERINFO(x, y));
11195 static void KillHeroUnlessExplosionProtected(int x, int y)
11197 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11198 KillHero(PLAYERINFO(x, y));
11201 void BuryHero(struct PlayerInfo *player)
11203 int jx = player->jx, jy = player->jy;
11205 if (!player->active)
11209 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11211 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11213 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11215 player->GameOver = TRUE;
11216 RemoveHero(player);
11219 void RemoveHero(struct PlayerInfo *player)
11221 int jx = player->jx, jy = player->jy;
11222 int i, found = FALSE;
11224 player->present = FALSE;
11225 player->active = FALSE;
11227 if (!ExplodeField[jx][jy])
11228 StorePlayer[jx][jy] = 0;
11230 for (i = 0; i < MAX_PLAYERS; i++)
11231 if (stored_player[i].active)
11235 AllPlayersGone = TRUE;
11242 =============================================================================
11243 checkDiagonalPushing()
11244 -----------------------------------------------------------------------------
11245 check if diagonal input device direction results in pushing of object
11246 (by checking if the alternative direction is walkable, diggable, ...)
11247 =============================================================================
11250 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11251 int x, int y, int real_dx, int real_dy)
11253 int jx, jy, dx, dy, xx, yy;
11255 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11258 /* diagonal direction: check alternative direction */
11263 xx = jx + (dx == 0 ? real_dx : 0);
11264 yy = jy + (dy == 0 ? real_dy : 0);
11266 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11270 =============================================================================
11272 -----------------------------------------------------------------------------
11273 x, y: field next to player (non-diagonal) to try to dig to
11274 real_dx, real_dy: direction as read from input device (can be diagonal)
11275 =============================================================================
11278 int DigField(struct PlayerInfo *player,
11279 int oldx, int oldy, int x, int y,
11280 int real_dx, int real_dy, int mode)
11283 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11285 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11286 boolean player_was_pushing = player->is_pushing;
11287 int jx = oldx, jy = oldy;
11288 int dx = x - jx, dy = y - jy;
11289 int nextx = x + dx, nexty = y + dy;
11290 int move_direction = (dx == -1 ? MV_LEFT :
11291 dx == +1 ? MV_RIGHT :
11293 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11294 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11296 int dig_side = MV_DIR_OPPOSITE(move_direction);
11298 static int trigger_sides[4] =
11300 CH_SIDE_RIGHT, /* moving left */
11301 CH_SIDE_LEFT, /* moving right */
11302 CH_SIDE_BOTTOM, /* moving up */
11303 CH_SIDE_TOP, /* moving down */
11305 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11307 int old_element = Feld[jx][jy];
11310 if (is_player) /* function can also be called by EL_PENGUIN */
11312 if (player->MovPos == 0)
11314 player->is_digging = FALSE;
11315 player->is_collecting = FALSE;
11318 if (player->MovPos == 0) /* last pushing move finished */
11319 player->is_pushing = FALSE;
11321 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11323 player->is_switching = FALSE;
11324 #if USE_NEW_PUSH_DELAY
11325 player->push_delay = -1;
11327 player->push_delay = 0;
11330 return MF_NO_ACTION;
11334 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11335 return MF_NO_ACTION;
11340 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11342 if (IS_TUBE(Feld[jx][jy]) ||
11343 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11347 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11348 int tube_leave_directions[][2] =
11350 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11351 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11352 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11353 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11354 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11355 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11356 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11357 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11358 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11359 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11360 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11361 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11364 while (tube_leave_directions[i][0] != tube_element)
11367 if (tube_leave_directions[i][0] == -1) /* should not happen */
11371 if (!(tube_leave_directions[i][1] & move_direction))
11372 return MF_NO_ACTION; /* tube has no opening in this direction */
11377 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11378 old_element = Back[jx][jy];
11382 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11383 return MF_NO_ACTION; /* field has no opening in this direction */
11385 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11386 return MF_NO_ACTION; /* field has no opening in this direction */
11388 element = Feld[x][y];
11390 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11391 return MF_NO_ACTION;
11393 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11394 game.engine_version >= VERSION_IDENT(2,2,0,0))
11395 return MF_NO_ACTION;
11398 if (game.gravity && is_player && !player->is_auto_moving &&
11399 canFallDown(player) && move_direction != MV_DOWN &&
11400 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11401 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11405 if (element == EL_EMPTY_SPACE &&
11406 game.gravity && !player->is_auto_moving &&
11407 canFallDown(player) && move_direction != MV_DOWN)
11408 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11414 case EL_SP_PORT_LEFT:
11415 case EL_SP_PORT_RIGHT:
11416 case EL_SP_PORT_UP:
11417 case EL_SP_PORT_DOWN:
11418 case EL_SP_PORT_HORIZONTAL:
11419 case EL_SP_PORT_VERTICAL:
11420 case EL_SP_PORT_ANY:
11421 case EL_SP_GRAVITY_PORT_LEFT:
11422 case EL_SP_GRAVITY_PORT_RIGHT:
11423 case EL_SP_GRAVITY_PORT_UP:
11424 case EL_SP_GRAVITY_PORT_DOWN:
11426 if (!canEnterSupaplexPort(x, y, dx, dy))
11427 return MF_NO_ACTION;
11430 element != EL_SP_PORT_LEFT &&
11431 element != EL_SP_GRAVITY_PORT_LEFT &&
11432 element != EL_SP_PORT_HORIZONTAL &&
11433 element != EL_SP_PORT_ANY) ||
11435 element != EL_SP_PORT_RIGHT &&
11436 element != EL_SP_GRAVITY_PORT_RIGHT &&
11437 element != EL_SP_PORT_HORIZONTAL &&
11438 element != EL_SP_PORT_ANY) ||
11440 element != EL_SP_PORT_UP &&
11441 element != EL_SP_GRAVITY_PORT_UP &&
11442 element != EL_SP_PORT_VERTICAL &&
11443 element != EL_SP_PORT_ANY) ||
11445 element != EL_SP_PORT_DOWN &&
11446 element != EL_SP_GRAVITY_PORT_DOWN &&
11447 element != EL_SP_PORT_VERTICAL &&
11448 element != EL_SP_PORT_ANY) ||
11449 !IN_LEV_FIELD(nextx, nexty) ||
11450 !IS_FREE(nextx, nexty))
11451 return MF_NO_ACTION;
11454 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11455 element == EL_SP_GRAVITY_PORT_RIGHT ||
11456 element == EL_SP_GRAVITY_PORT_UP ||
11457 element == EL_SP_GRAVITY_PORT_DOWN)
11458 game.gravity = !game.gravity;
11460 /* automatically move to the next field with double speed */
11461 player->programmed_action = move_direction;
11463 if (player->move_delay_reset_counter == 0)
11465 player->move_delay_reset_counter = 2; /* two double speed steps */
11467 DOUBLE_PLAYER_SPEED(player);
11470 player->move_delay_reset_counter = 2;
11472 DOUBLE_PLAYER_SPEED(player);
11476 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11479 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11485 case EL_TUBE_VERTICAL:
11486 case EL_TUBE_HORIZONTAL:
11487 case EL_TUBE_VERTICAL_LEFT:
11488 case EL_TUBE_VERTICAL_RIGHT:
11489 case EL_TUBE_HORIZONTAL_UP:
11490 case EL_TUBE_HORIZONTAL_DOWN:
11491 case EL_TUBE_LEFT_UP:
11492 case EL_TUBE_LEFT_DOWN:
11493 case EL_TUBE_RIGHT_UP:
11494 case EL_TUBE_RIGHT_DOWN:
11497 int tube_enter_directions[][2] =
11499 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11500 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11501 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11502 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11503 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11504 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11505 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11506 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11507 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11508 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11509 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11510 { -1, MV_NO_MOVING }
11513 while (tube_enter_directions[i][0] != element)
11516 if (tube_enter_directions[i][0] == -1) /* should not happen */
11520 if (!(tube_enter_directions[i][1] & move_direction))
11521 return MF_NO_ACTION; /* tube has no opening in this direction */
11523 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11531 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11533 if (IS_WALKABLE(element))
11536 int sound_element = SND_ELEMENT(element);
11537 int sound_action = ACTION_WALKING;
11540 if (!ACCESS_FROM(element, opposite_direction))
11541 return MF_NO_ACTION; /* field not accessible from this direction */
11545 if (element == EL_EMPTY_SPACE &&
11546 game.gravity && !player->is_auto_moving &&
11547 canFallDown(player) && move_direction != MV_DOWN)
11548 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11551 if (IS_RND_GATE(element))
11553 if (!player->key[RND_GATE_NR(element)])
11554 return MF_NO_ACTION;
11556 else if (IS_RND_GATE_GRAY(element))
11558 if (!player->key[RND_GATE_GRAY_NR(element)])
11559 return MF_NO_ACTION;
11561 else if (element == EL_EXIT_OPEN ||
11562 element == EL_SP_EXIT_OPEN ||
11563 element == EL_SP_EXIT_OPENING)
11565 sound_action = ACTION_PASSING; /* player is passing exit */
11567 else if (element == EL_EMPTY)
11569 sound_action = ACTION_MOVING; /* nothing to walk on */
11572 /* play sound from background or player, whatever is available */
11573 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11574 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11576 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11581 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11583 else if (IS_PASSABLE(element))
11587 if (!canPassField(x, y, move_direction))
11588 return MF_NO_ACTION;
11593 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11594 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11595 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11596 return MF_NO_ACTION;
11598 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11599 return MF_NO_ACTION;
11604 if (!ACCESS_FROM(element, opposite_direction))
11605 return MF_NO_ACTION; /* field not accessible from this direction */
11607 if (IS_CUSTOM_ELEMENT(element) &&
11608 !ACCESS_FROM(element, opposite_direction))
11609 return MF_NO_ACTION; /* field not accessible from this direction */
11613 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11614 return MF_NO_ACTION;
11619 if (IS_EM_GATE(element))
11621 if (!player->key[EM_GATE_NR(element)])
11622 return MF_NO_ACTION;
11624 else if (IS_EM_GATE_GRAY(element))
11626 if (!player->key[EM_GATE_GRAY_NR(element)])
11627 return MF_NO_ACTION;
11629 else if (IS_SP_PORT(element))
11631 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11632 element == EL_SP_GRAVITY_PORT_RIGHT ||
11633 element == EL_SP_GRAVITY_PORT_UP ||
11634 element == EL_SP_GRAVITY_PORT_DOWN)
11635 game.gravity = !game.gravity;
11636 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11637 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11638 element == EL_SP_GRAVITY_ON_PORT_UP ||
11639 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11640 game.gravity = TRUE;
11641 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11642 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11643 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11644 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11645 game.gravity = FALSE;
11648 /* automatically move to the next field with double speed */
11649 player->programmed_action = move_direction;
11651 if (player->move_delay_reset_counter == 0)
11653 player->move_delay_reset_counter = 2; /* two double speed steps */
11655 DOUBLE_PLAYER_SPEED(player);
11658 player->move_delay_reset_counter = 2;
11660 DOUBLE_PLAYER_SPEED(player);
11663 PlayLevelSoundAction(x, y, ACTION_PASSING);
11667 else if (IS_DIGGABLE(element))
11671 if (mode != DF_SNAP)
11674 GfxElement[x][y] = GFX_ELEMENT(element);
11677 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11679 player->is_digging = TRUE;
11682 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11684 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11685 player->index_bit, dig_side);
11688 if (mode == DF_SNAP)
11689 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11694 else if (IS_COLLECTIBLE(element))
11698 if (is_player && mode != DF_SNAP)
11700 GfxElement[x][y] = element;
11701 player->is_collecting = TRUE;
11704 if (element == EL_SPEED_PILL)
11705 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11706 else if (element == EL_EXTRA_TIME && level.time > 0)
11709 DrawGameValue_Time(TimeLeft);
11711 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11713 player->shield_normal_time_left += 10;
11714 if (element == EL_SHIELD_DEADLY)
11715 player->shield_deadly_time_left += 10;
11717 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11719 if (player->inventory_size < MAX_INVENTORY_SIZE)
11720 player->inventory_element[player->inventory_size++] = element;
11722 DrawGameValue_Dynamite(local_player->inventory_size);
11724 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11726 player->dynabomb_count++;
11727 player->dynabombs_left++;
11729 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11731 player->dynabomb_size++;
11733 else if (element == EL_DYNABOMB_INCREASE_POWER)
11735 player->dynabomb_xl = TRUE;
11737 else if (IS_KEY(element))
11739 player->key[KEY_NR(element)] = TRUE;
11741 DrawGameValue_Keys(player->key);
11743 redraw_mask |= REDRAW_DOOR_1;
11745 else if (IS_ENVELOPE(element))
11748 player->show_envelope = element;
11750 ShowEnvelope(element - EL_ENVELOPE_1);
11753 else if (IS_DROPPABLE(element) ||
11754 IS_THROWABLE(element)) /* can be collected and dropped */
11758 if (element_info[element].collect_count == 0)
11759 player->inventory_infinite_element = element;
11761 for (i = 0; i < element_info[element].collect_count; i++)
11762 if (player->inventory_size < MAX_INVENTORY_SIZE)
11763 player->inventory_element[player->inventory_size++] = element;
11765 DrawGameValue_Dynamite(local_player->inventory_size);
11767 else if (element_info[element].collect_count > 0)
11769 local_player->gems_still_needed -=
11770 element_info[element].collect_count;
11771 if (local_player->gems_still_needed < 0)
11772 local_player->gems_still_needed = 0;
11774 DrawGameValue_Emeralds(local_player->gems_still_needed);
11777 RaiseScoreElement(element);
11778 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11781 CheckTriggeredElementChangeByPlayer(x, y, element,
11782 CE_OTHER_GETS_COLLECTED,
11783 player->index_bit, dig_side);
11786 if (mode == DF_SNAP)
11787 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11792 else if (IS_PUSHABLE(element))
11794 if (mode == DF_SNAP && element != EL_BD_ROCK)
11795 return MF_NO_ACTION;
11797 if (CAN_FALL(element) && dy)
11798 return MF_NO_ACTION;
11800 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11801 !(element == EL_SPRING && level.use_spring_bug))
11802 return MF_NO_ACTION;
11805 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11806 ((move_direction & MV_VERTICAL &&
11807 ((element_info[element].move_pattern & MV_LEFT &&
11808 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11809 (element_info[element].move_pattern & MV_RIGHT &&
11810 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11811 (move_direction & MV_HORIZONTAL &&
11812 ((element_info[element].move_pattern & MV_UP &&
11813 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11814 (element_info[element].move_pattern & MV_DOWN &&
11815 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11816 return MF_NO_ACTION;
11820 /* do not push elements already moving away faster than player */
11821 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11822 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11823 return MF_NO_ACTION;
11825 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11826 return MF_NO_ACTION;
11832 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11834 if (player->push_delay_value == -1 || !player_was_pushing)
11835 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11837 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11839 if (player->push_delay_value == -1)
11840 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11843 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11845 if (player->push_delay_value == -1 || !player_was_pushing)
11846 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11849 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11851 if (!player->is_pushing)
11852 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11856 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11857 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11858 !player_is_pushing))
11859 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11862 if (!player->is_pushing &&
11863 game.engine_version >= VERSION_IDENT(2,2,0,7))
11864 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11868 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11869 player->push_delay, player->push_delay_value,
11870 FrameCounter, game.engine_version,
11871 player_was_pushing, player->is_pushing,
11872 element, element_info[element].token_name,
11873 GET_NEW_PUSH_DELAY(element));
11876 player->is_pushing = TRUE;
11878 if (!(IN_LEV_FIELD(nextx, nexty) &&
11879 (IS_FREE(nextx, nexty) ||
11880 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11881 IS_SB_ELEMENT(element)))))
11882 return MF_NO_ACTION;
11884 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11885 return MF_NO_ACTION;
11887 #if USE_NEW_PUSH_DELAY
11890 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11891 printf("::: ALERT: %d, %d [%d / %d]\n",
11892 player->push_delay, player->push_delay2,
11893 FrameCounter, FrameCounter / 50);
11896 if (player->push_delay == -1) /* new pushing; restart delay */
11897 player->push_delay = 0;
11899 if (player->push_delay == 0) /* new pushing; restart delay */
11900 player->push_delay = FrameCounter;
11903 #if USE_NEW_PUSH_DELAY
11905 if ( (player->push_delay > 0) != (!xxx_fr) )
11906 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11907 player->push_delay,
11908 xxx_pdv2, player->push_delay2, player->push_delay_value,
11909 FrameCounter, FrameCounter / 50);
11913 if (player->push_delay > 0 &&
11914 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11915 element != EL_SPRING && element != EL_BALLOON)
11918 if (player->push_delay < player->push_delay_value &&
11919 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11920 element != EL_SPRING && element != EL_BALLOON)
11924 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11925 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11926 element != EL_SPRING && element != EL_BALLOON)
11929 /* make sure that there is no move delay before next try to push */
11930 #if USE_NEW_MOVE_DELAY
11931 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11932 player->move_delay = 0;
11934 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11935 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11938 return MF_NO_ACTION;
11942 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11945 if (IS_SB_ELEMENT(element))
11947 if (element == EL_SOKOBAN_FIELD_FULL)
11949 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11950 local_player->sokobanfields_still_needed++;
11953 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11955 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11956 local_player->sokobanfields_still_needed--;
11959 Feld[x][y] = EL_SOKOBAN_OBJECT;
11961 if (Back[x][y] == Back[nextx][nexty])
11962 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11963 else if (Back[x][y] != 0)
11964 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11967 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11970 if (local_player->sokobanfields_still_needed == 0 &&
11971 game.emulation == EMU_SOKOBAN)
11973 player->LevelSolved = player->GameOver = TRUE;
11974 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11978 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11980 InitMovingField(x, y, move_direction);
11981 GfxAction[x][y] = ACTION_PUSHING;
11983 if (mode == DF_SNAP)
11984 ContinueMoving(x, y);
11986 MovPos[x][y] = (dx != 0 ? dx : dy);
11988 Pushed[x][y] = TRUE;
11989 Pushed[nextx][nexty] = TRUE;
11991 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11992 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11994 player->push_delay_value = -1; /* get new value later */
11996 #if USE_PUSH_BUGFIX
11997 /* now: check for element change _after_ element has been pushed! */
11999 if (game.use_change_when_pushing_bug)
12001 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12004 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12005 player->index_bit, dig_side);
12006 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
12007 player->index_bit, dig_side);
12013 /* check for element change _after_ element has been pushed! */
12017 /* !!! TEST ONLY !!! */
12018 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12019 player->index_bit, dig_side);
12020 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12021 player->index_bit, dig_side);
12023 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12024 player->index_bit, dig_side);
12025 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12026 player->index_bit, dig_side);
12034 else if (IS_SWITCHABLE(element))
12036 if (PLAYER_SWITCHING(player, x, y))
12038 CheckTriggeredElementChangeByPlayer(x,y, element,
12039 CE_OTHER_GETS_PRESSED,
12040 player->index_bit, dig_side);
12045 player->is_switching = TRUE;
12046 player->switch_x = x;
12047 player->switch_y = y;
12049 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12051 if (element == EL_ROBOT_WHEEL)
12053 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12057 DrawLevelField(x, y);
12059 else if (element == EL_SP_TERMINAL)
12063 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12065 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12067 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12068 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12071 else if (IS_BELT_SWITCH(element))
12073 ToggleBeltSwitch(x, y);
12075 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12076 element == EL_SWITCHGATE_SWITCH_DOWN)
12078 ToggleSwitchgateSwitch(x, y);
12080 else if (element == EL_LIGHT_SWITCH ||
12081 element == EL_LIGHT_SWITCH_ACTIVE)
12083 ToggleLightSwitch(x, y);
12086 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12087 SND_LIGHT_SWITCH_ACTIVATING :
12088 SND_LIGHT_SWITCH_DEACTIVATING);
12091 else if (element == EL_TIMEGATE_SWITCH)
12093 ActivateTimegateSwitch(x, y);
12095 else if (element == EL_BALLOON_SWITCH_LEFT ||
12096 element == EL_BALLOON_SWITCH_RIGHT ||
12097 element == EL_BALLOON_SWITCH_UP ||
12098 element == EL_BALLOON_SWITCH_DOWN ||
12099 element == EL_BALLOON_SWITCH_ANY)
12101 if (element == EL_BALLOON_SWITCH_ANY)
12102 game.balloon_dir = move_direction;
12104 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12105 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12106 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12107 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12110 else if (element == EL_LAMP)
12112 Feld[x][y] = EL_LAMP_ACTIVE;
12113 local_player->lights_still_needed--;
12115 ResetGfxAnimation(x, y);
12116 DrawLevelField(x, y);
12118 else if (element == EL_TIME_ORB_FULL)
12120 Feld[x][y] = EL_TIME_ORB_EMPTY;
12122 DrawGameValue_Time(TimeLeft);
12124 ResetGfxAnimation(x, y);
12125 DrawLevelField(x, y);
12128 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12132 CheckTriggeredElementChangeByPlayer(x, y, element,
12133 CE_OTHER_IS_SWITCHING,
12134 player->index_bit, dig_side);
12136 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12137 player->index_bit, dig_side);
12143 if (!PLAYER_SWITCHING(player, x, y))
12145 player->is_switching = TRUE;
12146 player->switch_x = x;
12147 player->switch_y = y;
12150 /* !!! TEST ONLY !!! */
12151 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12152 player->index_bit, dig_side);
12153 CheckTriggeredElementChangeByPlayer(x, y, element,
12154 CE_OTHER_IS_SWITCHING,
12155 player->index_bit, dig_side);
12157 CheckTriggeredElementChangeByPlayer(x, y, element,
12158 CE_OTHER_IS_SWITCHING,
12159 player->index_bit, dig_side);
12160 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12161 player->index_bit, dig_side);
12166 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12167 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12168 player->index_bit, dig_side);
12169 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12170 player->index_bit, dig_side);
12172 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12173 player->index_bit, dig_side);
12174 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12175 player->index_bit, dig_side);
12179 return MF_NO_ACTION;
12182 #if USE_NEW_PUSH_DELAY
12183 player->push_delay = -1;
12185 player->push_delay = 0;
12188 if (Feld[x][y] != element) /* really digged/collected something */
12189 player->is_collecting = !player->is_digging;
12194 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12196 int jx = player->jx, jy = player->jy;
12197 int x = jx + dx, y = jy + dy;
12198 int snap_direction = (dx == -1 ? MV_LEFT :
12199 dx == +1 ? MV_RIGHT :
12201 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12204 if (player->MovPos != 0)
12207 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12211 if (!player->active || !IN_LEV_FIELD(x, y))
12219 if (player->MovPos == 0)
12220 player->is_pushing = FALSE;
12222 player->is_snapping = FALSE;
12224 if (player->MovPos == 0)
12226 player->is_moving = FALSE;
12227 player->is_digging = FALSE;
12228 player->is_collecting = FALSE;
12234 if (player->is_snapping)
12237 player->MovDir = snap_direction;
12240 if (player->MovPos == 0)
12243 player->is_moving = FALSE;
12244 player->is_digging = FALSE;
12245 player->is_collecting = FALSE;
12248 player->is_dropping = FALSE;
12250 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12253 player->is_snapping = TRUE;
12256 if (player->MovPos == 0)
12259 player->is_moving = FALSE;
12260 player->is_digging = FALSE;
12261 player->is_collecting = FALSE;
12265 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12266 DrawLevelField(player->last_jx, player->last_jy);
12269 DrawLevelField(x, y);
12278 boolean DropElement(struct PlayerInfo *player)
12280 int old_element, new_element;
12281 int dropx = player->jx, dropy = player->jy;
12282 int drop_direction = player->MovDir;
12284 int drop_side = drop_direction;
12286 static int trigger_sides[4] =
12288 CH_SIDE_LEFT, /* dropping left */
12289 CH_SIDE_RIGHT, /* dropping right */
12290 CH_SIDE_TOP, /* dropping up */
12291 CH_SIDE_BOTTOM, /* dropping down */
12293 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12295 int drop_element = (player->inventory_size > 0 ?
12296 player->inventory_element[player->inventory_size - 1] :
12297 player->inventory_infinite_element != EL_UNDEFINED ?
12298 player->inventory_infinite_element :
12299 player->dynabombs_left > 0 ?
12300 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12303 if (IS_THROWABLE(drop_element))
12305 dropx += GET_DX_FROM_DIR(drop_direction);
12306 dropy += GET_DY_FROM_DIR(drop_direction);
12308 if (!IN_LEV_FIELD(dropx, dropy))
12312 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12313 new_element = drop_element; /* default: no change when dropping */
12315 /* check if player is active, not moving and ready to drop */
12316 if (!player->active || player->MovPos || player->drop_delay > 0)
12319 /* check if player has anything that can be dropped */
12321 if (new_element == EL_UNDEFINED)
12324 if (player->inventory_size == 0 &&
12325 player->inventory_infinite_element == EL_UNDEFINED &&
12326 player->dynabombs_left == 0)
12330 /* check if anything can be dropped at the current position */
12331 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12334 /* collected custom elements can only be dropped on empty fields */
12336 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12339 if (player->inventory_size > 0 &&
12340 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12341 && old_element != EL_EMPTY)
12345 if (old_element != EL_EMPTY)
12346 Back[dropx][dropy] = old_element; /* store old element on this field */
12348 ResetGfxAnimation(dropx, dropy);
12349 ResetRandomAnimationValue(dropx, dropy);
12351 if (player->inventory_size > 0 ||
12352 player->inventory_infinite_element != EL_UNDEFINED)
12354 if (player->inventory_size > 0)
12356 player->inventory_size--;
12359 new_element = player->inventory_element[player->inventory_size];
12362 DrawGameValue_Dynamite(local_player->inventory_size);
12364 if (new_element == EL_DYNAMITE)
12365 new_element = EL_DYNAMITE_ACTIVE;
12366 else if (new_element == EL_SP_DISK_RED)
12367 new_element = EL_SP_DISK_RED_ACTIVE;
12370 Feld[dropx][dropy] = new_element;
12372 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12373 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12374 el2img(Feld[dropx][dropy]), 0);
12376 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12379 /* needed if previous element just changed to "empty" in the last frame */
12380 Changed[dropx][dropy] = 0; /* allow another change */
12384 /* !!! TEST ONLY !!! */
12385 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12386 player->index_bit, drop_side);
12387 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12388 CE_OTHER_GETS_DROPPED,
12389 player->index_bit, drop_side);
12391 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12392 CE_OTHER_GETS_DROPPED,
12393 player->index_bit, drop_side);
12394 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12395 player->index_bit, drop_side);
12398 TestIfElementTouchesCustomElement(dropx, dropy);
12400 else /* player is dropping a dyna bomb */
12402 player->dynabombs_left--;
12405 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12408 Feld[dropx][dropy] = new_element;
12410 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12411 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12412 el2img(Feld[dropx][dropy]), 0);
12414 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12421 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12424 InitField_WithBug1(dropx, dropy, FALSE);
12426 InitField(dropx, dropy, FALSE);
12427 if (CAN_MOVE(Feld[dropx][dropy]))
12428 InitMovDir(dropx, dropy);
12432 new_element = Feld[dropx][dropy]; /* element might have changed */
12434 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12435 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12438 int move_stepsize = element_info[new_element].move_stepsize;
12440 int move_direction, nextx, nexty;
12442 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12443 MovDir[dropx][dropy] = drop_direction;
12445 move_direction = MovDir[dropx][dropy];
12446 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12447 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12450 Changed[dropx][dropy] = 0; /* allow another change */
12451 CheckCollision[dropx][dropy] = 2;
12454 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12457 WasJustMoving[dropx][dropy] = 3;
12460 InitMovingField(dropx, dropy, move_direction);
12461 ContinueMoving(dropx, dropy);
12466 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12469 Changed[dropx][dropy] = 0; /* allow another change */
12472 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12474 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12475 CE_HITTING_SOMETHING, move_direction);
12483 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12488 player->drop_delay = 8 + 8 + 8;
12492 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12497 player->is_dropping = TRUE;
12503 /* ------------------------------------------------------------------------- */
12504 /* game sound playing functions */
12505 /* ------------------------------------------------------------------------- */
12507 static int *loop_sound_frame = NULL;
12508 static int *loop_sound_volume = NULL;
12510 void InitPlayLevelSound()
12512 int num_sounds = getSoundListSize();
12514 checked_free(loop_sound_frame);
12515 checked_free(loop_sound_volume);
12517 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12518 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12521 static void PlayLevelSound(int x, int y, int nr)
12523 int sx = SCREENX(x), sy = SCREENY(y);
12524 int volume, stereo_position;
12525 int max_distance = 8;
12526 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12528 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12529 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12532 if (!IN_LEV_FIELD(x, y) ||
12533 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12534 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12537 volume = SOUND_MAX_VOLUME;
12539 if (!IN_SCR_FIELD(sx, sy))
12541 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12542 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12544 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12547 stereo_position = (SOUND_MAX_LEFT +
12548 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12549 (SCR_FIELDX + 2 * max_distance));
12551 if (IS_LOOP_SOUND(nr))
12553 /* This assures that quieter loop sounds do not overwrite louder ones,
12554 while restarting sound volume comparison with each new game frame. */
12556 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12559 loop_sound_volume[nr] = volume;
12560 loop_sound_frame[nr] = FrameCounter;
12563 PlaySoundExt(nr, volume, stereo_position, type);
12566 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12568 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12569 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12570 y < LEVELY(BY1) ? LEVELY(BY1) :
12571 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12575 static void PlayLevelSoundAction(int x, int y, int action)
12577 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12580 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12582 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12584 if (sound_effect != SND_UNDEFINED)
12585 PlayLevelSound(x, y, sound_effect);
12588 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12591 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12593 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12594 PlayLevelSound(x, y, sound_effect);
12597 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12599 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12601 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12602 PlayLevelSound(x, y, sound_effect);
12605 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12607 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12609 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12610 StopSound(sound_effect);
12613 static void PlayLevelMusic()
12615 if (levelset.music[level_nr] != MUS_UNDEFINED)
12616 PlayMusic(levelset.music[level_nr]); /* from config file */
12618 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12621 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12623 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12626 if (sample == SAMPLE_bug)
12627 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12633 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12637 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12641 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12645 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12649 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12653 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12657 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12660 case SAMPLE_android_clone:
12661 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12664 case SAMPLE_android_move:
12665 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12668 case SAMPLE_spring:
12669 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12673 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12677 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12680 case SAMPLE_eater_eat:
12681 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12685 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12688 case SAMPLE_collect:
12689 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12692 case SAMPLE_diamond:
12693 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12696 case SAMPLE_squash:
12697 /* !!! CHECK THIS !!! */
12699 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12701 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12705 case SAMPLE_wonderfall:
12706 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12710 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12714 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12718 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12722 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12726 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12730 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12733 case SAMPLE_wonder:
12734 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12738 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12741 case SAMPLE_exit_open:
12742 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12745 case SAMPLE_exit_leave:
12746 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12749 case SAMPLE_dynamite:
12750 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12754 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12758 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12762 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12766 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12770 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12774 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12778 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12783 void RaiseScore(int value)
12785 local_player->score += value;
12787 DrawGameValue_Score(local_player->score);
12790 void RaiseScoreElement(int element)
12795 case EL_BD_DIAMOND:
12796 case EL_EMERALD_YELLOW:
12797 case EL_EMERALD_RED:
12798 case EL_EMERALD_PURPLE:
12799 case EL_SP_INFOTRON:
12800 RaiseScore(level.score[SC_EMERALD]);
12803 RaiseScore(level.score[SC_DIAMOND]);
12806 RaiseScore(level.score[SC_CRYSTAL]);
12809 RaiseScore(level.score[SC_PEARL]);
12812 case EL_BD_BUTTERFLY:
12813 case EL_SP_ELECTRON:
12814 RaiseScore(level.score[SC_BUG]);
12817 case EL_BD_FIREFLY:
12818 case EL_SP_SNIKSNAK:
12819 RaiseScore(level.score[SC_SPACESHIP]);
12822 case EL_DARK_YAMYAM:
12823 RaiseScore(level.score[SC_YAMYAM]);
12826 RaiseScore(level.score[SC_ROBOT]);
12829 RaiseScore(level.score[SC_PACMAN]);
12832 RaiseScore(level.score[SC_NUT]);
12835 case EL_SP_DISK_RED:
12836 case EL_DYNABOMB_INCREASE_NUMBER:
12837 case EL_DYNABOMB_INCREASE_SIZE:
12838 case EL_DYNABOMB_INCREASE_POWER:
12839 RaiseScore(level.score[SC_DYNAMITE]);
12841 case EL_SHIELD_NORMAL:
12842 case EL_SHIELD_DEADLY:
12843 RaiseScore(level.score[SC_SHIELD]);
12845 case EL_EXTRA_TIME:
12846 RaiseScore(level.score[SC_TIME_BONUS]);
12860 RaiseScore(level.score[SC_KEY]);
12863 RaiseScore(element_info[element].collect_score);
12868 void RequestQuitGame(boolean ask_if_really_quit)
12870 if (AllPlayersGone ||
12871 !ask_if_really_quit ||
12872 level_editor_test_game ||
12873 Request("Do you really want to quit the game ?",
12874 REQ_ASK | REQ_STAY_CLOSED))
12876 #if defined(NETWORK_AVALIABLE)
12877 if (options.network)
12878 SendToServer_StopPlaying();
12882 game_status = GAME_MODE_MAIN;
12890 if (tape.playing && tape.deactivate_display)
12891 TapeDeactivateDisplayOff(TRUE);
12894 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12897 if (tape.playing && tape.deactivate_display)
12898 TapeDeactivateDisplayOn();
12905 /* ---------- new game button stuff ---------------------------------------- */
12907 /* graphic position values for game buttons */
12908 #define GAME_BUTTON_XSIZE 30
12909 #define GAME_BUTTON_YSIZE 30
12910 #define GAME_BUTTON_XPOS 5
12911 #define GAME_BUTTON_YPOS 215
12912 #define SOUND_BUTTON_XPOS 5
12913 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12915 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12916 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12917 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12918 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12919 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12920 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12927 } gamebutton_info[NUM_GAME_BUTTONS] =
12930 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12935 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12936 GAME_CTRL_ID_PAUSE,
12940 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12945 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12946 SOUND_CTRL_ID_MUSIC,
12947 "background music on/off"
12950 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12951 SOUND_CTRL_ID_LOOPS,
12952 "sound loops on/off"
12955 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12956 SOUND_CTRL_ID_SIMPLE,
12957 "normal sounds on/off"
12961 void CreateGameButtons()
12965 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12967 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12968 struct GadgetInfo *gi;
12971 unsigned long event_mask;
12972 int gd_xoffset, gd_yoffset;
12973 int gd_x1, gd_x2, gd_y1, gd_y2;
12976 gd_xoffset = gamebutton_info[i].x;
12977 gd_yoffset = gamebutton_info[i].y;
12978 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12979 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12981 if (id == GAME_CTRL_ID_STOP ||
12982 id == GAME_CTRL_ID_PAUSE ||
12983 id == GAME_CTRL_ID_PLAY)
12985 button_type = GD_TYPE_NORMAL_BUTTON;
12987 event_mask = GD_EVENT_RELEASED;
12988 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12989 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12993 button_type = GD_TYPE_CHECK_BUTTON;
12995 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12996 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12997 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12998 event_mask = GD_EVENT_PRESSED;
12999 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13000 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13003 gi = CreateGadget(GDI_CUSTOM_ID, id,
13004 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13005 GDI_X, DX + gd_xoffset,
13006 GDI_Y, DY + gd_yoffset,
13007 GDI_WIDTH, GAME_BUTTON_XSIZE,
13008 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13009 GDI_TYPE, button_type,
13010 GDI_STATE, GD_BUTTON_UNPRESSED,
13011 GDI_CHECKED, checked,
13012 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13013 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13014 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13015 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13016 GDI_EVENT_MASK, event_mask,
13017 GDI_CALLBACK_ACTION, HandleGameButtons,
13021 Error(ERR_EXIT, "cannot create gadget");
13023 game_gadget[id] = gi;
13027 void FreeGameButtons()
13031 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13032 FreeGadget(game_gadget[i]);
13035 static void MapGameButtons()
13039 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13040 MapGadget(game_gadget[i]);
13043 void UnmapGameButtons()
13047 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13048 UnmapGadget(game_gadget[i]);
13051 static void HandleGameButtons(struct GadgetInfo *gi)
13053 int id = gi->custom_id;
13055 if (game_status != GAME_MODE_PLAYING)
13060 case GAME_CTRL_ID_STOP:
13061 RequestQuitGame(TRUE);
13064 case GAME_CTRL_ID_PAUSE:
13065 if (options.network)
13067 #if defined(NETWORK_AVALIABLE)
13069 SendToServer_ContinuePlaying();
13071 SendToServer_PausePlaying();
13075 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13078 case GAME_CTRL_ID_PLAY:
13081 #if defined(NETWORK_AVALIABLE)
13082 if (options.network)
13083 SendToServer_ContinuePlaying();
13087 tape.pausing = FALSE;
13088 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13093 case SOUND_CTRL_ID_MUSIC:
13094 if (setup.sound_music)
13096 setup.sound_music = FALSE;
13099 else if (audio.music_available)
13101 setup.sound = setup.sound_music = TRUE;
13103 SetAudioMode(setup.sound);
13109 case SOUND_CTRL_ID_LOOPS:
13110 if (setup.sound_loops)
13111 setup.sound_loops = FALSE;
13112 else if (audio.loops_available)
13114 setup.sound = setup.sound_loops = TRUE;
13115 SetAudioMode(setup.sound);
13119 case SOUND_CTRL_ID_SIMPLE:
13120 if (setup.sound_simple)
13121 setup.sound_simple = FALSE;
13122 else if (audio.sound_available)
13124 setup.sound = setup.sound_simple = TRUE;
13125 SetAudioMode(setup.sound);