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_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
41 #define USE_BLOCK_DELAY_BUGFIX (TRUE * USE_NEW_STUFF * 1)
42 #define USE_GRAVITY_BUGFIX (TRUE * USE_NEW_STUFF * 0)
43 #define USE_GRAVITY_BUGFIX_2 (TRUE * USE_NEW_STUFF * 1)
45 #define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
46 #define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
54 /* for MovePlayer() */
55 #define MF_NO_ACTION 0
59 /* for ScrollPlayer() */
61 #define SCROLL_GO_ON 1
64 #define EX_PHASE_START 0
65 #define EX_TYPE_NONE 0
66 #define EX_TYPE_NORMAL (1 << 0)
67 #define EX_TYPE_CENTER (1 << 1)
68 #define EX_TYPE_BORDER (1 << 2)
69 #define EX_TYPE_CROSS (1 << 3)
70 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
72 /* special positions in the game control window (relative to control window) */
75 #define XX_EMERALDS 29
76 #define YY_EMERALDS 54
77 #define XX_DYNAMITE 29
78 #define YY_DYNAMITE 89
87 /* special positions in the game control window (relative to main window) */
88 #define DX_LEVEL (DX + XX_LEVEL)
89 #define DY_LEVEL (DY + YY_LEVEL)
90 #define DX_EMERALDS (DX + XX_EMERALDS)
91 #define DY_EMERALDS (DY + YY_EMERALDS)
92 #define DX_DYNAMITE (DX + XX_DYNAMITE)
93 #define DY_DYNAMITE (DY + YY_DYNAMITE)
94 #define DX_KEYS (DX + XX_KEYS)
95 #define DY_KEYS (DY + YY_KEYS)
96 #define DX_SCORE (DX + XX_SCORE)
97 #define DY_SCORE (DY + YY_SCORE)
98 #define DX_TIME1 (DX + XX_TIME1)
99 #define DX_TIME2 (DX + XX_TIME2)
100 #define DY_TIME (DY + YY_TIME)
102 /* values for initial player move delay (initial delay counter value) */
103 #define INITIAL_MOVE_DELAY_OFF -1
104 #define INITIAL_MOVE_DELAY_ON 0
106 /* values for player movement speed (which is in fact a delay value) */
107 #define MOVE_DELAY_NORMAL_SPEED 8
108 #define MOVE_DELAY_HIGH_SPEED 4
110 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
111 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
112 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
113 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
115 /* values for other actions */
116 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
118 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
119 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
121 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
123 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
124 RND(element_info[e].push_delay_random))
125 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
126 RND(element_info[e].drop_delay_random))
127 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
128 RND(element_info[e].move_delay_random))
129 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
130 (element_info[e].move_delay_random))
132 #define GET_TARGET_ELEMENT(e, ch) \
133 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
134 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
136 #define GET_VALID_PLAYER_ELEMENT(e) \
137 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
139 #define CAN_GROW_INTO(e) \
140 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
142 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
143 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
146 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
148 (CAN_MOVE_INTO_ACID(e) && \
149 Feld[x][y] == EL_ACID) || \
152 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
153 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
154 (CAN_MOVE_INTO_ACID(e) && \
155 Feld[x][y] == EL_ACID) || \
158 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
159 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
161 (CAN_MOVE_INTO_ACID(e) && \
162 Feld[x][y] == EL_ACID) || \
163 (DONT_COLLIDE_WITH(e) && \
165 !PLAYER_ENEMY_PROTECTED(x, y))))
168 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
169 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
171 (DONT_COLLIDE_WITH(e) && \
173 !PLAYER_ENEMY_PROTECTED(x, y))))
176 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
180 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
183 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
184 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
188 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
191 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
192 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
196 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
199 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
202 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
205 #define PIG_CAN_ENTER_FIELD(e, x, y) \
206 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
208 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
209 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
210 IS_FOOD_PENGUIN(Feld[x][y])))
211 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
214 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
217 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
222 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
223 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
224 (CAN_MOVE_INTO_ACID(e) && \
225 Feld[x][y] == EL_ACID) || \
226 Feld[x][y] == EL_DIAMOND))
228 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
229 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
230 (CAN_MOVE_INTO_ACID(e) && \
231 Feld[x][y] == EL_ACID) || \
232 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
234 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
235 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
236 (CAN_MOVE_INTO_ACID(e) && \
237 Feld[x][y] == EL_ACID) || \
238 IS_AMOEBOID(Feld[x][y])))
240 #define PIG_CAN_ENTER_FIELD(e, x, y) \
241 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
242 (CAN_MOVE_INTO_ACID(e) && \
243 Feld[x][y] == EL_ACID) || \
244 IS_FOOD_PIG(Feld[x][y])))
246 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
247 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
248 (CAN_MOVE_INTO_ACID(e) && \
249 Feld[x][y] == EL_ACID) || \
250 IS_FOOD_PENGUIN(Feld[x][y]) || \
251 Feld[x][y] == EL_EXIT_OPEN))
253 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
254 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
255 (CAN_MOVE_INTO_ACID(e) && \
256 Feld[x][y] == EL_ACID)))
258 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
259 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
260 (CAN_MOVE_INTO_ACID(e) && \
261 Feld[x][y] == EL_ACID) || \
264 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
265 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
266 (CAN_MOVE_INTO_ACID(e) && \
267 Feld[x][y] == EL_ACID)))
271 #define GROUP_NR(e) ((e) - EL_GROUP_START)
272 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
273 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
274 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
276 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
277 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
280 #define CE_ENTER_FIELD_COND(e, x, y) \
281 (!IS_PLAYER(x, y) && \
282 (Feld[x][y] == EL_ACID || \
283 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
285 #define CE_ENTER_FIELD_COND(e, x, y) \
286 (!IS_PLAYER(x, y) && \
287 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
290 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
291 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
293 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
294 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
296 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
297 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
298 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
299 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
301 /* game button identifiers */
302 #define GAME_CTRL_ID_STOP 0
303 #define GAME_CTRL_ID_PAUSE 1
304 #define GAME_CTRL_ID_PLAY 2
305 #define SOUND_CTRL_ID_MUSIC 3
306 #define SOUND_CTRL_ID_LOOPS 4
307 #define SOUND_CTRL_ID_SIMPLE 5
309 #define NUM_GAME_BUTTONS 6
312 /* forward declaration for internal use */
314 static void AdvanceFrameAndPlayerCounters(int);
316 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
317 static boolean MovePlayer(struct PlayerInfo *, int, int);
318 static void ScrollPlayer(struct PlayerInfo *, int);
319 static void ScrollScreen(struct PlayerInfo *, int);
321 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
323 static void InitBeltMovement(void);
324 static void CloseAllOpenTimegates(void);
325 static void CheckGravityMovement(struct PlayerInfo *);
326 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
327 static void KillHeroUnlessEnemyProtected(int, int);
328 static void KillHeroUnlessExplosionProtected(int, int);
330 static void TestIfPlayerTouchesCustomElement(int, int);
331 static void TestIfElementTouchesCustomElement(int, int);
332 static void TestIfElementHitsCustomElement(int, int, int);
334 static void TestIfElementSmashesCustomElement(int, int, int);
337 static void ChangeElement(int, int, int);
339 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
340 #define CheckTriggeredElementChange(x, y, e, ev) \
341 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
343 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
344 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
345 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
346 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
347 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
348 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
351 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
352 #define CheckElementChange(x, y, e, te, ev) \
353 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
354 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
355 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
356 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
357 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
358 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
359 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
361 static void PlayLevelSound(int, int, int);
362 static void PlayLevelSoundNearest(int, int, int);
363 static void PlayLevelSoundAction(int, int, int);
364 static void PlayLevelSoundElementAction(int, int, int, int);
365 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
366 static void PlayLevelSoundActionIfLoop(int, int, int);
367 static void StopLevelSoundActionIfLoop(int, int, int);
368 static void PlayLevelMusic();
370 static void MapGameButtons();
371 static void HandleGameButtons(struct GadgetInfo *);
373 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
376 /* ------------------------------------------------------------------------- */
377 /* definition of elements that automatically change to other elements after */
378 /* a specified time, eventually calling a function when changing */
379 /* ------------------------------------------------------------------------- */
381 /* forward declaration for changer functions */
382 static void InitBuggyBase(int x, int y);
383 static void WarnBuggyBase(int x, int y);
385 static void InitTrap(int x, int y);
386 static void ActivateTrap(int x, int y);
387 static void ChangeActiveTrap(int x, int y);
389 static void InitRobotWheel(int x, int y);
390 static void RunRobotWheel(int x, int y);
391 static void StopRobotWheel(int x, int y);
393 static void InitTimegateWheel(int x, int y);
394 static void RunTimegateWheel(int x, int y);
396 struct ChangingElementInfo
401 void (*pre_change_function)(int x, int y);
402 void (*change_function)(int x, int y);
403 void (*post_change_function)(int x, int y);
406 static struct ChangingElementInfo change_delay_list[] =
457 EL_SWITCHGATE_OPENING,
465 EL_SWITCHGATE_CLOSING,
466 EL_SWITCHGATE_CLOSED,
498 EL_ACID_SPLASH_RIGHT,
507 EL_SP_BUGGY_BASE_ACTIVATING,
514 EL_SP_BUGGY_BASE_ACTIVATING,
515 EL_SP_BUGGY_BASE_ACTIVE,
522 EL_SP_BUGGY_BASE_ACTIVE,
546 EL_ROBOT_WHEEL_ACTIVE,
554 EL_TIMEGATE_SWITCH_ACTIVE,
575 int push_delay_fixed, push_delay_random;
580 { EL_BALLOON, 0, 0 },
582 { EL_SOKOBAN_OBJECT, 2, 0 },
583 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
584 { EL_SATELLITE, 2, 0 },
585 { EL_SP_DISK_YELLOW, 2, 0 },
587 { EL_UNDEFINED, 0, 0 },
595 move_stepsize_list[] =
597 { EL_AMOEBA_DROP, 2 },
598 { EL_AMOEBA_DROPPING, 2 },
599 { EL_QUICKSAND_FILLING, 1 },
600 { EL_QUICKSAND_EMPTYING, 1 },
601 { EL_MAGIC_WALL_FILLING, 2 },
602 { EL_BD_MAGIC_WALL_FILLING, 2 },
603 { EL_MAGIC_WALL_EMPTYING, 2 },
604 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
614 collect_count_list[] =
617 { EL_BD_DIAMOND, 1 },
618 { EL_EMERALD_YELLOW, 1 },
619 { EL_EMERALD_RED, 1 },
620 { EL_EMERALD_PURPLE, 1 },
622 { EL_SP_INFOTRON, 1 },
634 access_direction_list[] =
636 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
637 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
638 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
639 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
640 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
641 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
642 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
643 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
644 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
645 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
646 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
648 { EL_SP_PORT_LEFT, MV_RIGHT },
649 { EL_SP_PORT_RIGHT, MV_LEFT },
650 { EL_SP_PORT_UP, MV_DOWN },
651 { EL_SP_PORT_DOWN, MV_UP },
652 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
653 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
654 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
655 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
656 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
657 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
658 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
659 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
660 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
661 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
662 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
663 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
664 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
665 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
666 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
668 { EL_UNDEFINED, MV_NO_MOVING }
671 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
673 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
674 CH_EVENT_BIT(CE_DELAY))
675 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
676 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
677 IS_JUST_CHANGING(x, y))
679 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
682 void GetPlayerConfig()
684 if (!audio.sound_available)
685 setup.sound_simple = FALSE;
687 if (!audio.loops_available)
688 setup.sound_loops = FALSE;
690 if (!audio.music_available)
691 setup.sound_music = FALSE;
693 if (!video.fullscreen_available)
694 setup.fullscreen = FALSE;
696 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
698 SetAudioMode(setup.sound);
702 static int getBeltNrFromBeltElement(int element)
704 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
705 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
706 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
709 static int getBeltNrFromBeltActiveElement(int element)
711 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
712 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
713 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
716 static int getBeltNrFromBeltSwitchElement(int element)
718 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
719 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
720 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
723 static int getBeltDirNrFromBeltSwitchElement(int element)
725 static int belt_base_element[4] =
727 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
728 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
729 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
730 EL_CONVEYOR_BELT_4_SWITCH_LEFT
733 int belt_nr = getBeltNrFromBeltSwitchElement(element);
734 int belt_dir_nr = element - belt_base_element[belt_nr];
736 return (belt_dir_nr % 3);
739 static int getBeltDirFromBeltSwitchElement(int element)
741 static int belt_move_dir[3] =
748 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
750 return belt_move_dir[belt_dir_nr];
753 static void InitPlayerField(int x, int y, int element, boolean init_game)
755 if (element == EL_SP_MURPHY)
759 if (stored_player[0].present)
761 Feld[x][y] = EL_SP_MURPHY_CLONE;
767 stored_player[0].use_murphy_graphic = TRUE;
770 Feld[x][y] = EL_PLAYER_1;
776 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
777 int jx = player->jx, jy = player->jy;
779 player->present = TRUE;
781 player->block_last_field = (element == EL_SP_MURPHY ?
782 level.sp_block_last_field :
783 level.block_last_field);
785 #if USE_NEW_BLOCK_STYLE
787 player->block_delay = (player->block_last_field ?
788 (element == EL_SP_MURPHY ?
789 level.sp_block_delay :
790 level.block_delay) : 0);
792 player->block_delay = (element == EL_SP_MURPHY ?
793 (player->block_last_field ? 7 : 1) :
794 (player->block_last_field ? 7 : 1));
798 printf("::: block_last_field == %d, block_delay = %d\n",
799 player->block_last_field, player->block_delay);
803 if (!options.network || player->connected)
805 player->active = TRUE;
807 /* remove potentially duplicate players */
808 if (StorePlayer[jx][jy] == Feld[x][y])
809 StorePlayer[jx][jy] = 0;
811 StorePlayer[x][y] = Feld[x][y];
815 printf("Player %d activated.\n", player->element_nr);
816 printf("[Local player is %d and currently %s.]\n",
817 local_player->element_nr,
818 local_player->active ? "active" : "not active");
822 Feld[x][y] = EL_EMPTY;
824 player->jx = player->last_jx = x;
825 player->jy = player->last_jy = y;
829 static void InitField(int x, int y, boolean init_game)
831 int element = Feld[x][y];
840 InitPlayerField(x, y, element, init_game);
843 case EL_SOKOBAN_FIELD_PLAYER:
844 element = Feld[x][y] = EL_PLAYER_1;
845 InitField(x, y, init_game);
847 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
848 InitField(x, y, init_game);
851 case EL_SOKOBAN_FIELD_EMPTY:
852 local_player->sokobanfields_still_needed++;
856 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
857 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
858 else if (x > 0 && Feld[x-1][y] == EL_ACID)
859 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
860 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
861 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
862 else if (y > 0 && Feld[x][y-1] == EL_ACID)
863 Feld[x][y] = EL_ACID_POOL_BOTTOM;
864 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
865 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
873 case EL_SPACESHIP_RIGHT:
874 case EL_SPACESHIP_UP:
875 case EL_SPACESHIP_LEFT:
876 case EL_SPACESHIP_DOWN:
878 case EL_BD_BUTTERFLY_RIGHT:
879 case EL_BD_BUTTERFLY_UP:
880 case EL_BD_BUTTERFLY_LEFT:
881 case EL_BD_BUTTERFLY_DOWN:
882 case EL_BD_BUTTERFLY:
883 case EL_BD_FIREFLY_RIGHT:
884 case EL_BD_FIREFLY_UP:
885 case EL_BD_FIREFLY_LEFT:
886 case EL_BD_FIREFLY_DOWN:
888 case EL_PACMAN_RIGHT:
912 if (y == lev_fieldy - 1)
914 Feld[x][y] = EL_AMOEBA_GROWING;
915 Store[x][y] = EL_AMOEBA_WET;
919 case EL_DYNAMITE_ACTIVE:
920 case EL_SP_DISK_RED_ACTIVE:
921 case EL_DYNABOMB_PLAYER_1_ACTIVE:
922 case EL_DYNABOMB_PLAYER_2_ACTIVE:
923 case EL_DYNABOMB_PLAYER_3_ACTIVE:
924 case EL_DYNABOMB_PLAYER_4_ACTIVE:
929 local_player->lights_still_needed++;
933 local_player->friends_still_needed++;
938 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
943 Feld[x][y] = EL_EMPTY;
948 case EL_EM_KEY_1_FILE:
949 Feld[x][y] = EL_EM_KEY_1;
951 case EL_EM_KEY_2_FILE:
952 Feld[x][y] = EL_EM_KEY_2;
954 case EL_EM_KEY_3_FILE:
955 Feld[x][y] = EL_EM_KEY_3;
957 case EL_EM_KEY_4_FILE:
958 Feld[x][y] = EL_EM_KEY_4;
962 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
963 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
964 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
965 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
966 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
967 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
968 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
969 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
970 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
971 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
972 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
973 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
976 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
977 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
978 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
980 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
982 game.belt_dir[belt_nr] = belt_dir;
983 game.belt_dir_nr[belt_nr] = belt_dir_nr;
985 else /* more than one switch -- set it like the first switch */
987 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
992 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
994 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
997 case EL_LIGHT_SWITCH_ACTIVE:
999 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1003 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1005 else if (IS_GROUP_ELEMENT(element))
1007 struct ElementGroupInfo *group = element_info[element].group;
1008 int last_anim_random_frame = gfx.anim_random_frame;
1011 if (group->choice_mode == ANIM_RANDOM)
1012 gfx.anim_random_frame = RND(group->num_elements_resolved);
1014 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1015 group->choice_mode, 0,
1018 if (group->choice_mode == ANIM_RANDOM)
1019 gfx.anim_random_frame = last_anim_random_frame;
1021 group->choice_pos++;
1023 Feld[x][y] = group->element_resolved[element_pos];
1025 InitField(x, y, init_game);
1031 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1033 InitField(x, y, init_game);
1035 /* not needed to call InitMovDir() -- already done by InitField()! */
1036 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1037 CAN_MOVE(Feld[x][y]))
1041 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1043 int old_element = Feld[x][y];
1045 InitField(x, y, init_game);
1047 /* not needed to call InitMovDir() -- already done by InitField()! */
1048 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1049 CAN_MOVE(old_element) &&
1050 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1053 /* this case is in fact a combination of not less than three bugs:
1054 first, it calls InitMovDir() for elements that can move, although this is
1055 already done by InitField(); then, it checks the element that was at this
1056 field _before_ the call to InitField() (which can change it); lastly, it
1057 was not called for "mole with direction" elements, which were treated as
1058 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1062 inline void DrawGameValue_Emeralds(int value)
1064 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1067 inline void DrawGameValue_Dynamite(int value)
1069 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1072 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1076 /* currently only 4 of 8 possible keys are displayed */
1077 for (i = 0; i < STD_NUM_KEYS; i++)
1079 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1080 el2edimg(EL_KEY_1 + i));
1083 inline void DrawGameValue_Score(int value)
1085 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1088 inline void DrawGameValue_Time(int value)
1091 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1093 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1096 inline void DrawGameValue_Level(int value)
1099 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1102 /* misuse area for displaying emeralds to draw bigger level number */
1103 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1104 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1106 /* now copy it to the area for displaying level number */
1107 BlitBitmap(drawto, drawto,
1108 DX_EMERALDS, DY_EMERALDS + 1,
1109 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1110 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1111 DX_LEVEL - 1, DY_LEVEL + 1);
1113 /* restore the area for displaying emeralds */
1114 DrawGameValue_Emeralds(local_player->gems_still_needed);
1116 /* yes, this is all really ugly :-) */
1120 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1123 int key[MAX_NUM_KEYS];
1126 for (i = 0; i < MAX_NUM_KEYS; i++)
1127 key[i] = key_bits & (1 << i);
1129 DrawGameValue_Level(level_nr);
1131 DrawGameValue_Emeralds(emeralds);
1132 DrawGameValue_Dynamite(dynamite);
1133 DrawGameValue_Score(score);
1134 DrawGameValue_Time(time);
1136 DrawGameValue_Keys(key);
1139 void DrawGameDoorValues()
1143 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1145 DrawGameDoorValues_EM();
1150 DrawGameValue_Level(level_nr);
1152 DrawGameValue_Emeralds(local_player->gems_still_needed);
1153 DrawGameValue_Dynamite(local_player->inventory_size);
1154 DrawGameValue_Score(local_player->score);
1155 DrawGameValue_Time(TimeLeft);
1157 for (i = 0; i < MAX_PLAYERS; i++)
1158 DrawGameValue_Keys(stored_player[i].key);
1161 static void resolve_group_element(int group_element, int recursion_depth)
1163 static int group_nr;
1164 static struct ElementGroupInfo *group;
1165 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1168 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1170 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1171 group_element - EL_GROUP_START + 1);
1173 /* replace element which caused too deep recursion by question mark */
1174 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1179 if (recursion_depth == 0) /* initialization */
1181 group = element_info[group_element].group;
1182 group_nr = group_element - EL_GROUP_START;
1184 group->num_elements_resolved = 0;
1185 group->choice_pos = 0;
1188 for (i = 0; i < actual_group->num_elements; i++)
1190 int element = actual_group->element[i];
1192 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1195 if (IS_GROUP_ELEMENT(element))
1196 resolve_group_element(element, recursion_depth + 1);
1199 group->element_resolved[group->num_elements_resolved++] = element;
1200 element_info[element].in_group[group_nr] = TRUE;
1205 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1207 printf("::: group %d: %d resolved elements\n",
1208 group_element - EL_GROUP_START, group->num_elements_resolved);
1209 for (i = 0; i < group->num_elements_resolved; i++)
1210 printf("::: - %d ['%s']\n", group->element_resolved[i],
1211 element_info[group->element_resolved[i]].token_name);
1218 =============================================================================
1220 -----------------------------------------------------------------------------
1221 initialize game engine due to level / tape version number
1222 =============================================================================
1225 static void InitGameEngine()
1229 /* set game engine from tape file when re-playing, else from level file */
1230 game.engine_version = (tape.playing ? tape.engine_version :
1231 level.game_version);
1233 /* ---------------------------------------------------------------------- */
1234 /* set flags for bugs and changes according to active game engine version */
1235 /* ---------------------------------------------------------------------- */
1239 Before 3.1.0, custom elements that "change when pushing" changed directly
1240 after the player started pushing them (until then handled in "DigField()").
1241 Since 3.1.0, these custom elements are not changed until the "pushing"
1242 move of the element is finished (now handled in "ContinueMoving()").
1244 Affected levels/tapes:
1245 The first condition is generally needed for all levels/tapes before version
1246 3.1.0, which might use the old behaviour before it was changed; known tapes
1247 that are affected are some tapes from the level set "Walpurgis Gardens" by
1249 The second condition is an exception from the above case and is needed for
1250 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1251 above (including some development versions of 3.1.0), but before it was
1252 known that this change would break tapes like the above and was fixed in
1253 3.1.1, so that the changed behaviour was active although the engine version
1254 while recording maybe was before 3.1.0. There is at least one tape that is
1255 affected by this exception, which is the tape for the one-level set "Bug
1256 Machine" by Juergen Bonhagen.
1259 game.use_bug_change_when_pushing =
1260 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1262 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1263 tape.game_version < VERSION_IDENT(3,1,1,0)));
1265 /* ---------------------------------------------------------------------- */
1267 /* dynamically adjust element properties according to game engine version */
1268 InitElementPropertiesEngine(game.engine_version);
1271 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1272 printf(" tape version == %06d [%s] [file: %06d]\n",
1273 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1275 printf(" => game.engine_version == %06d\n", game.engine_version);
1278 /* ---------- recursively resolve group elements ------------------------- */
1280 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1281 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1282 element_info[i].in_group[j] = FALSE;
1284 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1285 resolve_group_element(EL_GROUP_START + i, 0);
1287 /* ---------- initialize player's initial move delay --------------------- */
1289 #if USE_NEW_MOVE_DELAY
1290 /* dynamically adjust player properties according to level information */
1291 game.initial_move_delay_value =
1292 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1294 /* dynamically adjust player properties according to game engine version */
1295 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1296 game.initial_move_delay_value : 0);
1298 /* dynamically adjust player properties according to game engine version */
1299 game.initial_move_delay =
1300 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1301 INITIAL_MOVE_DELAY_OFF);
1303 /* dynamically adjust player properties according to level information */
1304 game.initial_move_delay_value =
1305 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1308 /* ---------- initialize player's initial push delay --------------------- */
1310 /* dynamically adjust player properties according to game engine version */
1311 game.initial_push_delay_value =
1312 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1314 /* ---------- initialize changing elements ------------------------------- */
1316 /* initialize changing elements information */
1317 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1319 struct ElementInfo *ei = &element_info[i];
1321 /* this pointer might have been changed in the level editor */
1322 ei->change = &ei->change_page[0];
1324 if (!IS_CUSTOM_ELEMENT(i))
1326 ei->change->target_element = EL_EMPTY_SPACE;
1327 ei->change->delay_fixed = 0;
1328 ei->change->delay_random = 0;
1329 ei->change->delay_frames = 1;
1332 ei->change_events = CE_BITMASK_DEFAULT;
1333 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1335 ei->event_page_nr[j] = 0;
1336 ei->event_page[j] = &ei->change_page[0];
1340 /* add changing elements from pre-defined list */
1341 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1343 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1344 struct ElementInfo *ei = &element_info[ch_delay->element];
1346 ei->change->target_element = ch_delay->target_element;
1347 ei->change->delay_fixed = ch_delay->change_delay;
1349 ei->change->pre_change_function = ch_delay->pre_change_function;
1350 ei->change->change_function = ch_delay->change_function;
1351 ei->change->post_change_function = ch_delay->post_change_function;
1353 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1356 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1361 /* add change events from custom element configuration */
1362 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1364 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1366 for (j = 0; j < ei->num_change_pages; j++)
1368 if (!ei->change_page[j].can_change)
1371 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1373 /* only add event page for the first page found with this event */
1374 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1375 !(ei->change_events & CH_EVENT_BIT(k)))
1377 ei->change_events |= CH_EVENT_BIT(k);
1378 ei->event_page_nr[k] = j;
1379 ei->event_page[k] = &ei->change_page[j];
1387 /* add change events from custom element configuration */
1388 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1390 int element = EL_CUSTOM_START + i;
1392 /* only add custom elements that change after fixed/random frame delay */
1393 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1394 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1398 /* ---------- initialize run-time trigger player and element ------------- */
1400 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1402 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1404 for (j = 0; j < ei->num_change_pages; j++)
1406 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1407 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1411 /* ---------- initialize trigger events ---------------------------------- */
1413 /* initialize trigger events information */
1414 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1415 trigger_events[i] = EP_BITMASK_DEFAULT;
1418 /* add trigger events from element change event properties */
1419 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1421 struct ElementInfo *ei = &element_info[i];
1423 for (j = 0; j < ei->num_change_pages; j++)
1425 if (!ei->change_page[j].can_change)
1428 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1430 int trigger_element = ei->change_page[j].trigger_element;
1432 if (IS_GROUP_ELEMENT(trigger_element))
1434 struct ElementGroupInfo *group = element_info[trigger_element].group;
1436 for (k = 0; k < group->num_elements_resolved; k++)
1437 trigger_events[group->element_resolved[k]]
1438 |= ei->change_page[j].events;
1441 trigger_events[trigger_element] |= ei->change_page[j].events;
1446 /* add trigger events from element change event properties */
1447 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1448 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1449 trigger_events[element_info[i].change->trigger_element] |=
1450 element_info[i].change->events;
1453 /* ---------- initialize push delay -------------------------------------- */
1455 /* initialize push delay values to default */
1456 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1458 if (!IS_CUSTOM_ELEMENT(i))
1460 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1461 element_info[i].push_delay_random = game.default_push_delay_random;
1465 /* set push delay value for certain elements from pre-defined list */
1466 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1468 int e = push_delay_list[i].element;
1470 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1471 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1474 /* set push delay value for Supaplex elements for newer engine versions */
1475 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1477 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1479 if (IS_SP_ELEMENT(i))
1481 #if USE_NEW_MOVE_STYLE
1482 /* set SP push delay to just enough to push under a falling zonk */
1483 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1485 element_info[i].push_delay_fixed = delay;
1486 element_info[i].push_delay_random = 0;
1488 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1489 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1495 /* ---------- initialize move stepsize ----------------------------------- */
1497 /* initialize move stepsize values to default */
1498 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1499 if (!IS_CUSTOM_ELEMENT(i))
1500 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1502 /* set move stepsize value for certain elements from pre-defined list */
1503 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1505 int e = move_stepsize_list[i].element;
1507 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1511 /* ---------- initialize move dig/leave ---------------------------------- */
1513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1515 element_info[i].can_leave_element = FALSE;
1516 element_info[i].can_leave_element_last = FALSE;
1520 /* ---------- initialize gem count --------------------------------------- */
1522 /* initialize gem count values for each element */
1523 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1524 if (!IS_CUSTOM_ELEMENT(i))
1525 element_info[i].collect_count = 0;
1527 /* add gem count values for all elements from pre-defined list */
1528 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1529 element_info[collect_count_list[i].element].collect_count =
1530 collect_count_list[i].count;
1532 /* ---------- initialize access direction -------------------------------- */
1534 /* initialize access direction values to default (access from every side) */
1535 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1536 if (!IS_CUSTOM_ELEMENT(i))
1537 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1539 /* set access direction value for certain elements from pre-defined list */
1540 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1541 element_info[access_direction_list[i].element].access_direction =
1542 access_direction_list[i].direction;
1547 =============================================================================
1549 -----------------------------------------------------------------------------
1550 initialize and start new game
1551 =============================================================================
1556 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1557 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1558 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1565 #if USE_NEW_AMOEBA_CODE
1566 printf("Using new amoeba code.\n");
1568 printf("Using old amoeba code.\n");
1573 /* don't play tapes over network */
1574 network_playing = (options.network && !tape.playing);
1576 for (i = 0; i < MAX_PLAYERS; i++)
1578 struct PlayerInfo *player = &stored_player[i];
1580 player->index_nr = i;
1581 player->index_bit = (1 << i);
1582 player->element_nr = EL_PLAYER_1 + i;
1584 player->present = FALSE;
1585 player->active = FALSE;
1588 player->effective_action = 0;
1589 player->programmed_action = 0;
1592 player->gems_still_needed = level.gems_needed;
1593 player->sokobanfields_still_needed = 0;
1594 player->lights_still_needed = 0;
1595 player->friends_still_needed = 0;
1597 for (j = 0; j < MAX_NUM_KEYS; j++)
1598 player->key[j] = FALSE;
1600 player->dynabomb_count = 0;
1601 player->dynabomb_size = 1;
1602 player->dynabombs_left = 0;
1603 player->dynabomb_xl = FALSE;
1605 player->MovDir = MV_NO_MOVING;
1608 player->GfxDir = MV_NO_MOVING;
1609 player->GfxAction = ACTION_DEFAULT;
1611 player->StepFrame = 0;
1613 player->use_murphy_graphic = FALSE;
1615 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1616 player->block_delay = -1; /* initialized in InitPlayerField() */
1618 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1620 player->actual_frame_counter = 0;
1622 player->step_counter = 0;
1624 player->last_move_dir = MV_NO_MOVING;
1626 player->is_waiting = FALSE;
1627 player->is_moving = FALSE;
1628 player->is_auto_moving = FALSE;
1629 player->is_digging = FALSE;
1630 player->is_snapping = FALSE;
1631 player->is_collecting = FALSE;
1632 player->is_pushing = FALSE;
1633 player->is_switching = FALSE;
1634 player->is_dropping = FALSE;
1636 player->is_bored = FALSE;
1637 player->is_sleeping = FALSE;
1639 player->frame_counter_bored = -1;
1640 player->frame_counter_sleeping = -1;
1642 player->anim_delay_counter = 0;
1643 player->post_delay_counter = 0;
1645 player->action_waiting = ACTION_DEFAULT;
1646 player->last_action_waiting = ACTION_DEFAULT;
1647 player->special_action_bored = ACTION_DEFAULT;
1648 player->special_action_sleeping = ACTION_DEFAULT;
1650 player->num_special_action_bored = 0;
1651 player->num_special_action_sleeping = 0;
1653 /* determine number of special actions for bored and sleeping animation */
1654 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1656 boolean found = FALSE;
1658 for (k = 0; k < NUM_DIRECTIONS; k++)
1659 if (el_act_dir2img(player->element_nr, j, k) !=
1660 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1664 player->num_special_action_bored++;
1668 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1670 boolean found = FALSE;
1672 for (k = 0; k < NUM_DIRECTIONS; k++)
1673 if (el_act_dir2img(player->element_nr, j, k) !=
1674 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1678 player->num_special_action_sleeping++;
1683 player->switch_x = -1;
1684 player->switch_y = -1;
1686 player->show_envelope = 0;
1688 player->move_delay = game.initial_move_delay;
1689 player->move_delay_value = game.initial_move_delay_value;
1691 player->move_delay_reset_counter = 0;
1693 #if USE_NEW_PUSH_DELAY
1694 player->push_delay = -1; /* initialized when pushing starts */
1695 player->push_delay_value = game.initial_push_delay_value;
1697 player->push_delay = 0;
1698 player->push_delay_value = game.initial_push_delay_value;
1701 player->drop_delay = 0;
1703 player->last_jx = player->last_jy = 0;
1704 player->jx = player->jy = 0;
1706 player->shield_normal_time_left = 0;
1707 player->shield_deadly_time_left = 0;
1709 player->inventory_infinite_element = EL_UNDEFINED;
1710 player->inventory_size = 0;
1712 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1713 SnapField(player, 0, 0);
1715 player->LevelSolved = FALSE;
1716 player->GameOver = FALSE;
1719 network_player_action_received = FALSE;
1721 #if defined(NETWORK_AVALIABLE)
1722 /* initial null action */
1723 if (network_playing)
1724 SendToServer_MovePlayer(MV_NO_MOVING);
1733 TimeLeft = level.time;
1736 ScreenMovDir = MV_NO_MOVING;
1740 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1742 AllPlayersGone = FALSE;
1744 game.yamyam_content_nr = 0;
1745 game.magic_wall_active = FALSE;
1746 game.magic_wall_time_left = 0;
1747 game.light_time_left = 0;
1748 game.timegate_time_left = 0;
1749 game.switchgate_pos = 0;
1750 game.balloon_dir = MV_NO_MOVING;
1751 game.gravity = level.initial_gravity;
1752 game.explosions_delayed = TRUE;
1754 game.envelope_active = FALSE;
1756 for (i = 0; i < NUM_BELTS; i++)
1758 game.belt_dir[i] = MV_NO_MOVING;
1759 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1762 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1763 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1765 for (x = 0; x < lev_fieldx; x++)
1767 for (y = 0; y < lev_fieldy; y++)
1769 Feld[x][y] = level.field[x][y];
1770 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1771 ChangeDelay[x][y] = 0;
1772 ChangePage[x][y] = -1;
1773 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1775 WasJustMoving[x][y] = 0;
1776 WasJustFalling[x][y] = 0;
1777 CheckCollision[x][y] = 0;
1779 Pushed[x][y] = FALSE;
1781 Changed[x][y] = CE_BITMASK_DEFAULT;
1782 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1784 ExplodePhase[x][y] = 0;
1785 ExplodeDelay[x][y] = 0;
1786 ExplodeField[x][y] = EX_TYPE_NONE;
1788 RunnerVisit[x][y] = 0;
1789 PlayerVisit[x][y] = 0;
1792 GfxRandom[x][y] = INIT_GFX_RANDOM();
1793 GfxElement[x][y] = EL_UNDEFINED;
1794 GfxAction[x][y] = ACTION_DEFAULT;
1795 GfxDir[x][y] = MV_NO_MOVING;
1799 for (y = 0; y < lev_fieldy; y++)
1801 for (x = 0; x < lev_fieldx; x++)
1803 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1805 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1807 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1810 InitField(x, y, TRUE);
1816 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1817 emulate_sb ? EMU_SOKOBAN :
1818 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1820 /* initialize explosion and ignition delay */
1821 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1823 if (!IS_CUSTOM_ELEMENT(i))
1826 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1827 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1828 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1829 int last_phase = (num_phase + 1) * delay;
1830 int half_phase = (num_phase / 2) * delay;
1832 element_info[i].explosion_delay = last_phase - 1;
1833 element_info[i].ignition_delay = half_phase;
1836 if (i == EL_BLACK_ORB)
1837 element_info[i].ignition_delay = 0;
1839 if (i == EL_BLACK_ORB)
1840 element_info[i].ignition_delay = 1;
1845 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1846 element_info[i].explosion_delay = 1;
1848 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1849 element_info[i].ignition_delay = 1;
1853 /* correct non-moving belts to start moving left */
1854 for (i = 0; i < NUM_BELTS; i++)
1855 if (game.belt_dir[i] == MV_NO_MOVING)
1856 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1858 /* check if any connected player was not found in playfield */
1859 for (i = 0; i < MAX_PLAYERS; i++)
1861 struct PlayerInfo *player = &stored_player[i];
1863 if (player->connected && !player->present)
1865 for (j = 0; j < MAX_PLAYERS; j++)
1867 struct PlayerInfo *some_player = &stored_player[j];
1868 int jx = some_player->jx, jy = some_player->jy;
1870 /* assign first free player found that is present in the playfield */
1871 if (some_player->present && !some_player->connected)
1873 player->present = TRUE;
1874 player->active = TRUE;
1876 some_player->present = FALSE;
1877 some_player->active = FALSE;
1880 player->element_nr = some_player->element_nr;
1883 #if USE_NEW_BLOCK_STYLE
1884 player->block_last_field = some_player->block_last_field;
1885 player->block_delay = some_player->block_delay;
1888 StorePlayer[jx][jy] = player->element_nr;
1889 player->jx = player->last_jx = jx;
1890 player->jy = player->last_jy = jy;
1900 /* when playing a tape, eliminate all players which do not participate */
1902 for (i = 0; i < MAX_PLAYERS; i++)
1904 if (stored_player[i].active && !tape.player_participates[i])
1906 struct PlayerInfo *player = &stored_player[i];
1907 int jx = player->jx, jy = player->jy;
1909 player->active = FALSE;
1910 StorePlayer[jx][jy] = 0;
1911 Feld[jx][jy] = EL_EMPTY;
1915 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1917 /* when in single player mode, eliminate all but the first active player */
1919 for (i = 0; i < MAX_PLAYERS; i++)
1921 if (stored_player[i].active)
1923 for (j = i + 1; j < MAX_PLAYERS; j++)
1925 if (stored_player[j].active)
1927 struct PlayerInfo *player = &stored_player[j];
1928 int jx = player->jx, jy = player->jy;
1930 player->active = FALSE;
1931 player->present = FALSE;
1933 StorePlayer[jx][jy] = 0;
1934 Feld[jx][jy] = EL_EMPTY;
1941 /* when recording the game, store which players take part in the game */
1944 for (i = 0; i < MAX_PLAYERS; i++)
1945 if (stored_player[i].active)
1946 tape.player_participates[i] = TRUE;
1951 for (i = 0; i < MAX_PLAYERS; i++)
1953 struct PlayerInfo *player = &stored_player[i];
1955 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1960 if (local_player == player)
1961 printf("Player %d is local player.\n", i+1);
1965 if (BorderElement == EL_EMPTY)
1968 SBX_Right = lev_fieldx - SCR_FIELDX;
1970 SBY_Lower = lev_fieldy - SCR_FIELDY;
1975 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1977 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1980 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1981 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1983 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1984 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1986 /* if local player not found, look for custom element that might create
1987 the player (make some assumptions about the right custom element) */
1988 if (!local_player->present)
1990 int start_x = 0, start_y = 0;
1991 int found_rating = 0;
1992 int found_element = EL_UNDEFINED;
1994 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1996 int element = Feld[x][y];
2001 if (!IS_CUSTOM_ELEMENT(element))
2004 if (CAN_CHANGE(element))
2006 for (i = 0; i < element_info[element].num_change_pages; i++)
2008 content = element_info[element].change_page[i].target_element;
2009 is_player = ELEM_IS_PLAYER(content);
2011 if (is_player && (found_rating < 3 || element < found_element))
2017 found_element = element;
2022 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2024 content = element_info[element].content[xx][yy];
2025 is_player = ELEM_IS_PLAYER(content);
2027 if (is_player && (found_rating < 2 || element < found_element))
2029 start_x = x + xx - 1;
2030 start_y = y + yy - 1;
2033 found_element = element;
2036 if (!CAN_CHANGE(element))
2039 for (i = 0; i < element_info[element].num_change_pages; i++)
2041 content= element_info[element].change_page[i].target_content[xx][yy];
2042 is_player = ELEM_IS_PLAYER(content);
2044 if (is_player && (found_rating < 1 || element < found_element))
2046 start_x = x + xx - 1;
2047 start_y = y + yy - 1;
2050 found_element = element;
2056 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2057 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2060 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2061 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2067 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2068 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2069 local_player->jx - MIDPOSX);
2071 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2072 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2073 local_player->jy - MIDPOSY);
2075 scroll_x = SBX_Left;
2076 scroll_y = SBY_Upper;
2077 if (local_player->jx >= SBX_Left + MIDPOSX)
2078 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2079 local_player->jx - MIDPOSX :
2081 if (local_player->jy >= SBY_Upper + MIDPOSY)
2082 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2083 local_player->jy - MIDPOSY :
2088 CloseDoor(DOOR_CLOSE_1);
2090 /* !!! FIX THIS (START) !!! */
2091 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2093 InitGameEngine_EM();
2100 /* after drawing the level, correct some elements */
2101 if (game.timegate_time_left == 0)
2102 CloseAllOpenTimegates();
2104 if (setup.soft_scrolling)
2105 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2107 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2110 /* !!! FIX THIS (END) !!! */
2112 /* copy default game door content to main double buffer */
2113 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2114 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2116 DrawGameDoorValues();
2120 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2121 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2122 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2126 /* copy actual game door content to door double buffer for OpenDoor() */
2127 BlitBitmap(drawto, bitmap_db_door,
2128 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2130 OpenDoor(DOOR_OPEN_ALL);
2132 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2134 if (setup.sound_music)
2137 KeyboardAutoRepeatOffUnlessAutoplay();
2141 for (i = 0; i < MAX_PLAYERS; i++)
2142 printf("Player %d %sactive.\n",
2143 i + 1, (stored_player[i].active ? "" : "not "));
2147 printf("::: starting game [%d]\n", FrameCounter);
2151 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2153 /* this is used for non-R'n'D game engines to update certain engine values */
2155 /* needed to determine if sounds are played within the visible screen area */
2156 scroll_x = actual_scroll_x;
2157 scroll_y = actual_scroll_y;
2160 void InitMovDir(int x, int y)
2162 int i, element = Feld[x][y];
2163 static int xy[4][2] =
2170 static int direction[3][4] =
2172 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2173 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2174 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2183 Feld[x][y] = EL_BUG;
2184 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2187 case EL_SPACESHIP_RIGHT:
2188 case EL_SPACESHIP_UP:
2189 case EL_SPACESHIP_LEFT:
2190 case EL_SPACESHIP_DOWN:
2191 Feld[x][y] = EL_SPACESHIP;
2192 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2195 case EL_BD_BUTTERFLY_RIGHT:
2196 case EL_BD_BUTTERFLY_UP:
2197 case EL_BD_BUTTERFLY_LEFT:
2198 case EL_BD_BUTTERFLY_DOWN:
2199 Feld[x][y] = EL_BD_BUTTERFLY;
2200 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2203 case EL_BD_FIREFLY_RIGHT:
2204 case EL_BD_FIREFLY_UP:
2205 case EL_BD_FIREFLY_LEFT:
2206 case EL_BD_FIREFLY_DOWN:
2207 Feld[x][y] = EL_BD_FIREFLY;
2208 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2211 case EL_PACMAN_RIGHT:
2213 case EL_PACMAN_LEFT:
2214 case EL_PACMAN_DOWN:
2215 Feld[x][y] = EL_PACMAN;
2216 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2219 case EL_SP_SNIKSNAK:
2220 MovDir[x][y] = MV_UP;
2223 case EL_SP_ELECTRON:
2224 MovDir[x][y] = MV_LEFT;
2231 Feld[x][y] = EL_MOLE;
2232 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2236 if (IS_CUSTOM_ELEMENT(element))
2238 struct ElementInfo *ei = &element_info[element];
2239 int move_direction_initial = ei->move_direction_initial;
2240 int move_pattern = ei->move_pattern;
2242 if (move_direction_initial == MV_START_PREVIOUS)
2244 if (MovDir[x][y] != MV_NO_MOVING)
2247 move_direction_initial = MV_START_AUTOMATIC;
2250 if (move_direction_initial == MV_START_RANDOM)
2251 MovDir[x][y] = 1 << RND(4);
2252 else if (move_direction_initial & MV_ANY_DIRECTION)
2253 MovDir[x][y] = move_direction_initial;
2254 else if (move_pattern == MV_ALL_DIRECTIONS ||
2255 move_pattern == MV_TURNING_LEFT ||
2256 move_pattern == MV_TURNING_RIGHT ||
2257 move_pattern == MV_TURNING_LEFT_RIGHT ||
2258 move_pattern == MV_TURNING_RIGHT_LEFT ||
2259 move_pattern == MV_TURNING_RANDOM)
2260 MovDir[x][y] = 1 << RND(4);
2261 else if (move_pattern == MV_HORIZONTAL)
2262 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2263 else if (move_pattern == MV_VERTICAL)
2264 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2265 else if (move_pattern & MV_ANY_DIRECTION)
2266 MovDir[x][y] = element_info[element].move_pattern;
2267 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2268 move_pattern == MV_ALONG_RIGHT_SIDE)
2271 /* use random direction as default start direction */
2272 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2273 MovDir[x][y] = 1 << RND(4);
2276 for (i = 0; i < NUM_DIRECTIONS; i++)
2278 int x1 = x + xy[i][0];
2279 int y1 = y + xy[i][1];
2281 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2283 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2284 MovDir[x][y] = direction[0][i];
2286 MovDir[x][y] = direction[1][i];
2295 MovDir[x][y] = 1 << RND(4);
2297 if (element != EL_BUG &&
2298 element != EL_SPACESHIP &&
2299 element != EL_BD_BUTTERFLY &&
2300 element != EL_BD_FIREFLY)
2303 for (i = 0; i < NUM_DIRECTIONS; i++)
2305 int x1 = x + xy[i][0];
2306 int y1 = y + xy[i][1];
2308 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2310 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2312 MovDir[x][y] = direction[0][i];
2315 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2316 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2318 MovDir[x][y] = direction[1][i];
2327 GfxDir[x][y] = MovDir[x][y];
2330 void InitAmoebaNr(int x, int y)
2333 int group_nr = AmoebeNachbarNr(x, y);
2337 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2339 if (AmoebaCnt[i] == 0)
2347 AmoebaNr[x][y] = group_nr;
2348 AmoebaCnt[group_nr]++;
2349 AmoebaCnt2[group_nr]++;
2355 boolean raise_level = FALSE;
2357 if (local_player->MovPos)
2361 if (tape.auto_play) /* tape might already be stopped here */
2362 tape.auto_play_level_solved = TRUE;
2364 if (tape.playing && tape.auto_play)
2365 tape.auto_play_level_solved = TRUE;
2368 local_player->LevelSolved = FALSE;
2370 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2374 if (!tape.playing && setup.sound_loops)
2375 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2376 SND_CTRL_PLAY_LOOP);
2378 while (TimeLeft > 0)
2380 if (!tape.playing && !setup.sound_loops)
2381 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2382 if (TimeLeft > 0 && !(TimeLeft % 10))
2383 RaiseScore(level.score[SC_TIME_BONUS]);
2384 if (TimeLeft > 100 && !(TimeLeft % 10))
2389 DrawGameValue_Time(TimeLeft);
2397 if (!tape.playing && setup.sound_loops)
2398 StopSound(SND_GAME_LEVELTIME_BONUS);
2400 else if (level.time == 0) /* level without time limit */
2402 if (!tape.playing && setup.sound_loops)
2403 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2404 SND_CTRL_PLAY_LOOP);
2406 while (TimePlayed < 999)
2408 if (!tape.playing && !setup.sound_loops)
2409 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2410 if (TimePlayed < 999 && !(TimePlayed % 10))
2411 RaiseScore(level.score[SC_TIME_BONUS]);
2412 if (TimePlayed < 900 && !(TimePlayed % 10))
2417 DrawGameValue_Time(TimePlayed);
2425 if (!tape.playing && setup.sound_loops)
2426 StopSound(SND_GAME_LEVELTIME_BONUS);
2429 /* close exit door after last player */
2430 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2431 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2432 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2434 int element = Feld[ExitX][ExitY];
2436 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2437 EL_SP_EXIT_CLOSING);
2439 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2442 /* Hero disappears */
2443 if (ExitX >= 0 && ExitY >= 0)
2444 DrawLevelField(ExitX, ExitY);
2451 CloseDoor(DOOR_CLOSE_1);
2456 SaveTape(tape.level_nr); /* Ask to save tape */
2459 if (level_nr == leveldir_current->handicap_level)
2461 leveldir_current->handicap_level++;
2462 SaveLevelSetup_SeriesInfo();
2465 if (level_editor_test_game)
2466 local_player->score = -1; /* no highscore when playing from editor */
2467 else if (level_nr < leveldir_current->last_level)
2468 raise_level = TRUE; /* advance to next level */
2470 if ((hi_pos = NewHiScore()) >= 0)
2472 game_status = GAME_MODE_SCORES;
2473 DrawHallOfFame(hi_pos);
2482 game_status = GAME_MODE_MAIN;
2499 LoadScore(level_nr);
2501 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2502 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2505 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2507 if (local_player->score > highscore[k].Score)
2509 /* player has made it to the hall of fame */
2511 if (k < MAX_SCORE_ENTRIES - 1)
2513 int m = MAX_SCORE_ENTRIES - 1;
2516 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2517 if (!strcmp(setup.player_name, highscore[l].Name))
2519 if (m == k) /* player's new highscore overwrites his old one */
2523 for (l = m; l > k; l--)
2525 strcpy(highscore[l].Name, highscore[l - 1].Name);
2526 highscore[l].Score = highscore[l - 1].Score;
2533 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2534 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2535 highscore[k].Score = local_player->score;
2541 else if (!strncmp(setup.player_name, highscore[k].Name,
2542 MAX_PLAYER_NAME_LEN))
2543 break; /* player already there with a higher score */
2549 SaveScore(level_nr);
2554 inline static int getElementMoveStepsize(int x, int y)
2556 int element = Feld[x][y];
2557 int direction = MovDir[x][y];
2558 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2559 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2560 int horiz_move = (dx != 0);
2561 int sign = (horiz_move ? dx : dy);
2562 int step = sign * element_info[element].move_stepsize;
2564 /* special values for move stepsize for spring and things on conveyor belt */
2568 if (element == EL_SPRING)
2569 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2570 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2571 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2572 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2574 if (CAN_FALL(element) &&
2575 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2576 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2577 else if (element == EL_SPRING)
2578 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2585 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2587 if (player->GfxAction != action || player->GfxDir != dir)
2590 printf("Player frame reset! (%d => %d, %d => %d)\n",
2591 player->GfxAction, action, player->GfxDir, dir);
2594 player->GfxAction = action;
2595 player->GfxDir = dir;
2597 player->StepFrame = 0;
2601 static void ResetRandomAnimationValue(int x, int y)
2603 GfxRandom[x][y] = INIT_GFX_RANDOM();
2606 static void ResetGfxAnimation(int x, int y)
2609 GfxAction[x][y] = ACTION_DEFAULT;
2610 GfxDir[x][y] = MovDir[x][y];
2613 void InitMovingField(int x, int y, int direction)
2615 int element = Feld[x][y];
2616 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2617 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2621 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2622 ResetGfxAnimation(x, y);
2624 #if USE_CAN_MOVE_NOT_MOVING
2626 MovDir[x][y] = direction;
2627 GfxDir[x][y] = direction;
2628 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2629 ACTION_FALLING : ACTION_MOVING);
2631 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2633 if (Feld[newx][newy] == EL_EMPTY)
2634 Feld[newx][newy] = EL_BLOCKED;
2636 MovDir[newx][newy] = MovDir[x][y];
2637 GfxFrame[newx][newy] = GfxFrame[x][y];
2638 GfxRandom[newx][newy] = GfxRandom[x][y];
2639 GfxAction[newx][newy] = GfxAction[x][y];
2640 GfxDir[newx][newy] = GfxDir[x][y];
2645 MovDir[newx][newy] = MovDir[x][y] = direction;
2646 GfxDir[x][y] = direction;
2648 if (Feld[newx][newy] == EL_EMPTY)
2649 Feld[newx][newy] = EL_BLOCKED;
2651 if (direction == MV_DOWN && CAN_FALL(element))
2652 GfxAction[x][y] = ACTION_FALLING;
2654 GfxAction[x][y] = ACTION_MOVING;
2656 GfxFrame[newx][newy] = GfxFrame[x][y];
2657 GfxRandom[newx][newy] = GfxRandom[x][y];
2658 GfxAction[newx][newy] = GfxAction[x][y];
2659 GfxDir[newx][newy] = GfxDir[x][y];
2663 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2665 int direction = MovDir[x][y];
2666 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2667 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2673 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2675 int oldx = x, oldy = y;
2676 int direction = MovDir[x][y];
2678 if (direction == MV_LEFT)
2680 else if (direction == MV_RIGHT)
2682 else if (direction == MV_UP)
2684 else if (direction == MV_DOWN)
2687 *comes_from_x = oldx;
2688 *comes_from_y = oldy;
2691 int MovingOrBlocked2Element(int x, int y)
2693 int element = Feld[x][y];
2695 if (element == EL_BLOCKED)
2699 Blocked2Moving(x, y, &oldx, &oldy);
2700 return Feld[oldx][oldy];
2706 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2708 /* like MovingOrBlocked2Element(), but if element is moving
2709 and (x,y) is the field the moving element is just leaving,
2710 return EL_BLOCKED instead of the element value */
2711 int element = Feld[x][y];
2713 if (IS_MOVING(x, y))
2715 if (element == EL_BLOCKED)
2719 Blocked2Moving(x, y, &oldx, &oldy);
2720 return Feld[oldx][oldy];
2729 static void RemoveField(int x, int y)
2731 Feld[x][y] = EL_EMPTY;
2738 ChangeDelay[x][y] = 0;
2739 ChangePage[x][y] = -1;
2740 Pushed[x][y] = FALSE;
2743 ExplodeField[x][y] = EX_TYPE_NONE;
2746 GfxElement[x][y] = EL_UNDEFINED;
2747 GfxAction[x][y] = ACTION_DEFAULT;
2748 GfxDir[x][y] = MV_NO_MOVING;
2751 void RemoveMovingField(int x, int y)
2753 int oldx = x, oldy = y, newx = x, newy = y;
2754 int element = Feld[x][y];
2755 int next_element = EL_UNDEFINED;
2757 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2760 if (IS_MOVING(x, y))
2762 Moving2Blocked(x, y, &newx, &newy);
2764 if (Feld[newx][newy] != EL_BLOCKED)
2767 if (Feld[newx][newy] != EL_BLOCKED)
2769 /* element is moving, but target field is not free (blocked), but
2770 already occupied by something different (example: acid pool);
2771 in this case, only remove the moving field, but not the target */
2773 RemoveField(oldx, oldy);
2775 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2777 DrawLevelField(oldx, oldy);
2783 else if (element == EL_BLOCKED)
2785 Blocked2Moving(x, y, &oldx, &oldy);
2786 if (!IS_MOVING(oldx, oldy))
2790 if (element == EL_BLOCKED &&
2791 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2792 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2793 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2794 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2795 next_element = get_next_element(Feld[oldx][oldy]);
2797 RemoveField(oldx, oldy);
2798 RemoveField(newx, newy);
2800 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2802 if (next_element != EL_UNDEFINED)
2803 Feld[oldx][oldy] = next_element;
2805 DrawLevelField(oldx, oldy);
2806 DrawLevelField(newx, newy);
2809 void DrawDynamite(int x, int y)
2811 int sx = SCREENX(x), sy = SCREENY(y);
2812 int graphic = el2img(Feld[x][y]);
2815 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2818 if (IS_WALKABLE_INSIDE(Back[x][y]))
2822 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2823 else if (Store[x][y])
2824 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2826 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2829 if (Back[x][y] || Store[x][y])
2830 DrawGraphicThruMask(sx, sy, graphic, frame);
2832 DrawGraphic(sx, sy, graphic, frame);
2834 if (game.emulation == EMU_SUPAPLEX)
2835 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2836 else if (Store[x][y])
2837 DrawGraphicThruMask(sx, sy, graphic, frame);
2839 DrawGraphic(sx, sy, graphic, frame);
2843 void CheckDynamite(int x, int y)
2845 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2849 if (MovDelay[x][y] != 0)
2852 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2859 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2861 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2862 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2863 StopSound(SND_DYNAMITE_ACTIVE);
2865 StopSound(SND_DYNABOMB_ACTIVE);
2871 void DrawRelocatePlayer(struct PlayerInfo *player)
2873 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2874 boolean no_delay = (tape.warp_forward);
2875 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2876 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2877 int jx = player->jx;
2878 int jy = player->jy;
2880 if (level.instant_relocation)
2883 int offset = (setup.scroll_delay ? 3 : 0);
2885 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2887 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2888 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2889 local_player->jx - MIDPOSX);
2891 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2892 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2893 local_player->jy - MIDPOSY);
2897 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2898 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2899 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2901 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2902 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2903 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2905 /* don't scroll over playfield boundaries */
2906 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2907 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2909 /* don't scroll over playfield boundaries */
2910 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2911 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2914 scroll_x += (local_player->jx - old_jx);
2915 scroll_y += (local_player->jy - old_jy);
2917 /* don't scroll over playfield boundaries */
2918 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2919 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2921 /* don't scroll over playfield boundaries */
2922 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2923 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2926 RedrawPlayfield(TRUE, 0,0,0,0);
2932 int offset = (setup.scroll_delay ? 3 : 0);
2934 int scroll_xx = -999, scroll_yy = -999;
2936 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2938 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2941 int fx = FX, fy = FY;
2943 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2944 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2945 local_player->jx - MIDPOSX);
2947 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2948 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2949 local_player->jy - MIDPOSY);
2951 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2952 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2955 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2958 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2965 fx += dx * TILEX / 2;
2966 fy += dy * TILEY / 2;
2968 ScrollLevel(dx, dy);
2971 /* scroll in two steps of half tile size to make things smoother */
2972 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2974 Delay(wait_delay_value);
2976 /* scroll second step to align at full tile size */
2978 Delay(wait_delay_value);
2981 int scroll_xx = -999, scroll_yy = -999;
2983 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2985 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2988 int fx = FX, fy = FY;
2990 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2991 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2992 local_player->jx - MIDPOSX);
2994 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2995 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2996 local_player->jy - MIDPOSY);
2998 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2999 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3002 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3005 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3012 fx += dx * TILEX / 2;
3013 fy += dy * TILEY / 2;
3015 ScrollLevel(dx, dy);
3018 /* scroll in two steps of half tile size to make things smoother */
3019 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3021 Delay(wait_delay_value);
3023 /* scroll second step to align at full tile size */
3025 Delay(wait_delay_value);
3031 Delay(wait_delay_value);
3035 void RelocatePlayer(int jx, int jy, int el_player_raw)
3038 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3040 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3042 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3043 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3044 boolean no_delay = (tape.warp_forward);
3045 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3046 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3047 int old_jx = player->jx;
3048 int old_jy = player->jy;
3049 int old_element = Feld[old_jx][old_jy];
3050 int element = Feld[jx][jy];
3051 boolean player_relocated = (old_jx != jx || old_jy != jy);
3053 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3054 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3056 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3057 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3058 int leave_side_horiz = move_dir_horiz;
3059 int leave_side_vert = move_dir_vert;
3061 static int trigger_sides[4][2] =
3063 /* enter side leave side */
3064 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3065 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3066 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3067 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3069 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3070 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3071 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3072 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3074 int enter_side = enter_side_horiz | enter_side_vert;
3075 int leave_side = leave_side_horiz | leave_side_vert;
3077 if (player->GameOver) /* do not reanimate dead player */
3080 if (!player_relocated) /* no need to relocate the player */
3083 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3085 RemoveField(jx, jy); /* temporarily remove newly placed player */
3086 DrawLevelField(jx, jy);
3089 if (player->present)
3091 while (player->MovPos)
3093 ScrollPlayer(player, SCROLL_GO_ON);
3094 ScrollScreen(NULL, SCROLL_GO_ON);
3096 #if USE_NEW_MOVE_DELAY
3097 AdvanceFrameAndPlayerCounters(player->index_nr);
3105 Delay(wait_delay_value);
3108 DrawPlayer(player); /* needed here only to cleanup last field */
3109 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3111 player->is_moving = FALSE;
3115 if (IS_CUSTOM_ELEMENT(old_element))
3116 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3118 player->index_bit, leave_side);
3120 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3122 player->index_bit, leave_side);
3125 Feld[jx][jy] = el_player;
3126 InitPlayerField(jx, jy, el_player, TRUE);
3128 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3130 Feld[jx][jy] = element;
3131 InitField(jx, jy, FALSE);
3135 if (player == local_player) /* only visually relocate local player */
3136 DrawRelocatePlayer(player);
3140 TestIfHeroTouchesBadThing(jx, jy);
3141 TestIfPlayerTouchesCustomElement(jx, jy);
3145 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3150 /* needed to allow change of walkable custom element by entering player */
3151 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3152 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3154 /* needed to allow change of walkable custom element by entering player */
3155 Changed[jx][jy] = 0; /* allow another change */
3160 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3161 enter_side == MV_LEFT ? "left" :
3162 enter_side == MV_RIGHT ? "right" :
3163 enter_side == MV_UP ? "top" :
3164 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3168 if (IS_CUSTOM_ELEMENT(element))
3169 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3170 player->index_bit, enter_side);
3172 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3173 CE_OTHER_GETS_ENTERED,
3174 player->index_bit, enter_side);
3178 void Explode(int ex, int ey, int phase, int mode)
3185 /* !!! eliminate this variable !!! */
3186 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3191 int last_phase = num_phase * delay;
3192 int half_phase = (num_phase / 2) * delay;
3193 int first_phase_after_start = EX_PHASE_START + 1;
3197 if (game.explosions_delayed)
3199 ExplodeField[ex][ey] = mode;
3203 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3205 int center_element = Feld[ex][ey];
3208 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3212 /* --- This is only really needed (and now handled) in "Impact()". --- */
3213 /* do not explode moving elements that left the explode field in time */
3214 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3215 center_element == EL_EMPTY &&
3216 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3221 if (mode == EX_TYPE_NORMAL ||
3222 mode == EX_TYPE_CENTER ||
3223 mode == EX_TYPE_CROSS)
3224 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3226 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3227 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3230 /* remove things displayed in background while burning dynamite */
3231 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3234 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3236 /* put moving element to center field (and let it explode there) */
3237 center_element = MovingOrBlocked2Element(ex, ey);
3238 RemoveMovingField(ex, ey);
3239 Feld[ex][ey] = center_element;
3245 last_phase = element_info[center_element].explosion_delay + 1;
3247 last_phase = element_info[center_element].explosion_delay;
3251 printf("::: %d -> %d\n", center_element, last_phase);
3255 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3257 int xx = x - ex + 1;
3258 int yy = y - ey + 1;
3263 if (!IN_LEV_FIELD(x, y) ||
3264 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3265 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3268 if (!IN_LEV_FIELD(x, y) ||
3269 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3273 if (!IN_LEV_FIELD(x, y) ||
3274 ((mode != EX_TYPE_NORMAL ||
3275 center_element == EL_AMOEBA_TO_DIAMOND) &&
3276 (x != ex || y != ey)))
3280 element = Feld[x][y];
3282 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3284 element = MovingOrBlocked2Element(x, y);
3286 if (!IS_EXPLOSION_PROOF(element))
3287 RemoveMovingField(x, y);
3293 if (IS_EXPLOSION_PROOF(element))
3296 /* indestructible elements can only explode in center (but not flames) */
3298 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3299 mode == EX_TYPE_BORDER)) ||
3300 element == EL_FLAMES)
3303 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3304 element == EL_FLAMES)
3310 if ((IS_INDESTRUCTIBLE(element) &&
3311 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3312 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3313 element == EL_FLAMES)
3318 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3319 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3320 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3322 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3325 if (IS_ACTIVE_BOMB(element))
3327 /* re-activate things under the bomb like gate or penguin */
3329 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3332 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3337 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3338 element_info[Feld[x][y]].token_name,
3339 Store[x][y], Store2[x][y]);
3346 /* save walkable background elements while explosion on same tile */
3348 if (IS_INDESTRUCTIBLE(element))
3349 Back[x][y] = element;
3353 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3354 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3355 Back[x][y] = element;
3357 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3358 (x != ex || y != ey))
3359 Back[x][y] = element;
3362 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3363 Back[x][y] = element;
3367 /* ignite explodable elements reached by other explosion */
3368 if (element == EL_EXPLOSION)
3369 element = Store2[x][y];
3372 if (AmoebaNr[x][y] &&
3373 (element == EL_AMOEBA_FULL ||
3374 element == EL_BD_AMOEBA ||
3375 element == EL_AMOEBA_GROWING))
3377 AmoebaCnt[AmoebaNr[x][y]]--;
3378 AmoebaCnt2[AmoebaNr[x][y]]--;
3384 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3386 switch(StorePlayer[ex][ey])
3389 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3392 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3395 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3399 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3404 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3405 Store[x][y] = EL_EMPTY;
3407 if (game.emulation == EMU_SUPAPLEX)
3408 Store[x][y] = EL_EMPTY;
3411 else if (center_element == EL_MOLE)
3412 Store[x][y] = EL_EMERALD_RED;
3413 else if (center_element == EL_PENGUIN)
3414 Store[x][y] = EL_EMERALD_PURPLE;
3415 else if (center_element == EL_BUG)
3416 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3417 else if (center_element == EL_BD_BUTTERFLY)
3418 Store[x][y] = EL_BD_DIAMOND;
3419 else if (center_element == EL_SP_ELECTRON)
3420 Store[x][y] = EL_SP_INFOTRON;
3421 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3422 Store[x][y] = level.amoeba_content;
3423 else if (center_element == EL_YAMYAM)
3424 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3425 else if (IS_CUSTOM_ELEMENT(center_element) &&
3426 element_info[center_element].content[xx][yy] != EL_EMPTY)
3427 Store[x][y] = element_info[center_element].content[xx][yy];
3428 else if (element == EL_WALL_EMERALD)
3429 Store[x][y] = EL_EMERALD;
3430 else if (element == EL_WALL_DIAMOND)
3431 Store[x][y] = EL_DIAMOND;
3432 else if (element == EL_WALL_BD_DIAMOND)
3433 Store[x][y] = EL_BD_DIAMOND;
3434 else if (element == EL_WALL_EMERALD_YELLOW)
3435 Store[x][y] = EL_EMERALD_YELLOW;
3436 else if (element == EL_WALL_EMERALD_RED)
3437 Store[x][y] = EL_EMERALD_RED;
3438 else if (element == EL_WALL_EMERALD_PURPLE)
3439 Store[x][y] = EL_EMERALD_PURPLE;
3440 else if (element == EL_WALL_PEARL)
3441 Store[x][y] = EL_PEARL;
3442 else if (element == EL_WALL_CRYSTAL)
3443 Store[x][y] = EL_CRYSTAL;
3444 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3445 Store[x][y] = element_info[element].content[1][1];
3447 Store[x][y] = EL_EMPTY;
3449 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3450 center_element == EL_AMOEBA_TO_DIAMOND)
3451 Store2[x][y] = element;
3454 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3455 element_info[Store2[x][y]].token_name);
3459 if (AmoebaNr[x][y] &&
3460 (element == EL_AMOEBA_FULL ||
3461 element == EL_BD_AMOEBA ||
3462 element == EL_AMOEBA_GROWING))
3464 AmoebaCnt[AmoebaNr[x][y]]--;
3465 AmoebaCnt2[AmoebaNr[x][y]]--;
3471 MovDir[x][y] = MovPos[x][y] = 0;
3472 GfxDir[x][y] = MovDir[x][y];
3477 Feld[x][y] = EL_EXPLOSION;
3479 GfxElement[x][y] = center_element;
3481 GfxElement[x][y] = EL_UNDEFINED;
3484 ExplodePhase[x][y] = 1;
3486 ExplodeDelay[x][y] = last_phase;
3491 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3493 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3500 if (center_element == EL_YAMYAM)
3501 game.yamyam_content_nr =
3502 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3505 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3506 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3520 GfxFrame[x][y] = 0; /* restart explosion animation */
3524 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3528 last_phase = ExplodeDelay[x][y];
3531 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3535 /* activate this even in non-DEBUG version until cause for crash in
3536 getGraphicAnimationFrame() (see below) is found and eliminated */
3540 if (GfxElement[x][y] == EL_UNDEFINED)
3543 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3544 printf("Explode(): This should never happen!\n");
3547 GfxElement[x][y] = EL_EMPTY;
3553 border_element = Store2[x][y];
3555 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3556 border_element = StorePlayer[x][y];
3558 if (IS_PLAYER(x, y))
3559 border_element = StorePlayer[x][y];
3563 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3564 element_info[border_element].token_name, Store2[x][y]);
3568 printf("::: phase == %d\n", phase);
3571 if (phase == element_info[border_element].ignition_delay ||
3572 phase == last_phase)
3574 boolean border_explosion = FALSE;
3578 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3579 !PLAYER_EXPLOSION_PROTECTED(x, y))
3581 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3584 if (IS_PLAYER(x, y))
3587 KillHeroUnlessExplosionProtected(x, y);
3588 border_explosion = TRUE;
3591 if (phase == last_phase)
3592 printf("::: IS_PLAYER\n");
3595 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3598 printf("::: %d,%d: %d %s\n", x, y, border_element,
3599 element_info[border_element].token_name);
3602 Feld[x][y] = Store2[x][y];
3605 border_explosion = TRUE;
3608 if (phase == last_phase)
3609 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3612 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3614 AmoebeUmwandeln(x, y);
3616 border_explosion = TRUE;
3619 if (phase == last_phase)
3620 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3621 element_info[border_element].explosion_delay,
3622 element_info[border_element].ignition_delay,
3628 /* if an element just explodes due to another explosion (chain-reaction),
3629 do not immediately end the new explosion when it was the last frame of
3630 the explosion (as it would be done in the following "if"-statement!) */
3631 if (border_explosion && phase == last_phase)
3638 if (phase == first_phase_after_start)
3640 int element = Store2[x][y];
3642 if (element == EL_BLACK_ORB)
3644 Feld[x][y] = Store2[x][y];
3649 else if (phase == half_phase)
3651 int element = Store2[x][y];
3653 if (IS_PLAYER(x, y))
3654 KillHeroUnlessExplosionProtected(x, y);
3655 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3657 Feld[x][y] = Store2[x][y];
3661 else if (element == EL_AMOEBA_TO_DIAMOND)
3662 AmoebeUmwandeln(x, y);
3666 if (phase == last_phase)
3671 printf("::: done: phase == %d\n", phase);
3675 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3678 element = Feld[x][y] = Store[x][y];
3679 Store[x][y] = Store2[x][y] = 0;
3680 GfxElement[x][y] = EL_UNDEFINED;
3682 /* player can escape from explosions and might therefore be still alive */
3683 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3684 element <= EL_PLAYER_IS_EXPLODING_4)
3685 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3687 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3688 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3689 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3692 /* restore probably existing indestructible background element */
3693 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3694 element = Feld[x][y] = Back[x][y];
3697 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3698 GfxDir[x][y] = MV_NO_MOVING;
3699 ChangeDelay[x][y] = 0;
3700 ChangePage[x][y] = -1;
3703 InitField_WithBug2(x, y, FALSE);
3705 InitField(x, y, FALSE);
3707 /* !!! not needed !!! */
3709 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3710 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3713 if (CAN_MOVE(element))
3718 DrawLevelField(x, y);
3720 TestIfElementTouchesCustomElement(x, y);
3722 if (GFX_CRUMBLED(element))
3723 DrawLevelFieldCrumbledSandNeighbours(x, y);
3725 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3726 StorePlayer[x][y] = 0;
3728 if (ELEM_IS_PLAYER(element))
3729 RelocatePlayer(x, y, element);
3732 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3734 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3738 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3740 int stored = Store[x][y];
3741 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3742 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3746 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3748 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3752 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3756 printf("::: %d / %d [%d - %d]\n",
3757 GfxFrame[x][y], phase - delay, phase, delay);
3761 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3762 element_info[GfxElement[x][y]].token_name,
3767 DrawLevelFieldCrumbledSand(x, y);
3769 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3771 DrawLevelElement(x, y, Back[x][y]);
3772 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3774 else if (IS_WALKABLE_UNDER(Back[x][y]))
3776 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3777 DrawLevelElementThruMask(x, y, Back[x][y]);
3779 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3780 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3784 void DynaExplode(int ex, int ey)
3787 int dynabomb_element = Feld[ex][ey];
3788 int dynabomb_size = 1;
3789 boolean dynabomb_xl = FALSE;
3790 struct PlayerInfo *player;
3791 static int xy[4][2] =
3799 if (IS_ACTIVE_BOMB(dynabomb_element))
3801 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3802 dynabomb_size = player->dynabomb_size;
3803 dynabomb_xl = player->dynabomb_xl;
3804 player->dynabombs_left++;
3807 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3809 for (i = 0; i < NUM_DIRECTIONS; i++)
3811 for (j = 1; j <= dynabomb_size; j++)
3813 int x = ex + j * xy[i][0];
3814 int y = ey + j * xy[i][1];
3817 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3820 element = Feld[x][y];
3822 /* do not restart explosions of fields with active bombs */
3823 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3826 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3830 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3831 !IS_DIGGABLE(element) && !dynabomb_xl)
3834 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3835 !CAN_GROW_INTO(element) && !dynabomb_xl)
3839 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3840 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3841 element != EL_SAND && !dynabomb_xl)
3848 void Bang(int x, int y)
3851 int element = MovingOrBlocked2Element(x, y);
3853 int element = Feld[x][y];
3857 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3859 if (IS_PLAYER(x, y))
3862 struct PlayerInfo *player = PLAYERINFO(x, y);
3864 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3865 player->element_nr);
3870 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3872 if (game.emulation == EMU_SUPAPLEX)
3873 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3875 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3880 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3888 case EL_BD_BUTTERFLY:
3891 case EL_DARK_YAMYAM:
3895 RaiseScoreElement(element);
3896 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3898 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3899 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3900 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3901 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3902 case EL_DYNABOMB_INCREASE_NUMBER:
3903 case EL_DYNABOMB_INCREASE_SIZE:
3904 case EL_DYNABOMB_INCREASE_POWER:
3909 case EL_LAMP_ACTIVE:
3911 case EL_AMOEBA_TO_DIAMOND:
3913 if (IS_PLAYER(x, y))
3914 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3916 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3920 if (element_info[element].explosion_type == EXPLODES_CROSS)
3922 if (CAN_EXPLODE_CROSS(element))
3925 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3930 else if (element_info[element].explosion_type == EXPLODES_1X1)
3932 else if (CAN_EXPLODE_1X1(element))
3934 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3936 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3940 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3943 void SplashAcid(int x, int y)
3946 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3947 (!IN_LEV_FIELD(x - 1, y - 2) ||
3948 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3949 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3951 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3952 (!IN_LEV_FIELD(x + 1, y - 2) ||
3953 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3954 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3956 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3958 /* input: position of element entering acid (obsolete) */
3960 int element = Feld[x][y];
3962 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3965 if (element != EL_ACID_SPLASH_LEFT &&
3966 element != EL_ACID_SPLASH_RIGHT)
3968 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3970 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3971 (!IN_LEV_FIELD(x - 1, y - 1) ||
3972 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3973 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3975 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3976 (!IN_LEV_FIELD(x + 1, y - 1) ||
3977 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3978 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3983 static void InitBeltMovement()
3985 static int belt_base_element[4] =
3987 EL_CONVEYOR_BELT_1_LEFT,
3988 EL_CONVEYOR_BELT_2_LEFT,
3989 EL_CONVEYOR_BELT_3_LEFT,
3990 EL_CONVEYOR_BELT_4_LEFT
3992 static int belt_base_active_element[4] =
3994 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3995 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3996 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3997 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4002 /* set frame order for belt animation graphic according to belt direction */
4003 for (i = 0; i < NUM_BELTS; i++)
4007 for (j = 0; j < NUM_BELT_PARTS; j++)
4009 int element = belt_base_active_element[belt_nr] + j;
4010 int graphic = el2img(element);
4012 if (game.belt_dir[i] == MV_LEFT)
4013 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4015 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4019 for (y = 0; y < lev_fieldy; y++)
4021 for (x = 0; x < lev_fieldx; x++)
4023 int element = Feld[x][y];
4025 for (i = 0; i < NUM_BELTS; i++)
4027 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4029 int e_belt_nr = getBeltNrFromBeltElement(element);
4032 if (e_belt_nr == belt_nr)
4034 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4036 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4044 static void ToggleBeltSwitch(int x, int y)
4046 static int belt_base_element[4] =
4048 EL_CONVEYOR_BELT_1_LEFT,
4049 EL_CONVEYOR_BELT_2_LEFT,
4050 EL_CONVEYOR_BELT_3_LEFT,
4051 EL_CONVEYOR_BELT_4_LEFT
4053 static int belt_base_active_element[4] =
4055 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4056 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4057 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4058 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4060 static int belt_base_switch_element[4] =
4062 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4063 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4064 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4065 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4067 static int belt_move_dir[4] =
4075 int element = Feld[x][y];
4076 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4077 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4078 int belt_dir = belt_move_dir[belt_dir_nr];
4081 if (!IS_BELT_SWITCH(element))
4084 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4085 game.belt_dir[belt_nr] = belt_dir;
4087 if (belt_dir_nr == 3)
4090 /* set frame order for belt animation graphic according to belt direction */
4091 for (i = 0; i < NUM_BELT_PARTS; i++)
4093 int element = belt_base_active_element[belt_nr] + i;
4094 int graphic = el2img(element);
4096 if (belt_dir == MV_LEFT)
4097 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4099 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4102 for (yy = 0; yy < lev_fieldy; yy++)
4104 for (xx = 0; xx < lev_fieldx; xx++)
4106 int element = Feld[xx][yy];
4108 if (IS_BELT_SWITCH(element))
4110 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4112 if (e_belt_nr == belt_nr)
4114 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4115 DrawLevelField(xx, yy);
4118 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4120 int e_belt_nr = getBeltNrFromBeltElement(element);
4122 if (e_belt_nr == belt_nr)
4124 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4126 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4127 DrawLevelField(xx, yy);
4130 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4132 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4134 if (e_belt_nr == belt_nr)
4136 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4138 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4139 DrawLevelField(xx, yy);
4146 static void ToggleSwitchgateSwitch(int x, int y)
4150 game.switchgate_pos = !game.switchgate_pos;
4152 for (yy = 0; yy < lev_fieldy; yy++)
4154 for (xx = 0; xx < lev_fieldx; xx++)
4156 int element = Feld[xx][yy];
4158 if (element == EL_SWITCHGATE_SWITCH_UP ||
4159 element == EL_SWITCHGATE_SWITCH_DOWN)
4161 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4162 DrawLevelField(xx, yy);
4164 else if (element == EL_SWITCHGATE_OPEN ||
4165 element == EL_SWITCHGATE_OPENING)
4167 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4169 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4171 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4174 else if (element == EL_SWITCHGATE_CLOSED ||
4175 element == EL_SWITCHGATE_CLOSING)
4177 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4179 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4181 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4188 static int getInvisibleActiveFromInvisibleElement(int element)
4190 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4191 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4192 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4196 static int getInvisibleFromInvisibleActiveElement(int element)
4198 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4199 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4200 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4204 static void RedrawAllLightSwitchesAndInvisibleElements()
4208 for (y = 0; y < lev_fieldy; y++)
4210 for (x = 0; x < lev_fieldx; x++)
4212 int element = Feld[x][y];
4214 if (element == EL_LIGHT_SWITCH &&
4215 game.light_time_left > 0)
4217 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4218 DrawLevelField(x, y);
4220 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4221 game.light_time_left == 0)
4223 Feld[x][y] = EL_LIGHT_SWITCH;
4224 DrawLevelField(x, y);
4226 else if (element == EL_INVISIBLE_STEELWALL ||
4227 element == EL_INVISIBLE_WALL ||
4228 element == EL_INVISIBLE_SAND)
4230 if (game.light_time_left > 0)
4231 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4233 DrawLevelField(x, y);
4235 /* uncrumble neighbour fields, if needed */
4236 if (element == EL_INVISIBLE_SAND)
4237 DrawLevelFieldCrumbledSandNeighbours(x, y);
4239 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4240 element == EL_INVISIBLE_WALL_ACTIVE ||
4241 element == EL_INVISIBLE_SAND_ACTIVE)
4243 if (game.light_time_left == 0)
4244 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4246 DrawLevelField(x, y);
4248 /* re-crumble neighbour fields, if needed */
4249 if (element == EL_INVISIBLE_SAND)
4250 DrawLevelFieldCrumbledSandNeighbours(x, y);
4256 static void ToggleLightSwitch(int x, int y)
4258 int element = Feld[x][y];
4260 game.light_time_left =
4261 (element == EL_LIGHT_SWITCH ?
4262 level.time_light * FRAMES_PER_SECOND : 0);
4264 RedrawAllLightSwitchesAndInvisibleElements();
4267 static void ActivateTimegateSwitch(int x, int y)
4271 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4273 for (yy = 0; yy < lev_fieldy; yy++)
4275 for (xx = 0; xx < lev_fieldx; xx++)
4277 int element = Feld[xx][yy];
4279 if (element == EL_TIMEGATE_CLOSED ||
4280 element == EL_TIMEGATE_CLOSING)
4282 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4283 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4287 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4289 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4290 DrawLevelField(xx, yy);
4297 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4300 void Impact(int x, int y)
4302 boolean lastline = (y == lev_fieldy-1);
4303 boolean object_hit = FALSE;
4304 boolean impact = (lastline || object_hit);
4305 int element = Feld[x][y];
4306 int smashed = EL_STEELWALL;
4308 if (!lastline) /* check if element below was hit */
4310 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4313 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4314 MovDir[x][y + 1] != MV_DOWN ||
4315 MovPos[x][y + 1] <= TILEY / 2));
4318 object_hit = !IS_FREE(x, y + 1);
4321 /* do not smash moving elements that left the smashed field in time */
4322 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4323 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4327 smashed = MovingOrBlocked2Element(x, y + 1);
4329 impact = (lastline || object_hit);
4332 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4334 SplashAcid(x, y + 1);
4338 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4339 /* only reset graphic animation if graphic really changes after impact */
4341 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4343 ResetGfxAnimation(x, y);
4344 DrawLevelField(x, y);
4347 if (impact && CAN_EXPLODE_IMPACT(element))
4352 else if (impact && element == EL_PEARL)
4354 ResetGfxAnimation(x, y);
4356 Feld[x][y] = EL_PEARL_BREAKING;
4357 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4360 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4362 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4367 if (impact && element == EL_AMOEBA_DROP)
4369 if (object_hit && IS_PLAYER(x, y + 1))
4370 KillHeroUnlessEnemyProtected(x, y + 1);
4371 else if (object_hit && smashed == EL_PENGUIN)
4375 Feld[x][y] = EL_AMOEBA_GROWING;
4376 Store[x][y] = EL_AMOEBA_WET;
4378 ResetRandomAnimationValue(x, y);
4383 if (object_hit) /* check which object was hit */
4385 if (CAN_PASS_MAGIC_WALL(element) &&
4386 (smashed == EL_MAGIC_WALL ||
4387 smashed == EL_BD_MAGIC_WALL))
4390 int activated_magic_wall =
4391 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4392 EL_BD_MAGIC_WALL_ACTIVE);
4394 /* activate magic wall / mill */
4395 for (yy = 0; yy < lev_fieldy; yy++)
4396 for (xx = 0; xx < lev_fieldx; xx++)
4397 if (Feld[xx][yy] == smashed)
4398 Feld[xx][yy] = activated_magic_wall;
4400 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4401 game.magic_wall_active = TRUE;
4403 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4404 SND_MAGIC_WALL_ACTIVATING :
4405 SND_BD_MAGIC_WALL_ACTIVATING));
4408 if (IS_PLAYER(x, y + 1))
4410 if (CAN_SMASH_PLAYER(element))
4412 KillHeroUnlessEnemyProtected(x, y + 1);
4416 else if (smashed == EL_PENGUIN)
4418 if (CAN_SMASH_PLAYER(element))
4424 else if (element == EL_BD_DIAMOND)
4426 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4432 else if (((element == EL_SP_INFOTRON ||
4433 element == EL_SP_ZONK) &&
4434 (smashed == EL_SP_SNIKSNAK ||
4435 smashed == EL_SP_ELECTRON ||
4436 smashed == EL_SP_DISK_ORANGE)) ||
4437 (element == EL_SP_INFOTRON &&
4438 smashed == EL_SP_DISK_YELLOW))
4444 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4450 else if (CAN_SMASH_EVERYTHING(element))
4452 if (IS_CLASSIC_ENEMY(smashed) ||
4453 CAN_EXPLODE_SMASHED(smashed))
4458 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4460 if (smashed == EL_LAMP ||
4461 smashed == EL_LAMP_ACTIVE)
4466 else if (smashed == EL_NUT)
4468 Feld[x][y + 1] = EL_NUT_BREAKING;
4469 PlayLevelSound(x, y, SND_NUT_BREAKING);
4470 RaiseScoreElement(EL_NUT);
4473 else if (smashed == EL_PEARL)
4475 ResetGfxAnimation(x, y);
4477 Feld[x][y + 1] = EL_PEARL_BREAKING;
4478 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4481 else if (smashed == EL_DIAMOND)
4483 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4484 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4487 else if (IS_BELT_SWITCH(smashed))
4489 ToggleBeltSwitch(x, y + 1);
4491 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4492 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4494 ToggleSwitchgateSwitch(x, y + 1);
4496 else if (smashed == EL_LIGHT_SWITCH ||
4497 smashed == EL_LIGHT_SWITCH_ACTIVE)
4499 ToggleLightSwitch(x, y + 1);
4504 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4507 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4510 /* !!! TEST ONLY !!! */
4511 CheckElementChangeBySide(x, y + 1, smashed, element,
4512 CE_SWITCHED, CH_SIDE_TOP);
4513 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4514 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4516 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4517 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4518 CheckElementChangeBySide(x, y + 1, smashed, element,
4519 CE_SWITCHED, CH_SIDE_TOP);
4525 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4530 /* play sound of magic wall / mill */
4532 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4533 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4535 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4536 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4537 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4538 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4543 /* play sound of object that hits the ground */
4544 if (lastline || object_hit)
4545 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4548 inline static void TurnRoundExt(int x, int y)
4560 { 0, 0 }, { 0, 0 }, { 0, 0 },
4565 int left, right, back;
4569 { MV_DOWN, MV_UP, MV_RIGHT },
4570 { MV_UP, MV_DOWN, MV_LEFT },
4572 { MV_LEFT, MV_RIGHT, MV_DOWN },
4576 { MV_RIGHT, MV_LEFT, MV_UP }
4579 int element = Feld[x][y];
4580 int move_pattern = element_info[element].move_pattern;
4582 int old_move_dir = MovDir[x][y];
4583 int left_dir = turn[old_move_dir].left;
4584 int right_dir = turn[old_move_dir].right;
4585 int back_dir = turn[old_move_dir].back;
4587 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4588 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4589 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4590 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4592 int left_x = x + left_dx, left_y = y + left_dy;
4593 int right_x = x + right_dx, right_y = y + right_dy;
4594 int move_x = x + move_dx, move_y = y + move_dy;
4598 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4600 TestIfBadThingTouchesOtherBadThing(x, y);
4602 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4603 MovDir[x][y] = right_dir;
4604 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4605 MovDir[x][y] = left_dir;
4607 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4609 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4613 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4614 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4616 TestIfBadThingTouchesOtherBadThing(x, y);
4618 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4619 MovDir[x][y] = left_dir;
4620 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4621 MovDir[x][y] = right_dir;
4623 if ((element == EL_SPACESHIP ||
4624 element == EL_SP_SNIKSNAK ||
4625 element == EL_SP_ELECTRON)
4626 && MovDir[x][y] != old_move_dir)
4628 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4632 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4634 TestIfBadThingTouchesOtherBadThing(x, y);
4636 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4637 MovDir[x][y] = left_dir;
4638 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4639 MovDir[x][y] = right_dir;
4641 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4643 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4646 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4648 TestIfBadThingTouchesOtherBadThing(x, y);
4650 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4651 MovDir[x][y] = left_dir;
4652 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4653 MovDir[x][y] = right_dir;
4655 if (MovDir[x][y] != old_move_dir)
4659 else if (element == EL_YAMYAM)
4661 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4662 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4664 if (can_turn_left && can_turn_right)
4665 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4666 else if (can_turn_left)
4667 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4668 else if (can_turn_right)
4669 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4671 MovDir[x][y] = back_dir;
4673 MovDelay[x][y] = 16 + 16 * RND(3);
4675 else if (element == EL_DARK_YAMYAM)
4677 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4679 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4682 if (can_turn_left && can_turn_right)
4683 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4684 else if (can_turn_left)
4685 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4686 else if (can_turn_right)
4687 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4689 MovDir[x][y] = back_dir;
4691 MovDelay[x][y] = 16 + 16 * RND(3);
4693 else if (element == EL_PACMAN)
4695 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4696 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4698 if (can_turn_left && can_turn_right)
4699 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4700 else if (can_turn_left)
4701 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4702 else if (can_turn_right)
4703 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4705 MovDir[x][y] = back_dir;
4707 MovDelay[x][y] = 6 + RND(40);
4709 else if (element == EL_PIG)
4711 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4712 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4713 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4714 boolean should_turn_left, should_turn_right, should_move_on;
4716 int rnd = RND(rnd_value);
4718 should_turn_left = (can_turn_left &&
4720 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4721 y + back_dy + left_dy)));
4722 should_turn_right = (can_turn_right &&
4724 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4725 y + back_dy + right_dy)));
4726 should_move_on = (can_move_on &&
4729 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4730 y + move_dy + left_dy) ||
4731 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4732 y + move_dy + right_dy)));
4734 if (should_turn_left || should_turn_right || should_move_on)
4736 if (should_turn_left && should_turn_right && should_move_on)
4737 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4738 rnd < 2 * rnd_value / 3 ? right_dir :
4740 else if (should_turn_left && should_turn_right)
4741 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4742 else if (should_turn_left && should_move_on)
4743 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4744 else if (should_turn_right && should_move_on)
4745 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4746 else if (should_turn_left)
4747 MovDir[x][y] = left_dir;
4748 else if (should_turn_right)
4749 MovDir[x][y] = right_dir;
4750 else if (should_move_on)
4751 MovDir[x][y] = old_move_dir;
4753 else if (can_move_on && rnd > rnd_value / 8)
4754 MovDir[x][y] = old_move_dir;
4755 else if (can_turn_left && can_turn_right)
4756 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4757 else if (can_turn_left && rnd > rnd_value / 8)
4758 MovDir[x][y] = left_dir;
4759 else if (can_turn_right && rnd > rnd_value/8)
4760 MovDir[x][y] = right_dir;
4762 MovDir[x][y] = back_dir;
4764 xx = x + move_xy[MovDir[x][y]].x;
4765 yy = y + move_xy[MovDir[x][y]].y;
4768 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4769 if (!IN_LEV_FIELD(xx, yy) ||
4770 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4771 MovDir[x][y] = old_move_dir;
4773 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4774 MovDir[x][y] = old_move_dir;
4779 else if (element == EL_DRAGON)
4781 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4782 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4783 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4785 int rnd = RND(rnd_value);
4788 if (FrameCounter < 1 && x == 0 && y == 29)
4789 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4792 if (can_move_on && rnd > rnd_value / 8)
4793 MovDir[x][y] = old_move_dir;
4794 else if (can_turn_left && can_turn_right)
4795 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4796 else if (can_turn_left && rnd > rnd_value / 8)
4797 MovDir[x][y] = left_dir;
4798 else if (can_turn_right && rnd > rnd_value / 8)
4799 MovDir[x][y] = right_dir;
4801 MovDir[x][y] = back_dir;
4803 xx = x + move_xy[MovDir[x][y]].x;
4804 yy = y + move_xy[MovDir[x][y]].y;
4807 if (FrameCounter < 1 && x == 0 && y == 29)
4808 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4809 xx, yy, Feld[xx][yy],
4814 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4815 MovDir[x][y] = old_move_dir;
4817 if (!IS_FREE(xx, yy))
4818 MovDir[x][y] = old_move_dir;
4822 if (FrameCounter < 1 && x == 0 && y == 29)
4823 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4828 else if (element == EL_MOLE)
4830 boolean can_move_on =
4831 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4832 IS_AMOEBOID(Feld[move_x][move_y]) ||
4833 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4836 boolean can_turn_left =
4837 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4838 IS_AMOEBOID(Feld[left_x][left_y])));
4840 boolean can_turn_right =
4841 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4842 IS_AMOEBOID(Feld[right_x][right_y])));
4844 if (can_turn_left && can_turn_right)
4845 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4846 else if (can_turn_left)
4847 MovDir[x][y] = left_dir;
4849 MovDir[x][y] = right_dir;
4852 if (MovDir[x][y] != old_move_dir)
4855 else if (element == EL_BALLOON)
4857 MovDir[x][y] = game.balloon_dir;
4860 else if (element == EL_SPRING)
4863 if (MovDir[x][y] & MV_HORIZONTAL &&
4864 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4865 MovDir[x][y] = MV_NO_MOVING;
4867 if (MovDir[x][y] & MV_HORIZONTAL &&
4868 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4869 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4870 MovDir[x][y] = MV_NO_MOVING;
4875 else if (element == EL_ROBOT ||
4876 element == EL_SATELLITE ||
4877 element == EL_PENGUIN)
4879 int attr_x = -1, attr_y = -1;
4890 for (i = 0; i < MAX_PLAYERS; i++)
4892 struct PlayerInfo *player = &stored_player[i];
4893 int jx = player->jx, jy = player->jy;
4895 if (!player->active)
4899 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4908 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4909 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4910 game.engine_version < VERSION_IDENT(3,1,0,0)))
4912 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4919 if (element == EL_PENGUIN)
4922 static int xy[4][2] =
4930 for (i = 0; i < NUM_DIRECTIONS; i++)
4932 int ex = x + xy[i][0];
4933 int ey = y + xy[i][1];
4935 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4944 MovDir[x][y] = MV_NO_MOVING;
4946 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4947 else if (attr_x > x)
4948 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4950 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4951 else if (attr_y > y)
4952 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4954 if (element == EL_ROBOT)
4958 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4959 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4960 Moving2Blocked(x, y, &newx, &newy);
4962 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4963 MovDelay[x][y] = 8 + 8 * !RND(3);
4965 MovDelay[x][y] = 16;
4967 else if (element == EL_PENGUIN)
4973 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4975 boolean first_horiz = RND(2);
4976 int new_move_dir = MovDir[x][y];
4979 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4980 Moving2Blocked(x, y, &newx, &newy);
4982 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4986 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4987 Moving2Blocked(x, y, &newx, &newy);
4989 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4992 MovDir[x][y] = old_move_dir;
4996 else /* (element == EL_SATELLITE) */
5002 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5004 boolean first_horiz = RND(2);
5005 int new_move_dir = MovDir[x][y];
5008 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5009 Moving2Blocked(x, y, &newx, &newy);
5011 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5015 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5016 Moving2Blocked(x, y, &newx, &newy);
5018 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5021 MovDir[x][y] = old_move_dir;
5026 else if (move_pattern == MV_TURNING_LEFT ||
5027 move_pattern == MV_TURNING_RIGHT ||
5028 move_pattern == MV_TURNING_LEFT_RIGHT ||
5029 move_pattern == MV_TURNING_RIGHT_LEFT ||
5030 move_pattern == MV_TURNING_RANDOM ||
5031 move_pattern == MV_ALL_DIRECTIONS)
5033 boolean can_turn_left =
5034 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5035 boolean can_turn_right =
5036 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5038 #if USE_CAN_MOVE_NOT_MOVING
5039 if (element_info[element].move_stepsize == 0) /* not moving */
5043 if (move_pattern == MV_TURNING_LEFT)
5044 MovDir[x][y] = left_dir;
5045 else if (move_pattern == MV_TURNING_RIGHT)
5046 MovDir[x][y] = right_dir;
5047 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5048 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5049 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5050 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5051 else if (move_pattern == MV_TURNING_RANDOM)
5052 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5053 can_turn_right && !can_turn_left ? right_dir :
5054 RND(2) ? left_dir : right_dir);
5055 else if (can_turn_left && can_turn_right)
5056 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5057 else if (can_turn_left)
5058 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5059 else if (can_turn_right)
5060 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5062 MovDir[x][y] = back_dir;
5064 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5066 else if (move_pattern == MV_HORIZONTAL ||
5067 move_pattern == MV_VERTICAL)
5069 if (move_pattern & old_move_dir)
5070 MovDir[x][y] = back_dir;
5071 else if (move_pattern == MV_HORIZONTAL)
5072 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5073 else if (move_pattern == MV_VERTICAL)
5074 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5076 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5078 else if (move_pattern & MV_ANY_DIRECTION)
5080 MovDir[x][y] = move_pattern;
5081 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5083 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5085 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5086 MovDir[x][y] = left_dir;
5087 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5088 MovDir[x][y] = right_dir;
5090 if (MovDir[x][y] != old_move_dir)
5091 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5093 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5095 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5096 MovDir[x][y] = right_dir;
5097 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5098 MovDir[x][y] = left_dir;
5100 if (MovDir[x][y] != old_move_dir)
5101 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5103 else if (move_pattern == MV_TOWARDS_PLAYER ||
5104 move_pattern == MV_AWAY_FROM_PLAYER)
5106 int attr_x = -1, attr_y = -1;
5108 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5119 for (i = 0; i < MAX_PLAYERS; i++)
5121 struct PlayerInfo *player = &stored_player[i];
5122 int jx = player->jx, jy = player->jy;
5124 if (!player->active)
5128 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5136 MovDir[x][y] = MV_NO_MOVING;
5138 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5139 else if (attr_x > x)
5140 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5142 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5143 else if (attr_y > y)
5144 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5146 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5148 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5150 boolean first_horiz = RND(2);
5151 int new_move_dir = MovDir[x][y];
5153 #if USE_CAN_MOVE_NOT_MOVING
5154 if (element_info[element].move_stepsize == 0) /* not moving */
5156 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5157 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5164 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5165 Moving2Blocked(x, y, &newx, &newy);
5167 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5171 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5172 Moving2Blocked(x, y, &newx, &newy);
5174 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5177 MovDir[x][y] = old_move_dir;
5180 else if (move_pattern == MV_WHEN_PUSHED ||
5181 move_pattern == MV_WHEN_DROPPED)
5183 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5184 MovDir[x][y] = MV_NO_MOVING;
5188 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5190 static int test_xy[7][2] =
5200 static int test_dir[7] =
5210 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5211 int move_preference = -1000000; /* start with very low preference */
5212 int new_move_dir = MV_NO_MOVING;
5213 int start_test = RND(4);
5216 for (i = 0; i < NUM_DIRECTIONS; i++)
5218 int move_dir = test_dir[start_test + i];
5219 int move_dir_preference;
5221 xx = x + test_xy[start_test + i][0];
5222 yy = y + test_xy[start_test + i][1];
5224 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5225 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5227 new_move_dir = move_dir;
5232 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5235 move_dir_preference = -1 * RunnerVisit[xx][yy];
5236 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5237 move_dir_preference = PlayerVisit[xx][yy];
5239 if (move_dir_preference > move_preference)
5241 /* prefer field that has not been visited for the longest time */
5242 move_preference = move_dir_preference;
5243 new_move_dir = move_dir;
5245 else if (move_dir_preference == move_preference &&
5246 move_dir == old_move_dir)
5248 /* prefer last direction when all directions are preferred equally */
5249 move_preference = move_dir_preference;
5250 new_move_dir = move_dir;
5254 MovDir[x][y] = new_move_dir;
5255 if (old_move_dir != new_move_dir)
5258 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5266 static void TurnRound(int x, int y)
5268 int direction = MovDir[x][y];
5271 GfxDir[x][y] = MovDir[x][y];
5277 GfxDir[x][y] = MovDir[x][y];
5280 if (direction != MovDir[x][y])
5285 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5288 GfxAction[x][y] = ACTION_WAITING;
5292 static boolean JustBeingPushed(int x, int y)
5296 for (i = 0; i < MAX_PLAYERS; i++)
5298 struct PlayerInfo *player = &stored_player[i];
5300 if (player->active && player->is_pushing && player->MovPos)
5302 int next_jx = player->jx + (player->jx - player->last_jx);
5303 int next_jy = player->jy + (player->jy - player->last_jy);
5305 if (x == next_jx && y == next_jy)
5313 void StartMoving(int x, int y)
5316 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5318 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5319 int element = Feld[x][y];
5325 if (MovDelay[x][y] == 0)
5326 GfxAction[x][y] = ACTION_DEFAULT;
5328 /* !!! this should be handled more generic (not only for mole) !!! */
5329 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5330 GfxAction[x][y] = ACTION_DEFAULT;
5333 if (CAN_FALL(element) && y < lev_fieldy - 1)
5335 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5336 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5337 if (JustBeingPushed(x, y))
5340 if (element == EL_QUICKSAND_FULL)
5342 if (IS_FREE(x, y + 1))
5344 InitMovingField(x, y, MV_DOWN);
5345 started_moving = TRUE;
5347 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5348 Store[x][y] = EL_ROCK;
5350 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5352 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5355 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5357 if (!MovDelay[x][y])
5358 MovDelay[x][y] = TILEY + 1;
5367 Feld[x][y] = EL_QUICKSAND_EMPTY;
5368 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5369 Store[x][y + 1] = Store[x][y];
5372 PlayLevelSoundAction(x, y, ACTION_FILLING);
5374 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5378 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5379 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5381 InitMovingField(x, y, MV_DOWN);
5382 started_moving = TRUE;
5384 Feld[x][y] = EL_QUICKSAND_FILLING;
5385 Store[x][y] = element;
5387 PlayLevelSoundAction(x, y, ACTION_FILLING);
5389 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5392 else if (element == EL_MAGIC_WALL_FULL)
5394 if (IS_FREE(x, y + 1))
5396 InitMovingField(x, y, MV_DOWN);
5397 started_moving = TRUE;
5399 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5400 Store[x][y] = EL_CHANGED(Store[x][y]);
5402 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5404 if (!MovDelay[x][y])
5405 MovDelay[x][y] = TILEY/4 + 1;
5414 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5415 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5416 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5420 else if (element == EL_BD_MAGIC_WALL_FULL)
5422 if (IS_FREE(x, y + 1))
5424 InitMovingField(x, y, MV_DOWN);
5425 started_moving = TRUE;
5427 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5428 Store[x][y] = EL_CHANGED2(Store[x][y]);
5430 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5432 if (!MovDelay[x][y])
5433 MovDelay[x][y] = TILEY/4 + 1;
5442 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5443 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5444 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5448 else if (CAN_PASS_MAGIC_WALL(element) &&
5449 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5450 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5452 InitMovingField(x, y, MV_DOWN);
5453 started_moving = TRUE;
5456 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5457 EL_BD_MAGIC_WALL_FILLING);
5458 Store[x][y] = element;
5461 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5463 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5466 SplashAcid(x, y + 1);
5468 InitMovingField(x, y, MV_DOWN);
5469 started_moving = TRUE;
5471 Store[x][y] = EL_ACID;
5473 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5474 GfxAction[x][y + 1] = ACTION_ACTIVE;
5478 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5479 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5481 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5482 CAN_SMASH(element) && WasJustFalling[x][y] &&
5483 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5485 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5486 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5487 (Feld[x][y + 1] == EL_BLOCKED)))
5491 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5492 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5493 WasJustMoving[x][y] && !Pushed[x][y + 1])
5495 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5496 WasJustMoving[x][y])
5501 /* this is needed for a special case not covered by calling "Impact()"
5502 from "ContinueMoving()": if an element moves to a tile directly below
5503 another element which was just falling on that tile (which was empty
5504 in the previous frame), the falling element above would just stop
5505 instead of smashing the element below (in previous version, the above
5506 element was just checked for "moving" instead of "falling", resulting
5507 in incorrect smashes caused by horizontal movement of the above
5508 element; also, the case of the player being the element to smash was
5509 simply not covered here... :-/ ) */
5512 WasJustMoving[x][y] = 0;
5513 WasJustFalling[x][y] = 0;
5516 CheckCollision[x][y] = 0;
5519 if (IS_PLAYER(x, y + 1))
5520 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5525 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5527 if (MovDir[x][y] == MV_NO_MOVING)
5529 InitMovingField(x, y, MV_DOWN);
5530 started_moving = TRUE;
5533 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5535 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5536 MovDir[x][y] = MV_DOWN;
5538 InitMovingField(x, y, MV_DOWN);
5539 started_moving = TRUE;
5541 else if (element == EL_AMOEBA_DROP)
5543 Feld[x][y] = EL_AMOEBA_GROWING;
5544 Store[x][y] = EL_AMOEBA_WET;
5546 /* Store[x][y + 1] must be zero, because:
5547 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5550 #if OLD_GAME_BEHAVIOUR
5551 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5553 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5554 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5555 element != EL_DX_SUPABOMB)
5558 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5559 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5560 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5561 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5564 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5565 (IS_FREE(x - 1, y + 1) ||
5566 Feld[x - 1][y + 1] == EL_ACID));
5567 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5568 (IS_FREE(x + 1, y + 1) ||
5569 Feld[x + 1][y + 1] == EL_ACID));
5570 boolean can_fall_any = (can_fall_left || can_fall_right);
5571 boolean can_fall_both = (can_fall_left && can_fall_right);
5573 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5575 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5577 if (slippery_type == SLIPPERY_ONLY_LEFT)
5578 can_fall_right = FALSE;
5579 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5580 can_fall_left = FALSE;
5581 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5582 can_fall_right = FALSE;
5583 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5584 can_fall_left = FALSE;
5586 can_fall_any = (can_fall_left || can_fall_right);
5587 can_fall_both = (can_fall_left && can_fall_right);
5590 #if USE_NEW_SP_SLIPPERY
5591 /* !!! better use the same properties as for custom elements here !!! */
5592 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5593 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5595 can_fall_right = FALSE; /* slip down on left side */
5596 can_fall_both = FALSE;
5603 if (game.emulation == EMU_BOULDERDASH ||
5604 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5605 can_fall_right = FALSE; /* slip down on left side */
5607 can_fall_left = !(can_fall_right = RND(2));
5609 can_fall_both = FALSE;
5616 if (can_fall_both &&
5617 (game.emulation != EMU_BOULDERDASH &&
5618 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5619 can_fall_left = !(can_fall_right = RND(2));
5622 /* if not determined otherwise, prefer left side for slipping down */
5623 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5624 started_moving = TRUE;
5628 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5630 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5633 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5634 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5635 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5636 int belt_dir = game.belt_dir[belt_nr];
5638 if ((belt_dir == MV_LEFT && left_is_free) ||
5639 (belt_dir == MV_RIGHT && right_is_free))
5642 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5645 InitMovingField(x, y, belt_dir);
5646 started_moving = TRUE;
5649 Pushed[x][y] = TRUE;
5650 Pushed[nextx][y] = TRUE;
5653 GfxAction[x][y] = ACTION_DEFAULT;
5657 MovDir[x][y] = 0; /* if element was moving, stop it */
5662 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5664 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5666 if (CAN_MOVE(element) && !started_moving)
5669 int move_pattern = element_info[element].move_pattern;
5674 if (MovDir[x][y] == MV_NO_MOVING)
5676 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5677 x, y, element, element_info[element].token_name);
5678 printf("StartMoving(): This should never happen!\n");
5683 Moving2Blocked(x, y, &newx, &newy);
5686 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5689 if ((element == EL_SATELLITE ||
5690 element == EL_BALLOON ||
5691 element == EL_SPRING)
5692 && JustBeingPushed(x, y))
5699 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5700 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5702 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5703 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5704 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5708 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5709 element, element_info[element].token_name,
5710 WasJustMoving[x][y],
5711 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5712 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5713 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5714 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5718 WasJustMoving[x][y] = 0;
5721 CheckCollision[x][y] = 0;
5723 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5726 if (Feld[x][y] != element) /* element has changed */
5728 element = Feld[x][y];
5729 move_pattern = element_info[element].move_pattern;
5731 if (!CAN_MOVE(element))
5735 if (Feld[x][y] != element) /* element has changed */
5743 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5744 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5746 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5748 Moving2Blocked(x, y, &newx, &newy);
5749 if (Feld[newx][newy] == EL_BLOCKED)
5750 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5756 if (FrameCounter < 1 && x == 0 && y == 29)
5757 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5760 if (!MovDelay[x][y]) /* start new movement phase */
5762 /* all objects that can change their move direction after each step
5763 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5765 if (element != EL_YAMYAM &&
5766 element != EL_DARK_YAMYAM &&
5767 element != EL_PACMAN &&
5768 !(move_pattern & MV_ANY_DIRECTION) &&
5769 move_pattern != MV_TURNING_LEFT &&
5770 move_pattern != MV_TURNING_RIGHT &&
5771 move_pattern != MV_TURNING_LEFT_RIGHT &&
5772 move_pattern != MV_TURNING_RIGHT_LEFT &&
5773 move_pattern != MV_TURNING_RANDOM)
5778 if (FrameCounter < 1 && x == 0 && y == 29)
5779 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5782 if (MovDelay[x][y] && (element == EL_BUG ||
5783 element == EL_SPACESHIP ||
5784 element == EL_SP_SNIKSNAK ||
5785 element == EL_SP_ELECTRON ||
5786 element == EL_MOLE))
5787 DrawLevelField(x, y);
5791 if (MovDelay[x][y]) /* wait some time before next movement */
5796 if (element == EL_YAMYAM)
5799 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5800 DrawLevelElementAnimation(x, y, element);
5804 if (MovDelay[x][y]) /* element still has to wait some time */
5807 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5808 ResetGfxAnimation(x, y);
5812 if (GfxAction[x][y] != ACTION_WAITING)
5813 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5815 GfxAction[x][y] = ACTION_WAITING;
5819 if (element == EL_ROBOT ||
5821 element == EL_PACMAN ||
5823 element == EL_YAMYAM ||
5824 element == EL_DARK_YAMYAM)
5827 DrawLevelElementAnimation(x, y, element);
5829 DrawLevelElementAnimationIfNeeded(x, y, element);
5831 PlayLevelSoundAction(x, y, ACTION_WAITING);
5833 else if (element == EL_SP_ELECTRON)
5834 DrawLevelElementAnimationIfNeeded(x, y, element);
5835 else if (element == EL_DRAGON)
5838 int dir = MovDir[x][y];
5839 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5840 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5841 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5842 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5843 dir == MV_UP ? IMG_FLAMES_1_UP :
5844 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5845 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5848 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5851 GfxAction[x][y] = ACTION_ATTACKING;
5853 if (IS_PLAYER(x, y))
5854 DrawPlayerField(x, y);
5856 DrawLevelField(x, y);
5858 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5860 for (i = 1; i <= 3; i++)
5862 int xx = x + i * dx;
5863 int yy = y + i * dy;
5864 int sx = SCREENX(xx);
5865 int sy = SCREENY(yy);
5866 int flame_graphic = graphic + (i - 1);
5868 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5873 int flamed = MovingOrBlocked2Element(xx, yy);
5877 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5879 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5880 RemoveMovingField(xx, yy);
5882 RemoveField(xx, yy);
5884 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5887 RemoveMovingField(xx, yy);
5891 if (ChangeDelay[xx][yy])
5892 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5893 Feld[xx][yy] == EL_BLOCKED));
5897 ChangeDelay[xx][yy] = 0;
5899 Feld[xx][yy] = EL_FLAMES;
5900 if (IN_SCR_FIELD(sx, sy))
5902 DrawLevelFieldCrumbledSand(xx, yy);
5903 DrawGraphic(sx, sy, flame_graphic, frame);
5908 if (Feld[xx][yy] == EL_FLAMES)
5909 Feld[xx][yy] = EL_EMPTY;
5910 DrawLevelField(xx, yy);
5915 if (MovDelay[x][y]) /* element still has to wait some time */
5917 PlayLevelSoundAction(x, y, ACTION_WAITING);
5923 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5924 for all other elements GfxAction will be set by InitMovingField() */
5925 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5926 GfxAction[x][y] = ACTION_MOVING;
5930 /* now make next step */
5932 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5934 if (DONT_COLLIDE_WITH(element) &&
5935 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5936 !PLAYER_ENEMY_PROTECTED(newx, newy))
5939 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5943 /* player killed by element which is deadly when colliding with */
5945 KillHero(PLAYERINFO(newx, newy));
5952 else if (CAN_MOVE_INTO_ACID(element) &&
5953 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5954 (MovDir[x][y] == MV_DOWN ||
5955 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5957 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5958 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5962 else if ((element == EL_PENGUIN ||
5963 element == EL_ROBOT ||
5964 element == EL_SATELLITE ||
5965 element == EL_BALLOON ||
5966 IS_CUSTOM_ELEMENT(element)) &&
5967 IN_LEV_FIELD(newx, newy) &&
5968 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5971 SplashAcid(newx, newy);
5972 Store[x][y] = EL_ACID;
5974 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5976 if (Feld[newx][newy] == EL_EXIT_OPEN)
5980 DrawLevelField(x, y);
5982 Feld[x][y] = EL_EMPTY;
5983 DrawLevelField(x, y);
5986 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5987 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5988 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5990 local_player->friends_still_needed--;
5991 if (!local_player->friends_still_needed &&
5992 !local_player->GameOver && AllPlayersGone)
5993 local_player->LevelSolved = local_player->GameOver = TRUE;
5997 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5999 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6000 DrawLevelField(newx, newy);
6002 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6004 else if (!IS_FREE(newx, newy))
6006 GfxAction[x][y] = ACTION_WAITING;
6008 if (IS_PLAYER(x, y))
6009 DrawPlayerField(x, y);
6011 DrawLevelField(x, y);
6016 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6018 if (IS_FOOD_PIG(Feld[newx][newy]))
6020 if (IS_MOVING(newx, newy))
6021 RemoveMovingField(newx, newy);
6024 Feld[newx][newy] = EL_EMPTY;
6025 DrawLevelField(newx, newy);
6028 PlayLevelSound(x, y, SND_PIG_DIGGING);
6030 else if (!IS_FREE(newx, newy))
6032 if (IS_PLAYER(x, y))
6033 DrawPlayerField(x, y);
6035 DrawLevelField(x, y);
6044 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6047 else if (IS_CUSTOM_ELEMENT(element) &&
6048 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6052 !IS_FREE(newx, newy)
6057 int new_element = Feld[newx][newy];
6060 printf("::: '%s' digs '%s' [%d]\n",
6061 element_info[element].token_name,
6062 element_info[Feld[newx][newy]].token_name,
6063 StorePlayer[newx][newy]);
6066 if (!IS_FREE(newx, newy))
6068 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6069 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6072 /* no element can dig solid indestructible elements */
6073 if (IS_INDESTRUCTIBLE(new_element) &&
6074 !IS_DIGGABLE(new_element) &&
6075 !IS_COLLECTIBLE(new_element))
6078 if (AmoebaNr[newx][newy] &&
6079 (new_element == EL_AMOEBA_FULL ||
6080 new_element == EL_BD_AMOEBA ||
6081 new_element == EL_AMOEBA_GROWING))
6083 AmoebaCnt[AmoebaNr[newx][newy]]--;
6084 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6087 if (IS_MOVING(newx, newy))
6088 RemoveMovingField(newx, newy);
6091 RemoveField(newx, newy);
6092 DrawLevelField(newx, newy);
6095 /* if digged element was about to explode, prevent the explosion */
6096 ExplodeField[newx][newy] = EX_TYPE_NONE;
6098 PlayLevelSoundAction(x, y, action);
6103 Store[newx][newy] = EL_EMPTY;
6104 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6105 Store[newx][newy] = element_info[element].move_leave_element;
6107 Store[newx][newy] = EL_EMPTY;
6108 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6109 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6110 Store[newx][newy] = element_info[element].move_leave_element;
6113 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6114 element_info[element].can_leave_element = TRUE;
6117 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6119 RunnerVisit[x][y] = FrameCounter;
6120 PlayerVisit[x][y] /= 8; /* expire player visit path */
6126 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6128 if (!IS_FREE(newx, newy))
6130 if (IS_PLAYER(x, y))
6131 DrawPlayerField(x, y);
6133 DrawLevelField(x, y);
6139 boolean wanna_flame = !RND(10);
6140 int dx = newx - x, dy = newy - y;
6141 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6142 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6143 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6144 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6145 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6146 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6149 IS_CLASSIC_ENEMY(element1) ||
6150 IS_CLASSIC_ENEMY(element2)) &&
6151 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6152 element1 != EL_FLAMES && element2 != EL_FLAMES)
6155 ResetGfxAnimation(x, y);
6156 GfxAction[x][y] = ACTION_ATTACKING;
6159 if (IS_PLAYER(x, y))
6160 DrawPlayerField(x, y);
6162 DrawLevelField(x, y);
6164 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6166 MovDelay[x][y] = 50;
6170 RemoveField(newx, newy);
6172 Feld[newx][newy] = EL_FLAMES;
6173 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6176 RemoveField(newx1, newy1);
6178 Feld[newx1][newy1] = EL_FLAMES;
6180 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6183 RemoveField(newx2, newy2);
6185 Feld[newx2][newy2] = EL_FLAMES;
6192 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6193 Feld[newx][newy] == EL_DIAMOND)
6195 if (IS_MOVING(newx, newy))
6196 RemoveMovingField(newx, newy);
6199 Feld[newx][newy] = EL_EMPTY;
6200 DrawLevelField(newx, newy);
6203 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6205 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6206 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6208 if (AmoebaNr[newx][newy])
6210 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6211 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6212 Feld[newx][newy] == EL_BD_AMOEBA)
6213 AmoebaCnt[AmoebaNr[newx][newy]]--;
6218 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6220 if (IS_MOVING(newx, newy))
6223 RemoveMovingField(newx, newy);
6227 Feld[newx][newy] = EL_EMPTY;
6228 DrawLevelField(newx, newy);
6231 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6233 else if ((element == EL_PACMAN || element == EL_MOLE)
6234 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6236 if (AmoebaNr[newx][newy])
6238 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6239 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6240 Feld[newx][newy] == EL_BD_AMOEBA)
6241 AmoebaCnt[AmoebaNr[newx][newy]]--;
6244 if (element == EL_MOLE)
6246 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6247 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6249 ResetGfxAnimation(x, y);
6250 GfxAction[x][y] = ACTION_DIGGING;
6251 DrawLevelField(x, y);
6253 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6255 return; /* wait for shrinking amoeba */
6257 else /* element == EL_PACMAN */
6259 Feld[newx][newy] = EL_EMPTY;
6260 DrawLevelField(newx, newy);
6261 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6264 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6265 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6266 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6268 /* wait for shrinking amoeba to completely disappear */
6271 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6273 /* object was running against a wall */
6278 if (move_pattern & MV_ANY_DIRECTION &&
6279 move_pattern == MovDir[x][y])
6281 int blocking_element =
6282 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6285 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6286 element_info[element].token_name,
6287 element_info[blocking_element].token_name,
6291 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6294 element = Feld[x][y]; /* element might have changed */
6299 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6300 DrawLevelElementAnimation(x, y, element);
6302 if (element == EL_BUG ||
6303 element == EL_SPACESHIP ||
6304 element == EL_SP_SNIKSNAK)
6305 DrawLevelField(x, y);
6306 else if (element == EL_MOLE)
6307 DrawLevelField(x, y);
6308 else if (element == EL_BD_BUTTERFLY ||
6309 element == EL_BD_FIREFLY)
6310 DrawLevelElementAnimationIfNeeded(x, y, element);
6311 else if (element == EL_SATELLITE)
6312 DrawLevelElementAnimationIfNeeded(x, y, element);
6313 else if (element == EL_SP_ELECTRON)
6314 DrawLevelElementAnimationIfNeeded(x, y, element);
6317 if (DONT_TOUCH(element))
6318 TestIfBadThingTouchesHero(x, y);
6321 PlayLevelSoundAction(x, y, ACTION_WAITING);
6327 InitMovingField(x, y, MovDir[x][y]);
6329 PlayLevelSoundAction(x, y, ACTION_MOVING);
6333 ContinueMoving(x, y);
6336 void ContinueMoving(int x, int y)
6338 int element = Feld[x][y];
6339 int stored = Store[x][y];
6340 struct ElementInfo *ei = &element_info[element];
6341 int direction = MovDir[x][y];
6342 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6343 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6344 int newx = x + dx, newy = y + dy;
6346 int nextx = newx + dx, nexty = newy + dy;
6349 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6350 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6352 boolean pushed_by_player = Pushed[x][y];
6355 MovPos[x][y] += getElementMoveStepsize(x, y);
6358 if (pushed_by_player && IS_PLAYER(x, y))
6360 /* special case: moving object pushed by player */
6361 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6364 if (pushed_by_player) /* special case: moving object pushed by player */
6365 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6368 if (ABS(MovPos[x][y]) < TILEX)
6370 DrawLevelField(x, y);
6372 return; /* element is still moving */
6375 /* element reached destination field */
6377 Feld[x][y] = EL_EMPTY;
6378 Feld[newx][newy] = element;
6379 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6382 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6384 element = Feld[newx][newy] = EL_ACID;
6387 else if (element == EL_MOLE)
6389 Feld[x][y] = EL_SAND;
6391 DrawLevelFieldCrumbledSandNeighbours(x, y);
6393 else if (element == EL_QUICKSAND_FILLING)
6395 element = Feld[newx][newy] = get_next_element(element);
6396 Store[newx][newy] = Store[x][y];
6398 else if (element == EL_QUICKSAND_EMPTYING)
6400 Feld[x][y] = get_next_element(element);
6401 element = Feld[newx][newy] = Store[x][y];
6403 else if (element == EL_MAGIC_WALL_FILLING)
6405 element = Feld[newx][newy] = get_next_element(element);
6406 if (!game.magic_wall_active)
6407 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6408 Store[newx][newy] = Store[x][y];
6410 else if (element == EL_MAGIC_WALL_EMPTYING)
6412 Feld[x][y] = get_next_element(element);
6413 if (!game.magic_wall_active)
6414 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6415 element = Feld[newx][newy] = Store[x][y];
6417 else if (element == EL_BD_MAGIC_WALL_FILLING)
6419 element = Feld[newx][newy] = get_next_element(element);
6420 if (!game.magic_wall_active)
6421 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6422 Store[newx][newy] = Store[x][y];
6424 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6426 Feld[x][y] = get_next_element(element);
6427 if (!game.magic_wall_active)
6428 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6429 element = Feld[newx][newy] = Store[x][y];
6431 else if (element == EL_AMOEBA_DROPPING)
6433 Feld[x][y] = get_next_element(element);
6434 element = Feld[newx][newy] = Store[x][y];
6436 else if (element == EL_SOKOBAN_OBJECT)
6439 Feld[x][y] = Back[x][y];
6441 if (Back[newx][newy])
6442 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6444 Back[x][y] = Back[newx][newy] = 0;
6447 else if (Store[x][y] == EL_ACID)
6449 element = Feld[newx][newy] = EL_ACID;
6453 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6454 ei->move_leave_element != EL_EMPTY &&
6455 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6456 Store[x][y] != EL_EMPTY))
6458 /* some elements can leave other elements behind after moving */
6460 Feld[x][y] = ei->move_leave_element;
6461 InitField(x, y, FALSE);
6463 if (GFX_CRUMBLED(Feld[x][y]))
6464 DrawLevelFieldCrumbledSandNeighbours(x, y);
6468 Store[x][y] = EL_EMPTY;
6472 MovDelay[newx][newy] = 0;
6474 if (CAN_CHANGE(element))
6476 /* copy element change control values to new field */
6477 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6478 ChangePage[newx][newy] = ChangePage[x][y];
6479 Changed[newx][newy] = Changed[x][y];
6480 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6483 ChangeDelay[x][y] = 0;
6484 ChangePage[x][y] = -1;
6485 Changed[x][y] = CE_BITMASK_DEFAULT;
6486 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6488 /* copy animation control values to new field */
6489 GfxFrame[newx][newy] = GfxFrame[x][y];
6490 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6491 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6492 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6494 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6497 /* do this after checking for left-behind element */
6498 ResetGfxAnimation(x, y); /* reset animation values for old field */
6502 /* some elements can leave other elements behind after moving */
6504 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6505 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6506 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6508 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6509 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6513 int move_leave_element = ei->move_leave_element;
6515 Feld[x][y] = move_leave_element;
6517 #if USE_PREVIOUS_MOVE_DIR
6518 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6519 MovDir[x][y] = direction;
6522 InitField(x, y, FALSE);
6524 if (GFX_CRUMBLED(Feld[x][y]))
6525 DrawLevelFieldCrumbledSandNeighbours(x, y);
6527 if (ELEM_IS_PLAYER(move_leave_element))
6528 RelocatePlayer(x, y, move_leave_element);
6533 /* some elements can leave other elements behind after moving */
6534 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6535 ei->move_leave_element != EL_EMPTY &&
6536 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6537 ei->can_leave_element_last))
6539 Feld[x][y] = ei->move_leave_element;
6540 InitField(x, y, FALSE);
6542 if (GFX_CRUMBLED(Feld[x][y]))
6543 DrawLevelFieldCrumbledSandNeighbours(x, y);
6546 ei->can_leave_element_last = ei->can_leave_element;
6547 ei->can_leave_element = FALSE;
6551 /* do this after checking for left-behind element */
6552 ResetGfxAnimation(x, y); /* reset animation values for old field */
6556 /* 2.1.1 (does not work correctly for spring) */
6557 if (!CAN_MOVE(element))
6558 MovDir[newx][newy] = 0;
6562 /* (does not work for falling objects that slide horizontally) */
6563 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6564 MovDir[newx][newy] = 0;
6567 if (!CAN_MOVE(element) ||
6568 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6569 MovDir[newx][newy] = 0;
6573 if (!CAN_MOVE(element) ||
6574 (CAN_FALL(element) && direction == MV_DOWN))
6575 GfxDir[x][y] = MovDir[newx][newy] = 0;
6577 if (!CAN_MOVE(element) ||
6578 (CAN_FALL(element) && direction == MV_DOWN &&
6579 (element == EL_SPRING ||
6580 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6581 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6582 GfxDir[x][y] = MovDir[newx][newy] = 0;
6588 DrawLevelField(x, y);
6589 DrawLevelField(newx, newy);
6591 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6593 /* prevent pushed element from moving on in pushed direction */
6594 if (pushed_by_player && CAN_MOVE(element) &&
6595 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6596 !(element_info[element].move_pattern & direction))
6597 TurnRound(newx, newy);
6600 /* prevent elements on conveyor belt from moving on in last direction */
6601 if (pushed_by_conveyor && CAN_FALL(element) &&
6602 direction & MV_HORIZONTAL)
6605 if (CAN_MOVE(element))
6606 InitMovDir(newx, newy);
6608 MovDir[newx][newy] = 0;
6610 MovDir[newx][newy] = 0;
6615 if (!pushed_by_player)
6617 int nextx = newx + dx, nexty = newy + dy;
6618 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6620 WasJustMoving[newx][newy] = 3;
6622 if (CAN_FALL(element) && direction == MV_DOWN)
6623 WasJustFalling[newx][newy] = 3;
6625 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6626 CheckCollision[newx][newy] = 2;
6629 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6631 TestIfBadThingTouchesHero(newx, newy);
6632 TestIfBadThingTouchesFriend(newx, newy);
6634 if (!IS_CUSTOM_ELEMENT(element))
6635 TestIfBadThingTouchesOtherBadThing(newx, newy);
6637 else if (element == EL_PENGUIN)
6638 TestIfFriendTouchesBadThing(newx, newy);
6640 #if USE_NEW_MOVE_STYLE
6642 if (CAN_FALL(element) && direction == MV_DOWN &&
6643 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6644 IS_PLAYER(x, newy + 1))
6645 printf("::: we would now kill the player [%d]\n", FrameCounter);
6648 /* give the player one last chance (one more frame) to move away */
6649 if (CAN_FALL(element) && direction == MV_DOWN &&
6650 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6651 (!IS_PLAYER(x, newy + 1) ||
6652 game.engine_version < VERSION_IDENT(3,1,1,0)))
6655 if (CAN_FALL(element) && direction == MV_DOWN &&
6656 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6664 if (pushed_by_player && !game.use_bug_change_when_pushing)
6666 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6669 if (pushed_by_player)
6674 int dig_side = MV_DIR_OPPOSITE(direction);
6676 static int trigger_sides[4] =
6678 CH_SIDE_RIGHT, /* moving left */
6679 CH_SIDE_LEFT, /* moving right */
6680 CH_SIDE_BOTTOM, /* moving up */
6681 CH_SIDE_TOP, /* moving down */
6683 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6685 struct PlayerInfo *player = PLAYERINFO(x, y);
6687 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6688 player->index_bit, dig_side);
6689 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6690 player->index_bit, dig_side);
6695 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6699 if (ChangePage[newx][newy] != -1) /* delayed change */
6700 ChangeElement(newx, newy, ChangePage[newx][newy]);
6705 TestIfElementHitsCustomElement(newx, newy, direction);
6709 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6711 int hitting_element = Feld[newx][newy];
6713 /* !!! fix side (direction) orientation here and elsewhere !!! */
6714 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6718 if (IN_LEV_FIELD(nextx, nexty))
6720 int opposite_direction = MV_DIR_OPPOSITE(direction);
6721 int hitting_side = direction;
6722 int touched_side = opposite_direction;
6723 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6724 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6725 MovDir[nextx][nexty] != direction ||
6726 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6732 CheckElementChangeBySide(nextx, nexty, touched_element,
6733 CE_HIT_BY_SOMETHING, opposite_direction);
6735 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6736 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6738 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6740 struct ElementChangeInfo *change =
6741 &element_info[hitting_element].change_page[i];
6743 if (change->can_change &&
6744 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6745 change->trigger_side & touched_side &&
6746 change->trigger_element == touched_element)
6748 CheckElementChangeByPage(newx, newy, hitting_element,
6749 touched_element, CE_OTHER_IS_HITTING,i);
6755 if (IS_CUSTOM_ELEMENT(touched_element) &&
6756 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6758 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6760 struct ElementChangeInfo *change =
6761 &element_info[touched_element].change_page[i];
6763 if (change->can_change &&
6764 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6765 change->trigger_side & hitting_side &&
6766 change->trigger_element == hitting_element)
6768 CheckElementChangeByPage(nextx, nexty, touched_element,
6769 hitting_element, CE_OTHER_GETS_HIT, i);
6780 TestIfPlayerTouchesCustomElement(newx, newy);
6781 TestIfElementTouchesCustomElement(newx, newy);
6784 int AmoebeNachbarNr(int ax, int ay)
6787 int element = Feld[ax][ay];
6789 static int xy[4][2] =
6797 for (i = 0; i < NUM_DIRECTIONS; i++)
6799 int x = ax + xy[i][0];
6800 int y = ay + xy[i][1];
6802 if (!IN_LEV_FIELD(x, y))
6805 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6806 group_nr = AmoebaNr[x][y];
6812 void AmoebenVereinigen(int ax, int ay)
6814 int i, x, y, xx, yy;
6815 int new_group_nr = AmoebaNr[ax][ay];
6816 static int xy[4][2] =
6824 if (new_group_nr == 0)
6827 for (i = 0; i < NUM_DIRECTIONS; i++)
6832 if (!IN_LEV_FIELD(x, y))
6835 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6836 Feld[x][y] == EL_BD_AMOEBA ||
6837 Feld[x][y] == EL_AMOEBA_DEAD) &&
6838 AmoebaNr[x][y] != new_group_nr)
6840 int old_group_nr = AmoebaNr[x][y];
6842 if (old_group_nr == 0)
6845 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6846 AmoebaCnt[old_group_nr] = 0;
6847 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6848 AmoebaCnt2[old_group_nr] = 0;
6850 for (yy = 0; yy < lev_fieldy; yy++)
6852 for (xx = 0; xx < lev_fieldx; xx++)
6854 if (AmoebaNr[xx][yy] == old_group_nr)
6855 AmoebaNr[xx][yy] = new_group_nr;
6862 void AmoebeUmwandeln(int ax, int ay)
6866 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6868 int group_nr = AmoebaNr[ax][ay];
6873 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6874 printf("AmoebeUmwandeln(): This should never happen!\n");
6879 for (y = 0; y < lev_fieldy; y++)
6881 for (x = 0; x < lev_fieldx; x++)
6883 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6886 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6890 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6891 SND_AMOEBA_TURNING_TO_GEM :
6892 SND_AMOEBA_TURNING_TO_ROCK));
6897 static int xy[4][2] =
6905 for (i = 0; i < NUM_DIRECTIONS; i++)
6910 if (!IN_LEV_FIELD(x, y))
6913 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6915 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6916 SND_AMOEBA_TURNING_TO_GEM :
6917 SND_AMOEBA_TURNING_TO_ROCK));
6924 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6927 int group_nr = AmoebaNr[ax][ay];
6928 boolean done = FALSE;
6933 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6934 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6939 for (y = 0; y < lev_fieldy; y++)
6941 for (x = 0; x < lev_fieldx; x++)
6943 if (AmoebaNr[x][y] == group_nr &&
6944 (Feld[x][y] == EL_AMOEBA_DEAD ||
6945 Feld[x][y] == EL_BD_AMOEBA ||
6946 Feld[x][y] == EL_AMOEBA_GROWING))
6949 Feld[x][y] = new_element;
6950 InitField(x, y, FALSE);
6951 DrawLevelField(x, y);
6958 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6959 SND_BD_AMOEBA_TURNING_TO_ROCK :
6960 SND_BD_AMOEBA_TURNING_TO_GEM));
6963 void AmoebeWaechst(int x, int y)
6965 static unsigned long sound_delay = 0;
6966 static unsigned long sound_delay_value = 0;
6968 if (!MovDelay[x][y]) /* start new growing cycle */
6972 if (DelayReached(&sound_delay, sound_delay_value))
6975 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6977 if (Store[x][y] == EL_BD_AMOEBA)
6978 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6980 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6982 sound_delay_value = 30;
6986 if (MovDelay[x][y]) /* wait some time before growing bigger */
6989 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6991 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6992 6 - MovDelay[x][y]);
6994 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6997 if (!MovDelay[x][y])
6999 Feld[x][y] = Store[x][y];
7001 DrawLevelField(x, y);
7006 void AmoebaDisappearing(int x, int y)
7008 static unsigned long sound_delay = 0;
7009 static unsigned long sound_delay_value = 0;
7011 if (!MovDelay[x][y]) /* start new shrinking cycle */
7015 if (DelayReached(&sound_delay, sound_delay_value))
7016 sound_delay_value = 30;
7019 if (MovDelay[x][y]) /* wait some time before shrinking */
7022 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7024 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7025 6 - MovDelay[x][y]);
7027 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7030 if (!MovDelay[x][y])
7032 Feld[x][y] = EL_EMPTY;
7033 DrawLevelField(x, y);
7035 /* don't let mole enter this field in this cycle;
7036 (give priority to objects falling to this field from above) */
7042 void AmoebeAbleger(int ax, int ay)
7045 int element = Feld[ax][ay];
7046 int graphic = el2img(element);
7047 int newax = ax, neway = ay;
7048 static int xy[4][2] =
7056 if (!level.amoeba_speed)
7058 Feld[ax][ay] = EL_AMOEBA_DEAD;
7059 DrawLevelField(ax, ay);
7063 if (IS_ANIMATED(graphic))
7064 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7066 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7067 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7069 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7072 if (MovDelay[ax][ay])
7076 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7079 int x = ax + xy[start][0];
7080 int y = ay + xy[start][1];
7082 if (!IN_LEV_FIELD(x, y))
7086 if (IS_FREE(x, y) ||
7087 CAN_GROW_INTO(Feld[x][y]) ||
7088 Feld[x][y] == EL_QUICKSAND_EMPTY)
7094 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7095 if (IS_FREE(x, y) ||
7096 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7103 if (newax == ax && neway == ay)
7106 else /* normal or "filled" (BD style) amoeba */
7109 boolean waiting_for_player = FALSE;
7111 for (i = 0; i < NUM_DIRECTIONS; i++)
7113 int j = (start + i) % 4;
7114 int x = ax + xy[j][0];
7115 int y = ay + xy[j][1];
7117 if (!IN_LEV_FIELD(x, y))
7121 if (IS_FREE(x, y) ||
7122 CAN_GROW_INTO(Feld[x][y]) ||
7123 Feld[x][y] == EL_QUICKSAND_EMPTY)
7130 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7131 if (IS_FREE(x, y) ||
7132 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7139 else if (IS_PLAYER(x, y))
7140 waiting_for_player = TRUE;
7143 if (newax == ax && neway == ay) /* amoeba cannot grow */
7146 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7148 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7151 Feld[ax][ay] = EL_AMOEBA_DEAD;
7152 DrawLevelField(ax, ay);
7153 AmoebaCnt[AmoebaNr[ax][ay]]--;
7155 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7157 if (element == EL_AMOEBA_FULL)
7158 AmoebeUmwandeln(ax, ay);
7159 else if (element == EL_BD_AMOEBA)
7160 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7165 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7167 /* amoeba gets larger by growing in some direction */
7169 int new_group_nr = AmoebaNr[ax][ay];
7172 if (new_group_nr == 0)
7174 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7175 printf("AmoebeAbleger(): This should never happen!\n");
7180 AmoebaNr[newax][neway] = new_group_nr;
7181 AmoebaCnt[new_group_nr]++;
7182 AmoebaCnt2[new_group_nr]++;
7184 /* if amoeba touches other amoeba(s) after growing, unify them */
7185 AmoebenVereinigen(newax, neway);
7187 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7189 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7195 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7196 (neway == lev_fieldy - 1 && newax != ax))
7198 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7199 Store[newax][neway] = element;
7201 else if (neway == ay)
7203 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7205 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7207 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7212 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7213 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7214 Store[ax][ay] = EL_AMOEBA_DROP;
7215 ContinueMoving(ax, ay);
7219 DrawLevelField(newax, neway);
7222 void Life(int ax, int ay)
7225 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7227 int element = Feld[ax][ay];
7228 int graphic = el2img(element);
7229 boolean changed = FALSE;
7231 if (IS_ANIMATED(graphic))
7232 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7237 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7238 MovDelay[ax][ay] = life_time;
7240 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7243 if (MovDelay[ax][ay])
7247 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7249 int xx = ax+x1, yy = ay+y1;
7252 if (!IN_LEV_FIELD(xx, yy))
7255 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7257 int x = xx+x2, y = yy+y2;
7259 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7262 if (((Feld[x][y] == element ||
7263 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7265 (IS_FREE(x, y) && Stop[x][y]))
7269 if (xx == ax && yy == ay) /* field in the middle */
7271 if (nachbarn < life[0] || nachbarn > life[1])
7273 Feld[xx][yy] = EL_EMPTY;
7275 DrawLevelField(xx, yy);
7276 Stop[xx][yy] = TRUE;
7281 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7282 { /* free border field */
7283 if (nachbarn >= life[2] && nachbarn <= life[3])
7285 Feld[xx][yy] = element;
7286 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7288 DrawLevelField(xx, yy);
7289 Stop[xx][yy] = TRUE;
7294 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7295 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7296 { /* free border field */
7297 if (nachbarn >= life[2] && nachbarn <= life[3])
7299 Feld[xx][yy] = element;
7300 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7302 DrawLevelField(xx, yy);
7303 Stop[xx][yy] = TRUE;
7311 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7312 SND_GAME_OF_LIFE_GROWING);
7315 static void InitRobotWheel(int x, int y)
7317 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7320 static void RunRobotWheel(int x, int y)
7322 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7325 static void StopRobotWheel(int x, int y)
7327 if (ZX == x && ZY == y)
7331 static void InitTimegateWheel(int x, int y)
7334 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7336 /* another brainless, "type style" bug ... :-( */
7337 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7341 static void RunTimegateWheel(int x, int y)
7343 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7346 void CheckExit(int x, int y)
7348 if (local_player->gems_still_needed > 0 ||
7349 local_player->sokobanfields_still_needed > 0 ||
7350 local_player->lights_still_needed > 0)
7352 int element = Feld[x][y];
7353 int graphic = el2img(element);
7355 if (IS_ANIMATED(graphic))
7356 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7361 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7364 Feld[x][y] = EL_EXIT_OPENING;
7366 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7369 void CheckExitSP(int x, int y)
7371 if (local_player->gems_still_needed > 0)
7373 int element = Feld[x][y];
7374 int graphic = el2img(element);
7376 if (IS_ANIMATED(graphic))
7377 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7382 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7385 Feld[x][y] = EL_SP_EXIT_OPENING;
7387 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7390 static void CloseAllOpenTimegates()
7394 for (y = 0; y < lev_fieldy; y++)
7396 for (x = 0; x < lev_fieldx; x++)
7398 int element = Feld[x][y];
7400 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7402 Feld[x][y] = EL_TIMEGATE_CLOSING;
7404 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7406 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7413 void EdelsteinFunkeln(int x, int y)
7415 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7418 if (Feld[x][y] == EL_BD_DIAMOND)
7421 if (MovDelay[x][y] == 0) /* next animation frame */
7422 MovDelay[x][y] = 11 * !SimpleRND(500);
7424 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7428 if (setup.direct_draw && MovDelay[x][y])
7429 SetDrawtoField(DRAW_BUFFERED);
7431 DrawLevelElementAnimation(x, y, Feld[x][y]);
7433 if (MovDelay[x][y] != 0)
7435 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7436 10 - MovDelay[x][y]);
7438 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7440 if (setup.direct_draw)
7444 dest_x = FX + SCREENX(x) * TILEX;
7445 dest_y = FY + SCREENY(y) * TILEY;
7447 BlitBitmap(drawto_field, window,
7448 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7449 SetDrawtoField(DRAW_DIRECT);
7455 void MauerWaechst(int x, int y)
7459 if (!MovDelay[x][y]) /* next animation frame */
7460 MovDelay[x][y] = 3 * delay;
7462 if (MovDelay[x][y]) /* wait some time before next frame */
7466 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7468 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7469 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7471 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7474 if (!MovDelay[x][y])
7476 if (MovDir[x][y] == MV_LEFT)
7478 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7479 DrawLevelField(x - 1, y);
7481 else if (MovDir[x][y] == MV_RIGHT)
7483 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7484 DrawLevelField(x + 1, y);
7486 else if (MovDir[x][y] == MV_UP)
7488 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7489 DrawLevelField(x, y - 1);
7493 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7494 DrawLevelField(x, y + 1);
7497 Feld[x][y] = Store[x][y];
7499 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7500 DrawLevelField(x, y);
7505 void MauerAbleger(int ax, int ay)
7507 int element = Feld[ax][ay];
7508 int graphic = el2img(element);
7509 boolean oben_frei = FALSE, unten_frei = FALSE;
7510 boolean links_frei = FALSE, rechts_frei = FALSE;
7511 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7512 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7513 boolean new_wall = FALSE;
7515 if (IS_ANIMATED(graphic))
7516 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7518 if (!MovDelay[ax][ay]) /* start building new wall */
7519 MovDelay[ax][ay] = 6;
7521 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7524 if (MovDelay[ax][ay])
7528 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7530 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7532 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7534 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7537 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7538 element == EL_EXPANDABLE_WALL_ANY)
7542 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7543 Store[ax][ay-1] = element;
7544 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7545 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7546 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7547 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7552 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7553 Store[ax][ay+1] = element;
7554 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7555 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7556 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7557 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7562 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7563 element == EL_EXPANDABLE_WALL_ANY ||
7564 element == EL_EXPANDABLE_WALL)
7568 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7569 Store[ax-1][ay] = element;
7570 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7571 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7572 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7573 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7579 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7580 Store[ax+1][ay] = element;
7581 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7582 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7583 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7584 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7589 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7590 DrawLevelField(ax, ay);
7592 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7594 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7595 unten_massiv = TRUE;
7596 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7597 links_massiv = TRUE;
7598 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7599 rechts_massiv = TRUE;
7601 if (((oben_massiv && unten_massiv) ||
7602 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7603 element == EL_EXPANDABLE_WALL) &&
7604 ((links_massiv && rechts_massiv) ||
7605 element == EL_EXPANDABLE_WALL_VERTICAL))
7606 Feld[ax][ay] = EL_WALL;
7610 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7612 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7616 void CheckForDragon(int x, int y)
7619 boolean dragon_found = FALSE;
7620 static int xy[4][2] =
7628 for (i = 0; i < NUM_DIRECTIONS; i++)
7630 for (j = 0; j < 4; j++)
7632 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7634 if (IN_LEV_FIELD(xx, yy) &&
7635 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7637 if (Feld[xx][yy] == EL_DRAGON)
7638 dragon_found = TRUE;
7647 for (i = 0; i < NUM_DIRECTIONS; i++)
7649 for (j = 0; j < 3; j++)
7651 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7653 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7655 Feld[xx][yy] = EL_EMPTY;
7656 DrawLevelField(xx, yy);
7665 static void InitBuggyBase(int x, int y)
7667 int element = Feld[x][y];
7668 int activating_delay = FRAMES_PER_SECOND / 4;
7671 (element == EL_SP_BUGGY_BASE ?
7672 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7673 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7675 element == EL_SP_BUGGY_BASE_ACTIVE ?
7676 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7679 static void WarnBuggyBase(int x, int y)
7682 static int xy[4][2] =
7690 for (i = 0; i < NUM_DIRECTIONS; i++)
7692 int xx = x + xy[i][0], yy = y + xy[i][1];
7694 if (IS_PLAYER(xx, yy))
7696 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7703 static void InitTrap(int x, int y)
7705 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7708 static void ActivateTrap(int x, int y)
7710 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7713 static void ChangeActiveTrap(int x, int y)
7715 int graphic = IMG_TRAP_ACTIVE;
7717 /* if new animation frame was drawn, correct crumbled sand border */
7718 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7719 DrawLevelFieldCrumbledSand(x, y);
7722 static void ChangeElementNowExt(int x, int y, int target_element)
7724 int previous_move_direction = MovDir[x][y];
7726 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7727 IS_WALKABLE(Feld[x][y]));
7729 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7730 IS_WALKABLE(Feld[x][y]) &&
7734 /* check if element under player changes from accessible to unaccessible
7735 (needed for special case of dropping element which then changes) */
7736 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7737 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7740 printf("::: BOOOM! [%d, '%s']\n", target_element,
7741 element_info[target_element].token_name);
7753 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7754 RemoveMovingField(x, y);
7758 Feld[x][y] = target_element;
7761 Feld[x][y] = target_element;
7764 ResetGfxAnimation(x, y);
7765 ResetRandomAnimationValue(x, y);
7767 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7768 MovDir[x][y] = previous_move_direction;
7771 InitField_WithBug1(x, y, FALSE);
7773 InitField(x, y, FALSE);
7774 if (CAN_MOVE(Feld[x][y]))
7778 DrawLevelField(x, y);
7780 if (GFX_CRUMBLED(Feld[x][y]))
7781 DrawLevelFieldCrumbledSandNeighbours(x, y);
7785 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7789 TestIfBadThingTouchesHero(x, y);
7790 TestIfPlayerTouchesCustomElement(x, y);
7791 TestIfElementTouchesCustomElement(x, y);
7794 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7795 if (ELEM_IS_PLAYER(target_element))
7796 RelocatePlayer(x, y, target_element);
7799 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7803 TestIfBadThingTouchesHero(x, y);
7804 TestIfPlayerTouchesCustomElement(x, y);
7805 TestIfElementTouchesCustomElement(x, y);
7809 static boolean ChangeElementNow(int x, int y, int element, int page)
7811 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7813 int old_element = Feld[x][y];
7815 /* always use default change event to prevent running into a loop */
7816 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7817 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7819 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7821 /* reset actual trigger element and player */
7822 change->actual_trigger_element = EL_EMPTY;
7823 change->actual_trigger_player = EL_PLAYER_1;
7826 /* do not change already changed elements with same change event */
7828 if (Changed[x][y] & ChangeEvent[x][y])
7835 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7838 /* !!! indirect change before direct change !!! */
7839 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7842 if (change->explode)
7849 if (change->use_target_content)
7851 boolean complete_replace = TRUE;
7852 boolean can_replace[3][3];
7855 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7858 boolean is_walkable;
7859 boolean is_diggable;
7860 boolean is_collectible;
7861 boolean is_removable;
7862 boolean is_destructible;
7863 int ex = x + xx - 1;
7864 int ey = y + yy - 1;
7865 int content_element = change->target_content[xx][yy];
7868 can_replace[xx][yy] = TRUE;
7870 if (ex == x && ey == y) /* do not check changing element itself */
7873 if (content_element == EL_EMPTY_SPACE)
7875 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7880 if (!IN_LEV_FIELD(ex, ey))
7882 can_replace[xx][yy] = FALSE;
7883 complete_replace = FALSE;
7889 if (Changed[ex][ey]) /* do not change already changed elements */
7891 can_replace[xx][yy] = FALSE;
7892 complete_replace = FALSE;
7900 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7901 e = MovingOrBlocked2Element(ex, ey);
7906 is_empty = (IS_FREE(ex, ey) ||
7907 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7908 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7909 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7913 is_empty = (IS_FREE(ex, ey) ||
7914 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7916 is_empty = (IS_FREE(ex, ey) ||
7917 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7922 is_walkable = (is_empty || IS_WALKABLE(e));
7923 is_diggable = (is_empty || IS_DIGGABLE(e));
7924 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7925 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7926 is_removable = (is_diggable || is_collectible);
7928 can_replace[xx][yy] =
7929 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7930 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7931 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7932 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7933 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7934 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7935 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7937 if (!can_replace[xx][yy])
7938 complete_replace = FALSE;
7940 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7941 IS_WALKABLE(content_element)));
7943 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7945 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7948 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7949 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7950 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7952 can_replace[xx][yy] = FALSE;
7953 complete_replace = FALSE;
7958 if (!change->only_if_complete || complete_replace)
7960 boolean something_has_changed = FALSE;
7962 if (change->only_if_complete && change->use_random_replace &&
7963 RND(100) < change->random_percentage)
7966 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7968 int ex = x + xx - 1;
7969 int ey = y + yy - 1;
7970 int content_element;
7972 if (can_replace[xx][yy] && (!change->use_random_replace ||
7973 RND(100) < change->random_percentage))
7975 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7976 RemoveMovingField(ex, ey);
7978 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7980 content_element = change->target_content[xx][yy];
7981 target_element = GET_TARGET_ELEMENT(content_element, change);
7983 ChangeElementNowExt(ex, ey, target_element);
7985 something_has_changed = TRUE;
7987 /* for symmetry reasons, freeze newly created border elements */
7988 if (ex != x || ey != y)
7989 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7993 if (something_has_changed)
7994 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7999 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8001 ChangeElementNowExt(x, y, target_element);
8003 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8007 /* this uses direct change before indirect change */
8008 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
8014 static void ChangeElement(int x, int y, int page)
8016 int element = MovingOrBlocked2Element(x, y);
8017 struct ElementInfo *ei = &element_info[element];
8018 struct ElementChangeInfo *change = &ei->change_page[page];
8021 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8024 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8025 x, y, element, element_info[element].token_name);
8026 printf("ChangeElement(): This should never happen!\n");
8031 /* this can happen with classic bombs on walkable, changing elements */
8032 if (!CAN_CHANGE(element))
8035 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8036 ChangeDelay[x][y] = 0;
8042 if (ChangeDelay[x][y] == 0) /* initialize element change */
8044 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8045 RND(change->delay_random * change->delay_frames)) + 1;
8047 ResetGfxAnimation(x, y);
8048 ResetRandomAnimationValue(x, y);
8050 if (change->pre_change_function)
8051 change->pre_change_function(x, y);
8054 ChangeDelay[x][y]--;
8056 if (ChangeDelay[x][y] != 0) /* continue element change */
8058 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8060 if (IS_ANIMATED(graphic))
8061 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8063 if (change->change_function)
8064 change->change_function(x, y);
8066 else /* finish element change */
8068 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8070 page = ChangePage[x][y];
8071 ChangePage[x][y] = -1;
8073 change = &ei->change_page[page];
8077 if (IS_MOVING(x, y) && !change->explode)
8079 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8082 ChangeDelay[x][y] = 1; /* try change after next move step */
8083 ChangePage[x][y] = page; /* remember page to use for change */
8088 if (ChangeElementNow(x, y, element, page))
8090 if (change->post_change_function)
8091 change->post_change_function(x, y);
8096 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8097 int trigger_element,
8104 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8106 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8109 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8111 int element = EL_CUSTOM_START + i;
8113 boolean change_element = FALSE;
8116 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8119 for (j = 0; j < element_info[element].num_change_pages; j++)
8121 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8123 if (change->can_change &&
8124 change->events & CH_EVENT_BIT(trigger_event) &&
8125 change->trigger_side & trigger_side &&
8126 change->trigger_player & trigger_player &&
8127 change->trigger_page & trigger_page_bits &&
8128 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8131 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8132 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8133 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8136 change_element = TRUE;
8139 change->actual_trigger_element = trigger_element;
8140 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8146 if (!change_element)
8149 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8152 if (x == lx && y == ly) /* do not change trigger element itself */
8156 if (Feld[x][y] == element)
8158 ChangeDelay[x][y] = 1;
8159 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8160 ChangeElement(x, y, page);
8168 static boolean CheckElementChangeExt(int x, int y,
8170 int trigger_element,
8176 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8179 if (Feld[x][y] == EL_BLOCKED)
8181 Blocked2Moving(x, y, &x, &y);
8182 element = Feld[x][y];
8186 if (Feld[x][y] != element) /* check if element has already changed */
8189 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8190 Feld[x][y], element_info[Feld[x][y]].token_name,
8191 element, element_info[element].token_name,
8200 if (trigger_page < 0)
8202 boolean change_element = FALSE;
8205 for (i = 0; i < element_info[element].num_change_pages; i++)
8207 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8209 if (change->can_change &&
8210 change->events & CH_EVENT_BIT(trigger_event) &&
8211 change->trigger_side & trigger_side &&
8212 change->trigger_player & trigger_player)
8214 change_element = TRUE;
8217 change->actual_trigger_element = trigger_element;
8218 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8224 if (!change_element)
8229 struct ElementInfo *ei = &element_info[element];
8230 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8232 change->actual_trigger_element = trigger_element;
8233 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8238 /* !!! this check misses pages with same event, but different side !!! */
8240 if (trigger_page < 0)
8241 trigger_page = element_info[element].event_page_nr[trigger_event];
8243 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8247 ChangeDelay[x][y] = 1;
8248 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8249 ChangeElement(x, y, trigger_page);
8254 static void PlayPlayerSound(struct PlayerInfo *player)
8256 int jx = player->jx, jy = player->jy;
8257 int element = player->element_nr;
8258 int last_action = player->last_action_waiting;
8259 int action = player->action_waiting;
8261 if (player->is_waiting)
8263 if (action != last_action)
8264 PlayLevelSoundElementAction(jx, jy, element, action);
8266 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8270 if (action != last_action)
8271 StopSound(element_info[element].sound[last_action]);
8273 if (last_action == ACTION_SLEEPING)
8274 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8278 static void PlayAllPlayersSound()
8282 for (i = 0; i < MAX_PLAYERS; i++)
8283 if (stored_player[i].active)
8284 PlayPlayerSound(&stored_player[i]);
8287 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8289 boolean last_waiting = player->is_waiting;
8290 int move_dir = player->MovDir;
8292 player->last_action_waiting = player->action_waiting;
8296 if (!last_waiting) /* not waiting -> waiting */
8298 player->is_waiting = TRUE;
8300 player->frame_counter_bored =
8302 game.player_boring_delay_fixed +
8303 SimpleRND(game.player_boring_delay_random);
8304 player->frame_counter_sleeping =
8306 game.player_sleeping_delay_fixed +
8307 SimpleRND(game.player_sleeping_delay_random);
8309 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8312 if (game.player_sleeping_delay_fixed +
8313 game.player_sleeping_delay_random > 0 &&
8314 player->anim_delay_counter == 0 &&
8315 player->post_delay_counter == 0 &&
8316 FrameCounter >= player->frame_counter_sleeping)
8317 player->is_sleeping = TRUE;
8318 else if (game.player_boring_delay_fixed +
8319 game.player_boring_delay_random > 0 &&
8320 FrameCounter >= player->frame_counter_bored)
8321 player->is_bored = TRUE;
8323 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8324 player->is_bored ? ACTION_BORING :
8327 if (player->is_sleeping)
8329 if (player->num_special_action_sleeping > 0)
8331 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8333 int last_special_action = player->special_action_sleeping;
8334 int num_special_action = player->num_special_action_sleeping;
8335 int special_action =
8336 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8337 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8338 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8339 last_special_action + 1 : ACTION_SLEEPING);
8340 int special_graphic =
8341 el_act_dir2img(player->element_nr, special_action, move_dir);
8343 player->anim_delay_counter =
8344 graphic_info[special_graphic].anim_delay_fixed +
8345 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8346 player->post_delay_counter =
8347 graphic_info[special_graphic].post_delay_fixed +
8348 SimpleRND(graphic_info[special_graphic].post_delay_random);
8350 player->special_action_sleeping = special_action;
8353 if (player->anim_delay_counter > 0)
8355 player->action_waiting = player->special_action_sleeping;
8356 player->anim_delay_counter--;
8358 else if (player->post_delay_counter > 0)
8360 player->post_delay_counter--;
8364 else if (player->is_bored)
8366 if (player->num_special_action_bored > 0)
8368 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8370 int special_action =
8371 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8372 int special_graphic =
8373 el_act_dir2img(player->element_nr, special_action, move_dir);
8375 player->anim_delay_counter =
8376 graphic_info[special_graphic].anim_delay_fixed +
8377 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8378 player->post_delay_counter =
8379 graphic_info[special_graphic].post_delay_fixed +
8380 SimpleRND(graphic_info[special_graphic].post_delay_random);
8382 player->special_action_bored = special_action;
8385 if (player->anim_delay_counter > 0)
8387 player->action_waiting = player->special_action_bored;
8388 player->anim_delay_counter--;
8390 else if (player->post_delay_counter > 0)
8392 player->post_delay_counter--;
8397 else if (last_waiting) /* waiting -> not waiting */
8399 player->is_waiting = FALSE;
8400 player->is_bored = FALSE;
8401 player->is_sleeping = FALSE;
8403 player->frame_counter_bored = -1;
8404 player->frame_counter_sleeping = -1;
8406 player->anim_delay_counter = 0;
8407 player->post_delay_counter = 0;
8409 player->action_waiting = ACTION_DEFAULT;
8411 player->special_action_bored = ACTION_DEFAULT;
8412 player->special_action_sleeping = ACTION_DEFAULT;
8417 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8420 static byte stored_player_action[MAX_PLAYERS];
8421 static int num_stored_actions = 0;
8423 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8424 int left = player_action & JOY_LEFT;
8425 int right = player_action & JOY_RIGHT;
8426 int up = player_action & JOY_UP;
8427 int down = player_action & JOY_DOWN;
8428 int button1 = player_action & JOY_BUTTON_1;
8429 int button2 = player_action & JOY_BUTTON_2;
8430 int dx = (left ? -1 : right ? 1 : 0);
8431 int dy = (up ? -1 : down ? 1 : 0);
8434 stored_player_action[player->index_nr] = 0;
8435 num_stored_actions++;
8439 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8442 if (!player->active || tape.pausing)
8446 printf("::: [%d %d %d %d] [%d %d]\n",
8447 left, right, up, down, button1, button2);
8453 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8458 if (player->MovPos == 0)
8459 CheckGravityMovement(player);
8462 snapped = SnapField(player, dx, dy);
8466 dropped = DropElement(player);
8468 moved = MovePlayer(player, dx, dy);
8471 if (tape.single_step && tape.recording && !tape.pausing)
8473 if (button1 || (dropped && !moved))
8475 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8476 SnapField(player, 0, 0); /* stop snapping */
8480 SetPlayerWaiting(player, FALSE);
8483 return player_action;
8485 stored_player_action[player->index_nr] = player_action;
8491 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8494 /* no actions for this player (no input at player's configured device) */
8496 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8497 SnapField(player, 0, 0);
8498 CheckGravityMovementWhenNotMoving(player);
8500 if (player->MovPos == 0)
8501 SetPlayerWaiting(player, TRUE);
8503 if (player->MovPos == 0) /* needed for tape.playing */
8504 player->is_moving = FALSE;
8506 player->is_dropping = FALSE;
8512 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8514 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8516 TapeRecordAction(stored_player_action);
8517 num_stored_actions = 0;
8524 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8526 static byte stored_player_action[MAX_PLAYERS];
8527 static int num_stored_actions = 0;
8528 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8529 int left = player_action & JOY_LEFT;
8530 int right = player_action & JOY_RIGHT;
8531 int up = player_action & JOY_UP;
8532 int down = player_action & JOY_DOWN;
8533 int button1 = player_action & JOY_BUTTON_1;
8534 int button2 = player_action & JOY_BUTTON_2;
8535 int dx = (left ? -1 : right ? 1 : 0);
8536 int dy = (up ? -1 : down ? 1 : 0);
8538 stored_player_action[player->index_nr] = 0;
8539 num_stored_actions++;
8541 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8543 if (!player->active || tape.pausing)
8548 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8551 snapped = SnapField(player, dx, dy);
8555 dropped = DropElement(player);
8557 moved = MovePlayer(player, dx, dy);
8560 if (tape.single_step && tape.recording && !tape.pausing)
8562 if (button1 || (dropped && !moved))
8564 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8565 SnapField(player, 0, 0); /* stop snapping */
8569 stored_player_action[player->index_nr] = player_action;
8573 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8575 /* no actions for this player (no input at player's configured device) */
8577 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8578 SnapField(player, 0, 0);
8579 CheckGravityMovementWhenNotMoving(player);
8581 if (player->MovPos == 0)
8582 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8584 if (player->MovPos == 0) /* needed for tape.playing */
8585 player->is_moving = FALSE;
8588 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8590 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8592 TapeRecordAction(stored_player_action);
8593 num_stored_actions = 0;
8598 void AdvanceFrameAndPlayerCounters(int player_nr)
8602 /* advance frame counters (global frame counter and time frame counter) */
8606 /* advance player counters (counters for move delay, move animation etc.) */
8607 for (i = 0; i < MAX_PLAYERS; i++)
8609 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8611 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8613 if (!advance_player_counters) /* not all players may be affected */
8616 stored_player[i].Frame += move_frames;
8618 if (stored_player[i].MovPos != 0)
8619 stored_player[i].StepFrame += move_frames;
8621 #if USE_NEW_MOVE_DELAY
8622 if (stored_player[i].move_delay > 0)
8623 stored_player[i].move_delay--;
8626 #if USE_NEW_PUSH_DELAY
8627 /* due to bugs in previous versions, counter must count up, not down */
8628 if (stored_player[i].push_delay != -1)
8629 stored_player[i].push_delay++;
8632 if (stored_player[i].drop_delay > 0)
8633 stored_player[i].drop_delay--;
8639 static unsigned long game_frame_delay = 0;
8640 unsigned long game_frame_delay_value;
8641 int magic_wall_x = 0, magic_wall_y = 0;
8642 int i, x, y, element, graphic;
8643 byte *recorded_player_action;
8644 byte summarized_player_action = 0;
8646 byte tape_action[MAX_PLAYERS];
8649 if (game_status != GAME_MODE_PLAYING)
8652 game_frame_delay_value =
8653 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8655 if (tape.playing && tape.warp_forward && !tape.pausing)
8656 game_frame_delay_value = 0;
8658 /* ---------- main game synchronization point ---------- */
8660 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8662 if (network_playing && !network_player_action_received)
8666 printf("DEBUG: try to get network player actions in time\n");
8670 #if defined(NETWORK_AVALIABLE)
8671 /* last chance to get network player actions without main loop delay */
8675 if (game_status != GAME_MODE_PLAYING)
8678 if (!network_player_action_received)
8682 printf("DEBUG: failed to get network player actions in time\n");
8693 printf("::: getting new tape action [%d]\n", FrameCounter);
8696 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8699 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8700 if (recorded_player_action == NULL && tape.pausing)
8705 printf("::: %d\n", stored_player[0].action);
8709 if (recorded_player_action != NULL)
8710 for (i = 0; i < MAX_PLAYERS; i++)
8711 stored_player[i].action = recorded_player_action[i];
8714 for (i = 0; i < MAX_PLAYERS; i++)
8716 summarized_player_action |= stored_player[i].action;
8718 if (!network_playing)
8719 stored_player[i].effective_action = stored_player[i].action;
8722 #if defined(NETWORK_AVALIABLE)
8723 if (network_playing)
8724 SendToServer_MovePlayer(summarized_player_action);
8727 if (!options.network && !setup.team_mode)
8728 local_player->effective_action = summarized_player_action;
8731 if (recorded_player_action != NULL)
8732 for (i = 0; i < MAX_PLAYERS; i++)
8733 stored_player[i].effective_action = recorded_player_action[i];
8737 for (i = 0; i < MAX_PLAYERS; i++)
8739 tape_action[i] = stored_player[i].effective_action;
8741 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8742 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8745 /* only save actions from input devices, but not programmed actions */
8747 TapeRecordAction(tape_action);
8750 for (i = 0; i < MAX_PLAYERS; i++)
8752 int actual_player_action = stored_player[i].effective_action;
8755 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8756 - rnd_equinox_tetrachloride 048
8757 - rnd_equinox_tetrachloride_ii 096
8758 - rnd_emanuel_schmieg 002
8759 - doctor_sloan_ww 001, 020
8761 if (stored_player[i].MovPos == 0)
8762 CheckGravityMovement(&stored_player[i]);
8766 /* overwrite programmed action with tape action */
8767 if (stored_player[i].programmed_action)
8768 actual_player_action = stored_player[i].programmed_action;
8772 if (stored_player[i].programmed_action)
8773 printf("::: %d\n", stored_player[i].programmed_action);
8776 if (recorded_player_action)
8779 if (stored_player[i].programmed_action &&
8780 stored_player[i].programmed_action != recorded_player_action[i])
8781 printf("::: %d: %d <-> %d\n", i,
8782 stored_player[i].programmed_action, recorded_player_action[i]);
8786 actual_player_action = recorded_player_action[i];
8791 /* overwrite tape action with programmed action */
8792 if (stored_player[i].programmed_action)
8793 actual_player_action = stored_player[i].programmed_action;
8798 printf("::: action: %d: %x [%d]\n",
8799 stored_player[i].MovPos, actual_player_action, FrameCounter);
8803 PlayerActions(&stored_player[i], actual_player_action);
8805 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8807 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8808 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8811 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8816 TapeRecordAction(tape_action);
8819 network_player_action_received = FALSE;
8821 ScrollScreen(NULL, SCROLL_GO_ON);
8827 for (i = 0; i < MAX_PLAYERS; i++)
8828 stored_player[i].Frame++;
8832 /* for backwards compatibility, the following code emulates a fixed bug that
8833 occured when pushing elements (causing elements that just made their last
8834 pushing step to already (if possible) make their first falling step in the
8835 same game frame, which is bad); this code is also needed to use the famous
8836 "spring push bug" which is used in older levels and might be wanted to be
8837 used also in newer levels, but in this case the buggy pushing code is only
8838 affecting the "spring" element and no other elements */
8841 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8843 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8846 for (i = 0; i < MAX_PLAYERS; i++)
8848 struct PlayerInfo *player = &stored_player[i];
8853 if (player->active && player->is_pushing && player->is_moving &&
8855 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8856 Feld[x][y] == EL_SPRING))
8858 if (player->active && player->is_pushing && player->is_moving &&
8862 ContinueMoving(x, y);
8864 /* continue moving after pushing (this is actually a bug) */
8865 if (!IS_MOVING(x, y))
8874 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8876 Changed[x][y] = CE_BITMASK_DEFAULT;
8877 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8879 #if USE_NEW_BLOCK_STYLE
8880 /* this must be handled before main playfield loop */
8881 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8884 if (MovDelay[x][y] <= 0)
8890 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8892 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8893 printf("GameActions(): This should never happen!\n");
8895 ChangePage[x][y] = -1;
8900 if (WasJustMoving[x][y] > 0)
8901 WasJustMoving[x][y]--;
8902 if (WasJustFalling[x][y] > 0)
8903 WasJustFalling[x][y]--;
8904 if (CheckCollision[x][y] > 0)
8905 CheckCollision[x][y]--;
8910 /* reset finished pushing action (not done in ContinueMoving() to allow
8911 continous pushing animation for elements with zero push delay) */
8912 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8914 ResetGfxAnimation(x, y);
8915 DrawLevelField(x, y);
8920 if (IS_BLOCKED(x, y))
8924 Blocked2Moving(x, y, &oldx, &oldy);
8925 if (!IS_MOVING(oldx, oldy))
8927 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8928 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8929 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8930 printf("GameActions(): This should never happen!\n");
8936 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8938 element = Feld[x][y];
8940 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8942 graphic = el2img(element);
8948 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8950 element = graphic = 0;
8954 if (graphic_info[graphic].anim_global_sync)
8955 GfxFrame[x][y] = FrameCounter;
8957 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8958 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8959 ResetRandomAnimationValue(x, y);
8961 SetRandomAnimationValue(x, y);
8964 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8967 if (IS_INACTIVE(element))
8969 if (IS_ANIMATED(graphic))
8970 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8976 /* this may take place after moving, so 'element' may have changed */
8978 if (IS_CHANGING(x, y))
8980 if (IS_CHANGING(x, y) &&
8981 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8985 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8986 element_info[element].event_page_nr[CE_DELAY]);
8988 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8991 element = Feld[x][y];
8992 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8996 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9001 element = Feld[x][y];
9002 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9004 if (element == EL_MOLE)
9005 printf("::: %d, %d, %d [%d]\n",
9006 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9010 if (element == EL_YAMYAM)
9011 printf("::: %d, %d, %d\n",
9012 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9016 if (IS_ANIMATED(graphic) &&
9020 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9023 if (element == EL_BUG)
9024 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9028 if (element == EL_MOLE)
9029 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9033 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9034 EdelsteinFunkeln(x, y);
9036 else if ((element == EL_ACID ||
9037 element == EL_EXIT_OPEN ||
9038 element == EL_SP_EXIT_OPEN ||
9039 element == EL_SP_TERMINAL ||
9040 element == EL_SP_TERMINAL_ACTIVE ||
9041 element == EL_EXTRA_TIME ||
9042 element == EL_SHIELD_NORMAL ||
9043 element == EL_SHIELD_DEADLY) &&
9044 IS_ANIMATED(graphic))
9045 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9046 else if (IS_MOVING(x, y))
9047 ContinueMoving(x, y);
9048 else if (IS_ACTIVE_BOMB(element))
9049 CheckDynamite(x, y);
9051 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9052 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9054 else if (element == EL_AMOEBA_GROWING)
9055 AmoebeWaechst(x, y);
9056 else if (element == EL_AMOEBA_SHRINKING)
9057 AmoebaDisappearing(x, y);
9059 #if !USE_NEW_AMOEBA_CODE
9060 else if (IS_AMOEBALIVE(element))
9061 AmoebeAbleger(x, y);
9064 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9066 else if (element == EL_EXIT_CLOSED)
9068 else if (element == EL_SP_EXIT_CLOSED)
9070 else if (element == EL_EXPANDABLE_WALL_GROWING)
9072 else if (element == EL_EXPANDABLE_WALL ||
9073 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9074 element == EL_EXPANDABLE_WALL_VERTICAL ||
9075 element == EL_EXPANDABLE_WALL_ANY)
9077 else if (element == EL_FLAMES)
9078 CheckForDragon(x, y);
9080 else if (IS_AUTO_CHANGING(element))
9081 ChangeElement(x, y);
9083 else if (element == EL_EXPLOSION)
9084 ; /* drawing of correct explosion animation is handled separately */
9085 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9086 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9089 /* this may take place after moving, so 'element' may have changed */
9090 if (IS_AUTO_CHANGING(Feld[x][y]))
9091 ChangeElement(x, y);
9094 if (IS_BELT_ACTIVE(element))
9095 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9097 if (game.magic_wall_active)
9099 int jx = local_player->jx, jy = local_player->jy;
9101 /* play the element sound at the position nearest to the player */
9102 if ((element == EL_MAGIC_WALL_FULL ||
9103 element == EL_MAGIC_WALL_ACTIVE ||
9104 element == EL_MAGIC_WALL_EMPTYING ||
9105 element == EL_BD_MAGIC_WALL_FULL ||
9106 element == EL_BD_MAGIC_WALL_ACTIVE ||
9107 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9108 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9116 #if USE_NEW_AMOEBA_CODE
9117 /* new experimental amoeba growth stuff */
9119 if (!(FrameCounter % 8))
9122 static unsigned long random = 1684108901;
9124 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9127 x = (random >> 10) % lev_fieldx;
9128 y = (random >> 20) % lev_fieldy;
9130 x = RND(lev_fieldx);
9131 y = RND(lev_fieldy);
9133 element = Feld[x][y];
9136 if (!IS_PLAYER(x,y) &&
9137 (element == EL_EMPTY ||
9138 CAN_GROW_INTO(element) ||
9139 element == EL_QUICKSAND_EMPTY ||
9140 element == EL_ACID_SPLASH_LEFT ||
9141 element == EL_ACID_SPLASH_RIGHT))
9143 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9144 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9145 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9146 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9147 Feld[x][y] = EL_AMOEBA_DROP;
9150 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9151 if (!IS_PLAYER(x,y) &&
9152 (element == EL_EMPTY ||
9153 element == EL_SAND ||
9154 element == EL_QUICKSAND_EMPTY ||
9155 element == EL_ACID_SPLASH_LEFT ||
9156 element == EL_ACID_SPLASH_RIGHT))
9158 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9159 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9160 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9161 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9162 Feld[x][y] = EL_AMOEBA_DROP;
9166 random = random * 129 + 1;
9172 if (game.explosions_delayed)
9175 game.explosions_delayed = FALSE;
9177 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9179 element = Feld[x][y];
9181 if (ExplodeField[x][y])
9182 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9183 else if (element == EL_EXPLOSION)
9184 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9186 ExplodeField[x][y] = EX_TYPE_NONE;
9189 game.explosions_delayed = TRUE;
9192 if (game.magic_wall_active)
9194 if (!(game.magic_wall_time_left % 4))
9196 int element = Feld[magic_wall_x][magic_wall_y];
9198 if (element == EL_BD_MAGIC_WALL_FULL ||
9199 element == EL_BD_MAGIC_WALL_ACTIVE ||
9200 element == EL_BD_MAGIC_WALL_EMPTYING)
9201 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9203 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9206 if (game.magic_wall_time_left > 0)
9208 game.magic_wall_time_left--;
9209 if (!game.magic_wall_time_left)
9211 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9213 element = Feld[x][y];
9215 if (element == EL_MAGIC_WALL_ACTIVE ||
9216 element == EL_MAGIC_WALL_FULL)
9218 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9219 DrawLevelField(x, y);
9221 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9222 element == EL_BD_MAGIC_WALL_FULL)
9224 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9225 DrawLevelField(x, y);
9229 game.magic_wall_active = FALSE;
9234 if (game.light_time_left > 0)
9236 game.light_time_left--;
9238 if (game.light_time_left == 0)
9239 RedrawAllLightSwitchesAndInvisibleElements();
9242 if (game.timegate_time_left > 0)
9244 game.timegate_time_left--;
9246 if (game.timegate_time_left == 0)
9247 CloseAllOpenTimegates();
9250 for (i = 0; i < MAX_PLAYERS; i++)
9252 struct PlayerInfo *player = &stored_player[i];
9254 if (SHIELD_ON(player))
9256 if (player->shield_deadly_time_left)
9257 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9258 else if (player->shield_normal_time_left)
9259 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9263 if (TimeFrames >= FRAMES_PER_SECOND)
9268 for (i = 0; i < MAX_PLAYERS; i++)
9270 struct PlayerInfo *player = &stored_player[i];
9272 if (SHIELD_ON(player))
9274 player->shield_normal_time_left--;
9276 if (player->shield_deadly_time_left > 0)
9277 player->shield_deadly_time_left--;
9281 if (!level.use_step_counter)
9289 if (TimeLeft <= 10 && setup.time_limit)
9290 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9292 DrawGameValue_Time(TimeLeft);
9294 if (!TimeLeft && setup.time_limit)
9295 for (i = 0; i < MAX_PLAYERS; i++)
9296 KillHero(&stored_player[i]);
9298 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9299 DrawGameValue_Time(TimePlayed);
9302 if (tape.recording || tape.playing)
9303 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9307 PlayAllPlayersSound();
9309 if (options.debug) /* calculate frames per second */
9311 static unsigned long fps_counter = 0;
9312 static int fps_frames = 0;
9313 unsigned long fps_delay_ms = Counter() - fps_counter;
9317 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9319 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9322 fps_counter = Counter();
9325 redraw_mask |= REDRAW_FPS;
9329 if (stored_player[0].jx != stored_player[0].last_jx ||
9330 stored_player[0].jy != stored_player[0].last_jy)
9331 printf("::: %d, %d, %d, %d, %d\n",
9332 stored_player[0].MovDir,
9333 stored_player[0].MovPos,
9334 stored_player[0].GfxPos,
9335 stored_player[0].Frame,
9336 stored_player[0].StepFrame);
9339 #if USE_NEW_MOVE_DELAY
9340 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9345 for (i = 0; i < MAX_PLAYERS; i++)
9348 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9350 stored_player[i].Frame += move_frames;
9352 if (stored_player[i].MovPos != 0)
9353 stored_player[i].StepFrame += move_frames;
9355 #if USE_NEW_MOVE_DELAY
9356 if (stored_player[i].move_delay > 0)
9357 stored_player[i].move_delay--;
9360 if (stored_player[i].drop_delay > 0)
9361 stored_player[i].drop_delay--;
9366 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9368 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9370 local_player->show_envelope = 0;
9374 #if USE_NEW_RANDOMIZE
9375 /* use random number generator in every frame to make it less predictable */
9376 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9381 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9383 int min_x = x, min_y = y, max_x = x, max_y = y;
9386 for (i = 0; i < MAX_PLAYERS; i++)
9388 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9390 if (!stored_player[i].active || &stored_player[i] == player)
9393 min_x = MIN(min_x, jx);
9394 min_y = MIN(min_y, jy);
9395 max_x = MAX(max_x, jx);
9396 max_y = MAX(max_y, jy);
9399 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9402 static boolean AllPlayersInVisibleScreen()
9406 for (i = 0; i < MAX_PLAYERS; i++)
9408 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9410 if (!stored_player[i].active)
9413 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9420 void ScrollLevel(int dx, int dy)
9422 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9425 BlitBitmap(drawto_field, drawto_field,
9426 FX + TILEX * (dx == -1) - softscroll_offset,
9427 FY + TILEY * (dy == -1) - softscroll_offset,
9428 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9429 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9430 FX + TILEX * (dx == 1) - softscroll_offset,
9431 FY + TILEY * (dy == 1) - softscroll_offset);
9435 x = (dx == 1 ? BX1 : BX2);
9436 for (y = BY1; y <= BY2; y++)
9437 DrawScreenField(x, y);
9442 y = (dy == 1 ? BY1 : BY2);
9443 for (x = BX1; x <= BX2; x++)
9444 DrawScreenField(x, y);
9447 redraw_mask |= REDRAW_FIELD;
9451 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9453 int nextx = x + dx, nexty = y + dy;
9454 int element = Feld[x][y];
9457 element != EL_SP_PORT_LEFT &&
9458 element != EL_SP_GRAVITY_PORT_LEFT &&
9459 element != EL_SP_PORT_HORIZONTAL &&
9460 element != EL_SP_PORT_ANY) ||
9462 element != EL_SP_PORT_RIGHT &&
9463 element != EL_SP_GRAVITY_PORT_RIGHT &&
9464 element != EL_SP_PORT_HORIZONTAL &&
9465 element != EL_SP_PORT_ANY) ||
9467 element != EL_SP_PORT_UP &&
9468 element != EL_SP_GRAVITY_PORT_UP &&
9469 element != EL_SP_PORT_VERTICAL &&
9470 element != EL_SP_PORT_ANY) ||
9472 element != EL_SP_PORT_DOWN &&
9473 element != EL_SP_GRAVITY_PORT_DOWN &&
9474 element != EL_SP_PORT_VERTICAL &&
9475 element != EL_SP_PORT_ANY) ||
9476 !IN_LEV_FIELD(nextx, nexty) ||
9477 !IS_FREE(nextx, nexty))
9484 static boolean canFallDown(struct PlayerInfo *player)
9486 int jx = player->jx, jy = player->jy;
9488 return (IN_LEV_FIELD(jx, jy + 1) &&
9489 (IS_FREE(jx, jy + 1) ||
9490 #if USE_NEW_BLOCK_STYLE
9491 #if USE_GRAVITY_BUGFIX
9492 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9495 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9496 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9497 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9500 static boolean canPassField(int x, int y, int move_dir)
9502 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9503 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9504 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9507 int element = Feld[x][y];
9509 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9510 !CAN_MOVE(element) &&
9511 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9512 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9513 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9516 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9518 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9519 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9520 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9524 int nextx = newx + dx;
9525 int nexty = newy + dy;
9529 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9530 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9532 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9534 (IS_DIGGABLE(Feld[newx][newy]) ||
9535 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9536 canPassField(newx, newy, move_dir)));
9539 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9540 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9541 (IS_DIGGABLE(Feld[newx][newy]) ||
9542 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9543 canPassField(newx, newy, move_dir)));
9546 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9547 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9548 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9549 canPassField(newx, newy, move_dir)));
9551 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9552 (IS_DIGGABLE(Feld[newx][newy]) ||
9553 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9554 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9555 !CAN_MOVE(Feld[newx][newy]) &&
9556 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9557 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9558 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9564 static void CheckGravityMovement(struct PlayerInfo *player)
9566 if (game.gravity && !player->programmed_action)
9569 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9570 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9572 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9573 int move_dir_vertical = player->action & MV_VERTICAL;
9577 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9579 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9582 int jx = player->jx, jy = player->jy;
9584 boolean player_is_moving_to_valid_field =
9585 (!player_is_snapping &&
9586 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9587 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9591 (player->last_move_dir & MV_HORIZONTAL ?
9592 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9593 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9597 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9598 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9599 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9600 int new_jx = jx + dx, new_jy = jy + dy;
9601 int nextx = new_jx + dx, nexty = new_jy + dy;
9607 boolean player_can_fall_down = canFallDown(player);
9609 boolean player_can_fall_down =
9610 (IN_LEV_FIELD(jx, jy + 1) &&
9611 (IS_FREE(jx, jy + 1) ||
9612 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9616 boolean player_can_fall_down =
9617 (IN_LEV_FIELD(jx, jy + 1) &&
9618 (IS_FREE(jx, jy + 1)));
9622 boolean player_is_moving_to_valid_field =
9625 !player_is_snapping &&
9629 IN_LEV_FIELD(new_jx, new_jy) &&
9630 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9631 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9632 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9633 IN_LEV_FIELD(nextx, nexty) &&
9634 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9636 IN_LEV_FIELD(new_jx, new_jy) &&
9637 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9638 Feld[new_jx][new_jy] == EL_SAND ||
9639 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9640 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9641 /* !!! extend EL_SAND to anything diggable !!! */
9647 boolean player_is_standing_on_valid_field =
9648 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9649 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9653 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9654 player_can_fall_down,
9655 player_is_standing_on_valid_field,
9656 player_is_moving_to_valid_field,
9657 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9658 player->effective_action,
9659 player->can_fall_into_acid);
9662 if (player_can_fall_down &&
9664 !player_is_standing_on_valid_field &&
9666 !player_is_moving_to_valid_field)
9669 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9670 jx, jy, FrameCounter);
9673 player->programmed_action = MV_DOWN;
9678 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9681 return CheckGravityMovement(player);
9684 if (game.gravity && !player->programmed_action)
9686 int jx = player->jx, jy = player->jy;
9687 boolean field_under_player_is_free =
9688 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9689 boolean player_is_standing_on_valid_field =
9690 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9691 (IS_WALKABLE(Feld[jx][jy]) &&
9692 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9694 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9695 player->programmed_action = MV_DOWN;
9701 -----------------------------------------------------------------------------
9702 dx, dy: direction (non-diagonal) to try to move the player to
9703 real_dx, real_dy: direction as read from input device (can be diagonal)
9706 boolean MovePlayerOneStep(struct PlayerInfo *player,
9707 int dx, int dy, int real_dx, int real_dy)
9710 static int trigger_sides[4][2] =
9712 /* enter side leave side */
9713 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9714 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9715 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9716 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9718 int move_direction = (dx == -1 ? MV_LEFT :
9719 dx == +1 ? MV_RIGHT :
9721 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9722 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9723 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9725 int jx = player->jx, jy = player->jy;
9726 int new_jx = jx + dx, new_jy = jy + dy;
9730 if (!player->active || (!dx && !dy))
9731 return MF_NO_ACTION;
9733 player->MovDir = (dx < 0 ? MV_LEFT :
9736 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9738 if (!IN_LEV_FIELD(new_jx, new_jy))
9739 return MF_NO_ACTION;
9741 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9742 return MF_NO_ACTION;
9745 element = MovingOrBlocked2Element(new_jx, new_jy);
9747 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9750 if (DONT_RUN_INTO(element))
9752 if (element == EL_ACID && dx == 0 && dy == 1)
9754 SplashAcid(new_jx, new_jy);
9755 Feld[jx][jy] = EL_PLAYER_1;
9756 InitMovingField(jx, jy, MV_DOWN);
9757 Store[jx][jy] = EL_ACID;
9758 ContinueMoving(jx, jy);
9762 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9767 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9768 if (can_move != MF_MOVING)
9771 /* check if DigField() has caused relocation of the player */
9772 if (player->jx != jx || player->jy != jy)
9773 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9775 StorePlayer[jx][jy] = 0;
9776 player->last_jx = jx;
9777 player->last_jy = jy;
9778 player->jx = new_jx;
9779 player->jy = new_jy;
9780 StorePlayer[new_jx][new_jy] = player->element_nr;
9783 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9785 player->step_counter++;
9788 player->drop_delay = 0;
9791 PlayerVisit[jx][jy] = FrameCounter;
9793 ScrollPlayer(player, SCROLL_INIT);
9796 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9798 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9800 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9803 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9805 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9806 CE_OTHER_GETS_ENTERED, enter_side);
9807 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9808 CE_ENTERED_BY_PLAYER, enter_side);
9815 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9817 int jx = player->jx, jy = player->jy;
9818 int old_jx = jx, old_jy = jy;
9819 int moved = MF_NO_ACTION;
9822 if (!player->active)
9827 if (player->MovPos == 0)
9829 player->is_moving = FALSE;
9830 player->is_digging = FALSE;
9831 player->is_collecting = FALSE;
9832 player->is_snapping = FALSE;
9833 player->is_pushing = FALSE;
9839 if (!player->active || (!dx && !dy))
9844 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9852 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9853 player->move_delay + player->move_delay_value);
9856 #if USE_NEW_MOVE_DELAY
9857 if (player->move_delay > 0)
9859 if (!FrameReached(&player->move_delay, player->move_delay_value))
9863 printf("::: can NOT move\n");
9869 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9870 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9877 printf("::: COULD move now\n");
9880 #if USE_NEW_MOVE_DELAY
9881 player->move_delay = -1; /* set to "uninitialized" value */
9884 /* store if player is automatically moved to next field */
9885 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9887 /* remove the last programmed player action */
9888 player->programmed_action = 0;
9892 /* should only happen if pre-1.2 tape recordings are played */
9893 /* this is only for backward compatibility */
9895 int original_move_delay_value = player->move_delay_value;
9898 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9902 /* scroll remaining steps with finest movement resolution */
9903 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9905 while (player->MovPos)
9907 ScrollPlayer(player, SCROLL_GO_ON);
9908 ScrollScreen(NULL, SCROLL_GO_ON);
9910 #if USE_NEW_MOVE_DELAY
9911 AdvanceFrameAndPlayerCounters(player->index_nr);
9920 player->move_delay_value = original_move_delay_value;
9923 if (player->last_move_dir & MV_HORIZONTAL)
9925 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9926 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9930 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9931 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9937 if (moved & MF_MOVING && !ScreenMovPos &&
9938 (player == local_player || !options.network))
9940 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9941 int offset = (setup.scroll_delay ? 3 : 0);
9943 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9945 /* actual player has left the screen -- scroll in that direction */
9946 if (jx != old_jx) /* player has moved horizontally */
9947 scroll_x += (jx - old_jx);
9948 else /* player has moved vertically */
9949 scroll_y += (jy - old_jy);
9953 if (jx != old_jx) /* player has moved horizontally */
9955 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9956 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9957 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9959 /* don't scroll over playfield boundaries */
9960 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9961 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9963 /* don't scroll more than one field at a time */
9964 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9966 /* don't scroll against the player's moving direction */
9967 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9968 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9969 scroll_x = old_scroll_x;
9971 else /* player has moved vertically */
9973 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9974 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9975 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9977 /* don't scroll over playfield boundaries */
9978 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9979 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9981 /* don't scroll more than one field at a time */
9982 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9984 /* don't scroll against the player's moving direction */
9985 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9986 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9987 scroll_y = old_scroll_y;
9991 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9993 if (!options.network && !AllPlayersInVisibleScreen())
9995 scroll_x = old_scroll_x;
9996 scroll_y = old_scroll_y;
10000 ScrollScreen(player, SCROLL_INIT);
10001 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10008 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10010 if (!(moved & MF_MOVING) && !player->is_pushing)
10015 player->StepFrame = 0;
10017 if (moved & MF_MOVING)
10020 printf("::: REALLY moves now\n");
10023 if (old_jx != jx && old_jy == jy)
10024 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10025 else if (old_jx == jx && old_jy != jy)
10026 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10028 DrawLevelField(jx, jy); /* for "crumbled sand" */
10030 player->last_move_dir = player->MovDir;
10031 player->is_moving = TRUE;
10033 player->is_snapping = FALSE;
10037 player->is_switching = FALSE;
10040 player->is_dropping = FALSE;
10044 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10047 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10050 int move_direction = player->MovDir;
10052 int enter_side = MV_DIR_OPPOSITE(move_direction);
10053 int leave_side = move_direction;
10055 static int trigger_sides[4][2] =
10057 /* enter side leave side */
10058 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10059 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10060 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10061 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10063 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10064 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10066 int old_element = Feld[old_jx][old_jy];
10067 int new_element = Feld[jx][jy];
10070 /* !!! TEST ONLY !!! */
10071 if (IS_CUSTOM_ELEMENT(old_element))
10072 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10074 player->index_bit, leave_side);
10076 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10077 CE_OTHER_GETS_LEFT,
10078 player->index_bit, leave_side);
10080 if (IS_CUSTOM_ELEMENT(new_element))
10081 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10082 player->index_bit, enter_side);
10084 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10085 CE_OTHER_GETS_ENTERED,
10086 player->index_bit, enter_side);
10096 CheckGravityMovementWhenNotMoving(player);
10099 player->last_move_dir = MV_NO_MOVING;
10101 player->is_moving = FALSE;
10103 #if USE_NEW_MOVE_STYLE
10104 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10105 /* ensure that the player is also allowed to move in the next frame */
10106 /* (currently, the player is forced to wait eight frames before he can try
10109 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10110 player->move_delay = 0; /* allow direct movement in the next frame */
10114 #if USE_NEW_MOVE_DELAY
10115 if (player->move_delay == -1) /* not yet initialized by DigField() */
10116 player->move_delay = player->move_delay_value;
10119 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10121 TestIfHeroTouchesBadThing(jx, jy);
10122 TestIfPlayerTouchesCustomElement(jx, jy);
10125 if (!player->active)
10126 RemoveHero(player);
10131 void ScrollPlayer(struct PlayerInfo *player, int mode)
10133 int jx = player->jx, jy = player->jy;
10134 int last_jx = player->last_jx, last_jy = player->last_jy;
10135 int move_stepsize = TILEX / player->move_delay_value;
10137 if (!player->active || !player->MovPos)
10140 if (mode == SCROLL_INIT)
10142 player->actual_frame_counter = FrameCounter;
10143 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10146 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10148 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10149 player->block_delay);
10152 #if USE_NEW_BLOCK_STYLE
10155 if (player->block_delay <= 0)
10156 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10159 if (player->block_delay > 0 &&
10160 Feld[last_jx][last_jy] == EL_EMPTY)
10162 int last_field_block_delay = player->block_delay;
10164 #if USE_BLOCK_DELAY_BUGFIX
10165 /* when blocking enabled, correct block delay for fast movement */
10166 if (player->block_delay > 1 &&
10167 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10168 last_field_block_delay = player->move_delay_value;
10171 #if USE_GRAVITY_BUGFIX_2
10172 /* when blocking enabled, correct block delay for gravity movement */
10173 if (player->block_delay > 1 &&
10174 game.gravity && player->MovDir == MV_UP)
10175 last_field_block_delay = player->move_delay_value - 1;
10178 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10179 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10182 #if USE_NEW_MOVE_STYLE
10183 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10184 player->block_last_field) &&
10185 Feld[last_jx][last_jy] == EL_EMPTY)
10186 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10188 if (Feld[last_jx][last_jy] == EL_EMPTY)
10189 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10194 DrawPlayer(player);
10199 else if (!FrameReached(&player->actual_frame_counter, 1))
10202 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10203 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10205 #if USE_NEW_BLOCK_STYLE
10207 if (!player->block_last_field &&
10208 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10210 RemoveField(last_jx, last_jy);
10212 Feld[last_jx][last_jy] = EL_EMPTY;
10216 /* before DrawPlayer() to draw correct player graphic for this case */
10217 if (player->MovPos == 0)
10218 CheckGravityMovement(player);
10221 DrawPlayer(player); /* needed here only to cleanup last field */
10224 if (player->MovPos == 0) /* player reached destination field */
10227 if (player->move_delay_reset_counter > 0)
10229 player->move_delay_reset_counter--;
10231 if (player->move_delay_reset_counter == 0)
10233 /* continue with normal speed after quickly moving through gate */
10234 HALVE_PLAYER_SPEED(player);
10236 /* be able to make the next move without delay */
10237 player->move_delay = 0;
10241 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10243 /* continue with normal speed after quickly moving through gate */
10244 HALVE_PLAYER_SPEED(player);
10246 /* be able to make the next move without delay */
10247 player->move_delay = 0;
10251 #if USE_NEW_BLOCK_STYLE
10253 if (player->block_last_field &&
10254 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10256 RemoveField(last_jx, last_jy);
10258 Feld[last_jx][last_jy] = EL_EMPTY;
10262 player->last_jx = jx;
10263 player->last_jy = jy;
10265 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10266 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10267 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10269 DrawPlayer(player); /* needed here only to cleanup last field */
10270 RemoveHero(player);
10272 if (local_player->friends_still_needed == 0 ||
10273 IS_SP_ELEMENT(Feld[jx][jy]))
10274 player->LevelSolved = player->GameOver = TRUE;
10278 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10279 /* this breaks one level: "machine", level 000 */
10281 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10284 int move_direction = player->MovDir;
10286 int enter_side = MV_DIR_OPPOSITE(move_direction);
10287 int leave_side = move_direction;
10289 static int trigger_sides[4][2] =
10291 /* enter side leave side */
10292 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10293 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10294 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10295 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10297 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10298 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10300 int old_jx = last_jx;
10301 int old_jy = last_jy;
10302 int old_element = Feld[old_jx][old_jy];
10303 int new_element = Feld[jx][jy];
10306 /* !!! TEST ONLY !!! */
10307 if (IS_CUSTOM_ELEMENT(old_element))
10308 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10310 player->index_bit, leave_side);
10312 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10313 CE_OTHER_GETS_LEFT,
10314 player->index_bit, leave_side);
10316 if (IS_CUSTOM_ELEMENT(new_element))
10317 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10318 player->index_bit, enter_side);
10320 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10321 CE_OTHER_GETS_ENTERED,
10322 player->index_bit, enter_side);
10328 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10330 TestIfHeroTouchesBadThing(jx, jy);
10331 TestIfPlayerTouchesCustomElement(jx, jy);
10334 /* needed because pushed element has not yet reached its destination,
10335 so it would trigger a change event at its previous field location */
10336 if (!player->is_pushing)
10338 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10341 if (!player->active)
10342 RemoveHero(player);
10345 if (level.use_step_counter)
10355 if (TimeLeft <= 10 && setup.time_limit)
10356 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10358 DrawGameValue_Time(TimeLeft);
10360 if (!TimeLeft && setup.time_limit)
10361 for (i = 0; i < MAX_PLAYERS; i++)
10362 KillHero(&stored_player[i]);
10364 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10365 DrawGameValue_Time(TimePlayed);
10368 if (tape.single_step && tape.recording && !tape.pausing &&
10369 !player->programmed_action)
10370 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10374 void ScrollScreen(struct PlayerInfo *player, int mode)
10376 static unsigned long screen_frame_counter = 0;
10378 if (mode == SCROLL_INIT)
10380 /* set scrolling step size according to actual player's moving speed */
10381 ScrollStepSize = TILEX / player->move_delay_value;
10383 screen_frame_counter = FrameCounter;
10384 ScreenMovDir = player->MovDir;
10385 ScreenMovPos = player->MovPos;
10386 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10389 else if (!FrameReached(&screen_frame_counter, 1))
10394 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10395 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10396 redraw_mask |= REDRAW_FIELD;
10399 ScreenMovDir = MV_NO_MOVING;
10402 void TestIfPlayerTouchesCustomElement(int x, int y)
10404 static int xy[4][2] =
10411 static int trigger_sides[4][2] =
10413 /* center side border side */
10414 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10415 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10416 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10417 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10419 static int touch_dir[4] =
10421 MV_LEFT | MV_RIGHT,
10426 int center_element = Feld[x][y]; /* should always be non-moving! */
10429 for (i = 0; i < NUM_DIRECTIONS; i++)
10431 int xx = x + xy[i][0];
10432 int yy = y + xy[i][1];
10433 int center_side = trigger_sides[i][0];
10434 int border_side = trigger_sides[i][1];
10435 int border_element;
10437 if (!IN_LEV_FIELD(xx, yy))
10440 if (IS_PLAYER(x, y))
10442 struct PlayerInfo *player = PLAYERINFO(x, y);
10444 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10445 border_element = Feld[xx][yy]; /* may be moving! */
10446 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10447 border_element = Feld[xx][yy];
10448 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10449 border_element = MovingOrBlocked2Element(xx, yy);
10451 continue; /* center and border element do not touch */
10454 /* !!! TEST ONLY !!! */
10455 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10456 player->index_bit, border_side);
10457 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10458 CE_OTHER_GETS_TOUCHED,
10459 player->index_bit, border_side);
10461 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10462 CE_OTHER_GETS_TOUCHED,
10463 player->index_bit, border_side);
10464 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10465 player->index_bit, border_side);
10468 else if (IS_PLAYER(xx, yy))
10470 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10472 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10474 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10475 continue; /* center and border element do not touch */
10479 /* !!! TEST ONLY !!! */
10480 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10481 player->index_bit, center_side);
10482 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10483 CE_OTHER_GETS_TOUCHED,
10484 player->index_bit, center_side);
10486 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10487 CE_OTHER_GETS_TOUCHED,
10488 player->index_bit, center_side);
10489 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10490 player->index_bit, center_side);
10498 void TestIfElementTouchesCustomElement(int x, int y)
10500 static int xy[4][2] =
10507 static int trigger_sides[4][2] =
10509 /* center side border side */
10510 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10511 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10512 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10513 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10515 static int touch_dir[4] =
10517 MV_LEFT | MV_RIGHT,
10522 boolean change_center_element = FALSE;
10523 int center_element_change_page = 0;
10524 int center_element = Feld[x][y]; /* should always be non-moving! */
10525 int border_trigger_element = EL_UNDEFINED;
10528 for (i = 0; i < NUM_DIRECTIONS; i++)
10530 int xx = x + xy[i][0];
10531 int yy = y + xy[i][1];
10532 int center_side = trigger_sides[i][0];
10533 int border_side = trigger_sides[i][1];
10534 int border_element;
10536 if (!IN_LEV_FIELD(xx, yy))
10539 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10540 border_element = Feld[xx][yy]; /* may be moving! */
10541 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10542 border_element = Feld[xx][yy];
10543 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10544 border_element = MovingOrBlocked2Element(xx, yy);
10546 continue; /* center and border element do not touch */
10548 /* check for change of center element (but change it only once) */
10549 if (IS_CUSTOM_ELEMENT(center_element) &&
10550 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10551 !change_center_element)
10553 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10555 struct ElementChangeInfo *change =
10556 &element_info[center_element].change_page[j];
10558 if (change->can_change &&
10559 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10560 change->trigger_side & border_side &&
10562 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10564 change->trigger_element == border_element
10568 change_center_element = TRUE;
10569 center_element_change_page = j;
10570 border_trigger_element = border_element;
10577 /* check for change of border element */
10578 if (IS_CUSTOM_ELEMENT(border_element) &&
10579 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10581 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10583 struct ElementChangeInfo *change =
10584 &element_info[border_element].change_page[j];
10586 if (change->can_change &&
10587 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10588 change->trigger_side & center_side &&
10590 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10592 change->trigger_element == center_element
10597 printf("::: border_element %d, %d\n", x, y);
10600 CheckElementChangeByPage(xx, yy, border_element, center_element,
10601 CE_OTHER_IS_TOUCHING, j);
10608 if (change_center_element)
10611 printf("::: center_element %d, %d\n", x, y);
10614 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10615 CE_OTHER_IS_TOUCHING, center_element_change_page);
10619 void TestIfElementHitsCustomElement(int x, int y, int direction)
10621 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10622 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10623 int hitx = x + dx, hity = y + dy;
10624 int hitting_element = Feld[x][y];
10625 int touched_element;
10627 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10628 !IS_FREE(hitx, hity) &&
10629 (!IS_MOVING(hitx, hity) ||
10630 MovDir[hitx][hity] != direction ||
10631 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10634 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10638 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10642 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10643 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10645 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10646 CE_HITTING_SOMETHING, direction);
10648 if (IN_LEV_FIELD(hitx, hity))
10650 int opposite_direction = MV_DIR_OPPOSITE(direction);
10651 int hitting_side = direction;
10652 int touched_side = opposite_direction;
10654 int touched_element = MovingOrBlocked2Element(hitx, hity);
10657 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10658 MovDir[hitx][hity] != direction ||
10659 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10668 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10669 CE_HIT_BY_SOMETHING, opposite_direction);
10671 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10672 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10674 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10676 struct ElementChangeInfo *change =
10677 &element_info[hitting_element].change_page[i];
10679 if (change->can_change &&
10680 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10681 change->trigger_side & touched_side &&
10684 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10686 change->trigger_element == touched_element
10690 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10691 CE_OTHER_IS_HITTING, i);
10697 if (IS_CUSTOM_ELEMENT(touched_element) &&
10698 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10700 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10702 struct ElementChangeInfo *change =
10703 &element_info[touched_element].change_page[i];
10705 if (change->can_change &&
10706 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10707 change->trigger_side & hitting_side &&
10709 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10711 change->trigger_element == hitting_element
10715 CheckElementChangeByPage(hitx, hity, touched_element,
10716 hitting_element, CE_OTHER_GETS_HIT, i);
10726 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10728 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10729 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10730 int hitx = x + dx, hity = y + dy;
10731 int hitting_element = Feld[x][y];
10732 int touched_element;
10734 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10735 !IS_FREE(hitx, hity) &&
10736 (!IS_MOVING(hitx, hity) ||
10737 MovDir[hitx][hity] != direction ||
10738 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10741 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10745 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10749 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10750 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10752 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10753 EP_CAN_SMASH_EVERYTHING, direction);
10755 if (IN_LEV_FIELD(hitx, hity))
10757 int opposite_direction = MV_DIR_OPPOSITE(direction);
10758 int hitting_side = direction;
10759 int touched_side = opposite_direction;
10761 int touched_element = MovingOrBlocked2Element(hitx, hity);
10764 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10765 MovDir[hitx][hity] != direction ||
10766 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10775 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10776 CE_SMASHED_BY_SOMETHING, opposite_direction);
10778 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10779 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10781 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10783 struct ElementChangeInfo *change =
10784 &element_info[hitting_element].change_page[i];
10786 if (change->can_change &&
10787 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10788 change->trigger_side & touched_side &&
10791 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10793 change->trigger_element == touched_element
10797 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10798 CE_OTHER_IS_SMASHING, i);
10804 if (IS_CUSTOM_ELEMENT(touched_element) &&
10805 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10807 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10809 struct ElementChangeInfo *change =
10810 &element_info[touched_element].change_page[i];
10812 if (change->can_change &&
10813 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10814 change->trigger_side & hitting_side &&
10816 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10818 change->trigger_element == hitting_element
10822 CheckElementChangeByPage(hitx, hity, touched_element,
10823 hitting_element, CE_OTHER_GETS_SMASHED,i);
10833 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10835 int i, kill_x = -1, kill_y = -1;
10836 int bad_element = -1;
10837 static int test_xy[4][2] =
10844 static int test_dir[4] =
10852 for (i = 0; i < NUM_DIRECTIONS; i++)
10854 int test_x, test_y, test_move_dir, test_element;
10856 test_x = good_x + test_xy[i][0];
10857 test_y = good_y + test_xy[i][1];
10859 if (!IN_LEV_FIELD(test_x, test_y))
10863 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10866 test_element = Feld[test_x][test_y];
10868 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10871 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10872 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10874 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10875 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10879 bad_element = test_element;
10885 if (kill_x != -1 || kill_y != -1)
10887 if (IS_PLAYER(good_x, good_y))
10889 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10892 if (player->shield_deadly_time_left > 0 &&
10893 !IS_INDESTRUCTIBLE(bad_element))
10894 Bang(kill_x, kill_y);
10895 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10898 if (player->shield_deadly_time_left > 0)
10899 Bang(kill_x, kill_y);
10900 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10905 Bang(good_x, good_y);
10909 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10911 int i, kill_x = -1, kill_y = -1;
10912 int bad_element = Feld[bad_x][bad_y];
10913 static int test_xy[4][2] =
10920 static int touch_dir[4] =
10922 MV_LEFT | MV_RIGHT,
10927 static int test_dir[4] =
10935 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10938 for (i = 0; i < NUM_DIRECTIONS; i++)
10940 int test_x, test_y, test_move_dir, test_element;
10942 test_x = bad_x + test_xy[i][0];
10943 test_y = bad_y + test_xy[i][1];
10944 if (!IN_LEV_FIELD(test_x, test_y))
10948 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10950 test_element = Feld[test_x][test_y];
10952 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10953 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10955 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10956 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10958 /* good thing is player or penguin that does not move away */
10959 if (IS_PLAYER(test_x, test_y))
10961 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10963 if (bad_element == EL_ROBOT && player->is_moving)
10964 continue; /* robot does not kill player if he is moving */
10966 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10968 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10969 continue; /* center and border element do not touch */
10976 else if (test_element == EL_PENGUIN)
10985 if (kill_x != -1 || kill_y != -1)
10987 if (IS_PLAYER(kill_x, kill_y))
10989 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10992 if (player->shield_deadly_time_left > 0 &&
10993 !IS_INDESTRUCTIBLE(bad_element))
10994 Bang(bad_x, bad_y);
10995 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10998 if (player->shield_deadly_time_left > 0)
10999 Bang(bad_x, bad_y);
11000 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11005 Bang(kill_x, kill_y);
11009 void TestIfHeroTouchesBadThing(int x, int y)
11011 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11014 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11016 TestIfGoodThingHitsBadThing(x, y, move_dir);
11019 void TestIfBadThingTouchesHero(int x, int y)
11021 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11024 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11026 TestIfBadThingHitsGoodThing(x, y, move_dir);
11029 void TestIfFriendTouchesBadThing(int x, int y)
11031 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11034 void TestIfBadThingTouchesFriend(int x, int y)
11036 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11039 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11041 int i, kill_x = bad_x, kill_y = bad_y;
11042 static int xy[4][2] =
11050 for (i = 0; i < NUM_DIRECTIONS; i++)
11054 x = bad_x + xy[i][0];
11055 y = bad_y + xy[i][1];
11056 if (!IN_LEV_FIELD(x, y))
11059 element = Feld[x][y];
11060 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11061 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11069 if (kill_x != bad_x || kill_y != bad_y)
11070 Bang(bad_x, bad_y);
11073 void KillHero(struct PlayerInfo *player)
11075 int jx = player->jx, jy = player->jy;
11077 if (!player->active)
11080 /* remove accessible field at the player's position */
11081 Feld[jx][jy] = EL_EMPTY;
11083 /* deactivate shield (else Bang()/Explode() would not work right) */
11084 player->shield_normal_time_left = 0;
11085 player->shield_deadly_time_left = 0;
11091 static void KillHeroUnlessEnemyProtected(int x, int y)
11093 if (!PLAYER_ENEMY_PROTECTED(x, y))
11094 KillHero(PLAYERINFO(x, y));
11097 static void KillHeroUnlessExplosionProtected(int x, int y)
11099 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11100 KillHero(PLAYERINFO(x, y));
11103 void BuryHero(struct PlayerInfo *player)
11105 int jx = player->jx, jy = player->jy;
11107 if (!player->active)
11111 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11113 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11115 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11117 player->GameOver = TRUE;
11118 RemoveHero(player);
11121 void RemoveHero(struct PlayerInfo *player)
11123 int jx = player->jx, jy = player->jy;
11124 int i, found = FALSE;
11126 player->present = FALSE;
11127 player->active = FALSE;
11129 if (!ExplodeField[jx][jy])
11130 StorePlayer[jx][jy] = 0;
11132 for (i = 0; i < MAX_PLAYERS; i++)
11133 if (stored_player[i].active)
11137 AllPlayersGone = TRUE;
11144 =============================================================================
11145 checkDiagonalPushing()
11146 -----------------------------------------------------------------------------
11147 check if diagonal input device direction results in pushing of object
11148 (by checking if the alternative direction is walkable, diggable, ...)
11149 =============================================================================
11152 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11153 int x, int y, int real_dx, int real_dy)
11155 int jx, jy, dx, dy, xx, yy;
11157 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11160 /* diagonal direction: check alternative direction */
11165 xx = jx + (dx == 0 ? real_dx : 0);
11166 yy = jy + (dy == 0 ? real_dy : 0);
11168 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11172 =============================================================================
11174 -----------------------------------------------------------------------------
11175 x, y: field next to player (non-diagonal) to try to dig to
11176 real_dx, real_dy: direction as read from input device (can be diagonal)
11177 =============================================================================
11180 int DigField(struct PlayerInfo *player,
11181 int oldx, int oldy, int x, int y,
11182 int real_dx, int real_dy, int mode)
11185 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11187 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11188 boolean player_was_pushing = player->is_pushing;
11189 int jx = oldx, jy = oldy;
11190 int dx = x - jx, dy = y - jy;
11191 int nextx = x + dx, nexty = y + dy;
11192 int move_direction = (dx == -1 ? MV_LEFT :
11193 dx == +1 ? MV_RIGHT :
11195 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11196 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11198 int dig_side = MV_DIR_OPPOSITE(move_direction);
11200 static int trigger_sides[4] =
11202 CH_SIDE_RIGHT, /* moving left */
11203 CH_SIDE_LEFT, /* moving right */
11204 CH_SIDE_BOTTOM, /* moving up */
11205 CH_SIDE_TOP, /* moving down */
11207 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11209 int old_element = Feld[jx][jy];
11212 if (is_player) /* function can also be called by EL_PENGUIN */
11214 if (player->MovPos == 0)
11216 player->is_digging = FALSE;
11217 player->is_collecting = FALSE;
11220 if (player->MovPos == 0) /* last pushing move finished */
11221 player->is_pushing = FALSE;
11223 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11225 player->is_switching = FALSE;
11226 #if USE_NEW_PUSH_DELAY
11227 player->push_delay = -1;
11229 player->push_delay = 0;
11232 return MF_NO_ACTION;
11236 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11237 return MF_NO_ACTION;
11242 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11244 if (IS_TUBE(Feld[jx][jy]) ||
11245 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11249 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11250 int tube_leave_directions[][2] =
11252 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11253 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11254 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11255 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11256 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11257 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11258 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11259 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11260 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11261 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11262 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11263 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11266 while (tube_leave_directions[i][0] != tube_element)
11269 if (tube_leave_directions[i][0] == -1) /* should not happen */
11273 if (!(tube_leave_directions[i][1] & move_direction))
11274 return MF_NO_ACTION; /* tube has no opening in this direction */
11279 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11280 old_element = Back[jx][jy];
11284 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11285 return MF_NO_ACTION; /* field has no opening in this direction */
11287 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11288 return MF_NO_ACTION; /* field has no opening in this direction */
11290 element = Feld[x][y];
11292 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11293 return MF_NO_ACTION;
11295 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11296 game.engine_version >= VERSION_IDENT(2,2,0,0))
11297 return MF_NO_ACTION;
11300 if (game.gravity && is_player && !player->is_auto_moving &&
11301 canFallDown(player) && move_direction != MV_DOWN &&
11302 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11303 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11307 if (element == EL_EMPTY_SPACE &&
11308 game.gravity && !player->is_auto_moving &&
11309 canFallDown(player) && move_direction != MV_DOWN)
11310 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11316 case EL_SP_PORT_LEFT:
11317 case EL_SP_PORT_RIGHT:
11318 case EL_SP_PORT_UP:
11319 case EL_SP_PORT_DOWN:
11320 case EL_SP_PORT_HORIZONTAL:
11321 case EL_SP_PORT_VERTICAL:
11322 case EL_SP_PORT_ANY:
11323 case EL_SP_GRAVITY_PORT_LEFT:
11324 case EL_SP_GRAVITY_PORT_RIGHT:
11325 case EL_SP_GRAVITY_PORT_UP:
11326 case EL_SP_GRAVITY_PORT_DOWN:
11328 if (!canEnterSupaplexPort(x, y, dx, dy))
11329 return MF_NO_ACTION;
11332 element != EL_SP_PORT_LEFT &&
11333 element != EL_SP_GRAVITY_PORT_LEFT &&
11334 element != EL_SP_PORT_HORIZONTAL &&
11335 element != EL_SP_PORT_ANY) ||
11337 element != EL_SP_PORT_RIGHT &&
11338 element != EL_SP_GRAVITY_PORT_RIGHT &&
11339 element != EL_SP_PORT_HORIZONTAL &&
11340 element != EL_SP_PORT_ANY) ||
11342 element != EL_SP_PORT_UP &&
11343 element != EL_SP_GRAVITY_PORT_UP &&
11344 element != EL_SP_PORT_VERTICAL &&
11345 element != EL_SP_PORT_ANY) ||
11347 element != EL_SP_PORT_DOWN &&
11348 element != EL_SP_GRAVITY_PORT_DOWN &&
11349 element != EL_SP_PORT_VERTICAL &&
11350 element != EL_SP_PORT_ANY) ||
11351 !IN_LEV_FIELD(nextx, nexty) ||
11352 !IS_FREE(nextx, nexty))
11353 return MF_NO_ACTION;
11356 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11357 element == EL_SP_GRAVITY_PORT_RIGHT ||
11358 element == EL_SP_GRAVITY_PORT_UP ||
11359 element == EL_SP_GRAVITY_PORT_DOWN)
11360 game.gravity = !game.gravity;
11362 /* automatically move to the next field with double speed */
11363 player->programmed_action = move_direction;
11365 if (player->move_delay_reset_counter == 0)
11367 player->move_delay_reset_counter = 2; /* two double speed steps */
11369 DOUBLE_PLAYER_SPEED(player);
11372 player->move_delay_reset_counter = 2;
11374 DOUBLE_PLAYER_SPEED(player);
11378 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11381 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11387 case EL_TUBE_VERTICAL:
11388 case EL_TUBE_HORIZONTAL:
11389 case EL_TUBE_VERTICAL_LEFT:
11390 case EL_TUBE_VERTICAL_RIGHT:
11391 case EL_TUBE_HORIZONTAL_UP:
11392 case EL_TUBE_HORIZONTAL_DOWN:
11393 case EL_TUBE_LEFT_UP:
11394 case EL_TUBE_LEFT_DOWN:
11395 case EL_TUBE_RIGHT_UP:
11396 case EL_TUBE_RIGHT_DOWN:
11399 int tube_enter_directions[][2] =
11401 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11402 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11403 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11404 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11405 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11406 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11407 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11408 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11409 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11410 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11411 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11412 { -1, MV_NO_MOVING }
11415 while (tube_enter_directions[i][0] != element)
11418 if (tube_enter_directions[i][0] == -1) /* should not happen */
11422 if (!(tube_enter_directions[i][1] & move_direction))
11423 return MF_NO_ACTION; /* tube has no opening in this direction */
11425 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11433 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11435 if (IS_WALKABLE(element))
11438 int sound_element = SND_ELEMENT(element);
11439 int sound_action = ACTION_WALKING;
11442 if (!ACCESS_FROM(element, opposite_direction))
11443 return MF_NO_ACTION; /* field not accessible from this direction */
11447 if (element == EL_EMPTY_SPACE &&
11448 game.gravity && !player->is_auto_moving &&
11449 canFallDown(player) && move_direction != MV_DOWN)
11450 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11453 if (IS_RND_GATE(element))
11455 if (!player->key[RND_GATE_NR(element)])
11456 return MF_NO_ACTION;
11458 else if (IS_RND_GATE_GRAY(element))
11460 if (!player->key[RND_GATE_GRAY_NR(element)])
11461 return MF_NO_ACTION;
11463 else if (element == EL_EXIT_OPEN ||
11464 element == EL_SP_EXIT_OPEN ||
11465 element == EL_SP_EXIT_OPENING)
11467 sound_action = ACTION_PASSING; /* player is passing exit */
11469 else if (element == EL_EMPTY)
11471 sound_action = ACTION_MOVING; /* nothing to walk on */
11474 /* play sound from background or player, whatever is available */
11475 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11476 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11478 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11483 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11485 else if (IS_PASSABLE(element))
11489 if (!canPassField(x, y, move_direction))
11490 return MF_NO_ACTION;
11495 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11496 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11497 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11498 return MF_NO_ACTION;
11500 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11501 return MF_NO_ACTION;
11506 if (!ACCESS_FROM(element, opposite_direction))
11507 return MF_NO_ACTION; /* field not accessible from this direction */
11509 if (IS_CUSTOM_ELEMENT(element) &&
11510 !ACCESS_FROM(element, opposite_direction))
11511 return MF_NO_ACTION; /* field not accessible from this direction */
11515 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11516 return MF_NO_ACTION;
11521 if (IS_EM_GATE(element))
11523 if (!player->key[EM_GATE_NR(element)])
11524 return MF_NO_ACTION;
11526 else if (IS_EM_GATE_GRAY(element))
11528 if (!player->key[EM_GATE_GRAY_NR(element)])
11529 return MF_NO_ACTION;
11531 else if (IS_SP_PORT(element))
11533 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11534 element == EL_SP_GRAVITY_PORT_RIGHT ||
11535 element == EL_SP_GRAVITY_PORT_UP ||
11536 element == EL_SP_GRAVITY_PORT_DOWN)
11537 game.gravity = !game.gravity;
11538 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11539 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11540 element == EL_SP_GRAVITY_ON_PORT_UP ||
11541 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11542 game.gravity = TRUE;
11543 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11544 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11545 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11546 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11547 game.gravity = FALSE;
11550 /* automatically move to the next field with double speed */
11551 player->programmed_action = move_direction;
11553 if (player->move_delay_reset_counter == 0)
11555 player->move_delay_reset_counter = 2; /* two double speed steps */
11557 DOUBLE_PLAYER_SPEED(player);
11560 player->move_delay_reset_counter = 2;
11562 DOUBLE_PLAYER_SPEED(player);
11565 PlayLevelSoundAction(x, y, ACTION_PASSING);
11569 else if (IS_DIGGABLE(element))
11573 if (mode != DF_SNAP)
11576 GfxElement[x][y] = GFX_ELEMENT(element);
11579 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11581 player->is_digging = TRUE;
11584 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11586 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11587 player->index_bit, dig_side);
11590 if (mode == DF_SNAP)
11591 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11596 else if (IS_COLLECTIBLE(element))
11600 if (is_player && mode != DF_SNAP)
11602 GfxElement[x][y] = element;
11603 player->is_collecting = TRUE;
11606 if (element == EL_SPEED_PILL)
11607 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11608 else if (element == EL_EXTRA_TIME && level.time > 0)
11611 DrawGameValue_Time(TimeLeft);
11613 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11615 player->shield_normal_time_left += 10;
11616 if (element == EL_SHIELD_DEADLY)
11617 player->shield_deadly_time_left += 10;
11619 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11621 if (player->inventory_size < MAX_INVENTORY_SIZE)
11622 player->inventory_element[player->inventory_size++] = element;
11624 DrawGameValue_Dynamite(local_player->inventory_size);
11626 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11628 player->dynabomb_count++;
11629 player->dynabombs_left++;
11631 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11633 player->dynabomb_size++;
11635 else if (element == EL_DYNABOMB_INCREASE_POWER)
11637 player->dynabomb_xl = TRUE;
11639 else if (IS_KEY(element))
11641 player->key[KEY_NR(element)] = TRUE;
11643 DrawGameValue_Keys(player->key);
11645 redraw_mask |= REDRAW_DOOR_1;
11647 else if (IS_ENVELOPE(element))
11650 player->show_envelope = element;
11652 ShowEnvelope(element - EL_ENVELOPE_1);
11655 else if (IS_DROPPABLE(element) ||
11656 IS_THROWABLE(element)) /* can be collected and dropped */
11660 if (element_info[element].collect_count == 0)
11661 player->inventory_infinite_element = element;
11663 for (i = 0; i < element_info[element].collect_count; i++)
11664 if (player->inventory_size < MAX_INVENTORY_SIZE)
11665 player->inventory_element[player->inventory_size++] = element;
11667 DrawGameValue_Dynamite(local_player->inventory_size);
11669 else if (element_info[element].collect_count > 0)
11671 local_player->gems_still_needed -=
11672 element_info[element].collect_count;
11673 if (local_player->gems_still_needed < 0)
11674 local_player->gems_still_needed = 0;
11676 DrawGameValue_Emeralds(local_player->gems_still_needed);
11679 RaiseScoreElement(element);
11680 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11683 CheckTriggeredElementChangeByPlayer(x, y, element,
11684 CE_OTHER_GETS_COLLECTED,
11685 player->index_bit, dig_side);
11688 if (mode == DF_SNAP)
11689 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11694 else if (IS_PUSHABLE(element))
11696 if (mode == DF_SNAP && element != EL_BD_ROCK)
11697 return MF_NO_ACTION;
11699 if (CAN_FALL(element) && dy)
11700 return MF_NO_ACTION;
11702 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11703 !(element == EL_SPRING && level.use_spring_bug))
11704 return MF_NO_ACTION;
11707 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11708 ((move_direction & MV_VERTICAL &&
11709 ((element_info[element].move_pattern & MV_LEFT &&
11710 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11711 (element_info[element].move_pattern & MV_RIGHT &&
11712 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11713 (move_direction & MV_HORIZONTAL &&
11714 ((element_info[element].move_pattern & MV_UP &&
11715 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11716 (element_info[element].move_pattern & MV_DOWN &&
11717 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11718 return MF_NO_ACTION;
11722 /* do not push elements already moving away faster than player */
11723 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11724 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11725 return MF_NO_ACTION;
11727 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11728 return MF_NO_ACTION;
11734 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11736 if (player->push_delay_value == -1 || !player_was_pushing)
11737 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11739 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11741 if (player->push_delay_value == -1)
11742 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11745 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11747 if (player->push_delay_value == -1 || !player_was_pushing)
11748 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11751 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11753 if (!player->is_pushing)
11754 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11758 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11759 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11760 !player_is_pushing))
11761 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11764 if (!player->is_pushing &&
11765 game.engine_version >= VERSION_IDENT(2,2,0,7))
11766 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11770 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11771 player->push_delay, player->push_delay_value,
11772 FrameCounter, game.engine_version,
11773 player_was_pushing, player->is_pushing,
11774 element, element_info[element].token_name,
11775 GET_NEW_PUSH_DELAY(element));
11778 player->is_pushing = TRUE;
11780 if (!(IN_LEV_FIELD(nextx, nexty) &&
11781 (IS_FREE(nextx, nexty) ||
11782 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11783 IS_SB_ELEMENT(element)))))
11784 return MF_NO_ACTION;
11786 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11787 return MF_NO_ACTION;
11789 #if USE_NEW_PUSH_DELAY
11792 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11793 printf("::: ALERT: %d, %d [%d / %d]\n",
11794 player->push_delay, player->push_delay2,
11795 FrameCounter, FrameCounter / 50);
11798 if (player->push_delay == -1) /* new pushing; restart delay */
11799 player->push_delay = 0;
11801 if (player->push_delay == 0) /* new pushing; restart delay */
11802 player->push_delay = FrameCounter;
11805 #if USE_NEW_PUSH_DELAY
11807 if ( (player->push_delay > 0) != (!xxx_fr) )
11808 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11809 player->push_delay,
11810 xxx_pdv2, player->push_delay2, player->push_delay_value,
11811 FrameCounter, FrameCounter / 50);
11815 if (player->push_delay > 0 &&
11816 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11817 element != EL_SPRING && element != EL_BALLOON)
11820 if (player->push_delay < player->push_delay_value &&
11821 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11822 element != EL_SPRING && element != EL_BALLOON)
11826 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11827 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11828 element != EL_SPRING && element != EL_BALLOON)
11831 /* make sure that there is no move delay before next try to push */
11832 #if USE_NEW_MOVE_DELAY
11833 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11834 player->move_delay = 0;
11836 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11837 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11840 return MF_NO_ACTION;
11844 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11847 if (IS_SB_ELEMENT(element))
11849 if (element == EL_SOKOBAN_FIELD_FULL)
11851 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11852 local_player->sokobanfields_still_needed++;
11855 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11857 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11858 local_player->sokobanfields_still_needed--;
11861 Feld[x][y] = EL_SOKOBAN_OBJECT;
11863 if (Back[x][y] == Back[nextx][nexty])
11864 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11865 else if (Back[x][y] != 0)
11866 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11869 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11872 if (local_player->sokobanfields_still_needed == 0 &&
11873 game.emulation == EMU_SOKOBAN)
11875 player->LevelSolved = player->GameOver = TRUE;
11876 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11880 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11882 InitMovingField(x, y, move_direction);
11883 GfxAction[x][y] = ACTION_PUSHING;
11885 if (mode == DF_SNAP)
11886 ContinueMoving(x, y);
11888 MovPos[x][y] = (dx != 0 ? dx : dy);
11890 Pushed[x][y] = TRUE;
11891 Pushed[nextx][nexty] = TRUE;
11893 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11894 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11896 player->push_delay_value = -1; /* get new value later */
11898 #if USE_PUSH_BUGFIX
11899 /* now: check for element change _after_ element has been pushed! */
11901 if (game.use_bug_change_when_pushing)
11903 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11906 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11907 player->index_bit, dig_side);
11908 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11909 player->index_bit, dig_side);
11915 /* check for element change _after_ element has been pushed! */
11919 /* !!! TEST ONLY !!! */
11920 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11921 player->index_bit, dig_side);
11922 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11923 player->index_bit, dig_side);
11925 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11926 player->index_bit, dig_side);
11927 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11928 player->index_bit, dig_side);
11936 else if (IS_SWITCHABLE(element))
11938 if (PLAYER_SWITCHING(player, x, y))
11940 CheckTriggeredElementChangeByPlayer(x,y, element,
11941 CE_OTHER_GETS_PRESSED,
11942 player->index_bit, dig_side);
11947 player->is_switching = TRUE;
11948 player->switch_x = x;
11949 player->switch_y = y;
11951 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11953 if (element == EL_ROBOT_WHEEL)
11955 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11959 DrawLevelField(x, y);
11961 else if (element == EL_SP_TERMINAL)
11965 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11967 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11969 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11970 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11973 else if (IS_BELT_SWITCH(element))
11975 ToggleBeltSwitch(x, y);
11977 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11978 element == EL_SWITCHGATE_SWITCH_DOWN)
11980 ToggleSwitchgateSwitch(x, y);
11982 else if (element == EL_LIGHT_SWITCH ||
11983 element == EL_LIGHT_SWITCH_ACTIVE)
11985 ToggleLightSwitch(x, y);
11988 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11989 SND_LIGHT_SWITCH_ACTIVATING :
11990 SND_LIGHT_SWITCH_DEACTIVATING);
11993 else if (element == EL_TIMEGATE_SWITCH)
11995 ActivateTimegateSwitch(x, y);
11997 else if (element == EL_BALLOON_SWITCH_LEFT ||
11998 element == EL_BALLOON_SWITCH_RIGHT ||
11999 element == EL_BALLOON_SWITCH_UP ||
12000 element == EL_BALLOON_SWITCH_DOWN ||
12001 element == EL_BALLOON_SWITCH_ANY)
12003 if (element == EL_BALLOON_SWITCH_ANY)
12004 game.balloon_dir = move_direction;
12006 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12007 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12008 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12009 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12012 else if (element == EL_LAMP)
12014 Feld[x][y] = EL_LAMP_ACTIVE;
12015 local_player->lights_still_needed--;
12017 ResetGfxAnimation(x, y);
12018 DrawLevelField(x, y);
12020 else if (element == EL_TIME_ORB_FULL)
12022 Feld[x][y] = EL_TIME_ORB_EMPTY;
12024 DrawGameValue_Time(TimeLeft);
12026 ResetGfxAnimation(x, y);
12027 DrawLevelField(x, y);
12030 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12034 CheckTriggeredElementChangeByPlayer(x, y, element,
12035 CE_OTHER_IS_SWITCHING,
12036 player->index_bit, dig_side);
12038 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12039 player->index_bit, dig_side);
12045 if (!PLAYER_SWITCHING(player, x, y))
12047 player->is_switching = TRUE;
12048 player->switch_x = x;
12049 player->switch_y = y;
12052 /* !!! TEST ONLY !!! */
12053 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12054 player->index_bit, dig_side);
12055 CheckTriggeredElementChangeByPlayer(x, y, element,
12056 CE_OTHER_IS_SWITCHING,
12057 player->index_bit, dig_side);
12059 CheckTriggeredElementChangeByPlayer(x, y, element,
12060 CE_OTHER_IS_SWITCHING,
12061 player->index_bit, dig_side);
12062 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12063 player->index_bit, dig_side);
12068 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12069 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12070 player->index_bit, dig_side);
12071 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12072 player->index_bit, dig_side);
12074 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12075 player->index_bit, dig_side);
12076 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12077 player->index_bit, dig_side);
12081 return MF_NO_ACTION;
12084 #if USE_NEW_PUSH_DELAY
12085 player->push_delay = -1;
12087 player->push_delay = 0;
12090 if (Feld[x][y] != element) /* really digged/collected something */
12091 player->is_collecting = !player->is_digging;
12096 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12098 int jx = player->jx, jy = player->jy;
12099 int x = jx + dx, y = jy + dy;
12100 int snap_direction = (dx == -1 ? MV_LEFT :
12101 dx == +1 ? MV_RIGHT :
12103 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12106 if (player->MovPos != 0)
12109 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12113 if (!player->active || !IN_LEV_FIELD(x, y))
12121 if (player->MovPos == 0)
12122 player->is_pushing = FALSE;
12124 player->is_snapping = FALSE;
12126 if (player->MovPos == 0)
12128 player->is_moving = FALSE;
12129 player->is_digging = FALSE;
12130 player->is_collecting = FALSE;
12136 if (player->is_snapping)
12139 player->MovDir = snap_direction;
12142 if (player->MovPos == 0)
12145 player->is_moving = FALSE;
12146 player->is_digging = FALSE;
12147 player->is_collecting = FALSE;
12150 player->is_dropping = FALSE;
12152 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12155 player->is_snapping = TRUE;
12158 if (player->MovPos == 0)
12161 player->is_moving = FALSE;
12162 player->is_digging = FALSE;
12163 player->is_collecting = FALSE;
12167 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12168 DrawLevelField(player->last_jx, player->last_jy);
12171 DrawLevelField(x, y);
12180 boolean DropElement(struct PlayerInfo *player)
12182 int old_element, new_element;
12183 int dropx = player->jx, dropy = player->jy;
12184 int drop_direction = player->MovDir;
12186 int drop_side = drop_direction;
12188 static int trigger_sides[4] =
12190 CH_SIDE_LEFT, /* dropping left */
12191 CH_SIDE_RIGHT, /* dropping right */
12192 CH_SIDE_TOP, /* dropping up */
12193 CH_SIDE_BOTTOM, /* dropping down */
12195 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12197 int drop_element = (player->inventory_size > 0 ?
12198 player->inventory_element[player->inventory_size - 1] :
12199 player->inventory_infinite_element != EL_UNDEFINED ?
12200 player->inventory_infinite_element :
12201 player->dynabombs_left > 0 ?
12202 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12205 if (IS_THROWABLE(drop_element))
12207 dropx += GET_DX_FROM_DIR(drop_direction);
12208 dropy += GET_DY_FROM_DIR(drop_direction);
12210 if (!IN_LEV_FIELD(dropx, dropy))
12214 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12215 new_element = drop_element; /* default: no change when dropping */
12217 /* check if player is active, not moving and ready to drop */
12218 if (!player->active || player->MovPos || player->drop_delay > 0)
12221 /* check if player has anything that can be dropped */
12223 if (new_element == EL_UNDEFINED)
12226 if (player->inventory_size == 0 &&
12227 player->inventory_infinite_element == EL_UNDEFINED &&
12228 player->dynabombs_left == 0)
12232 /* check if anything can be dropped at the current position */
12233 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12236 /* collected custom elements can only be dropped on empty fields */
12238 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12241 if (player->inventory_size > 0 &&
12242 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12243 && old_element != EL_EMPTY)
12247 if (old_element != EL_EMPTY)
12248 Back[dropx][dropy] = old_element; /* store old element on this field */
12250 ResetGfxAnimation(dropx, dropy);
12251 ResetRandomAnimationValue(dropx, dropy);
12253 if (player->inventory_size > 0 ||
12254 player->inventory_infinite_element != EL_UNDEFINED)
12256 if (player->inventory_size > 0)
12258 player->inventory_size--;
12261 new_element = player->inventory_element[player->inventory_size];
12264 DrawGameValue_Dynamite(local_player->inventory_size);
12266 if (new_element == EL_DYNAMITE)
12267 new_element = EL_DYNAMITE_ACTIVE;
12268 else if (new_element == EL_SP_DISK_RED)
12269 new_element = EL_SP_DISK_RED_ACTIVE;
12272 Feld[dropx][dropy] = new_element;
12274 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12275 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12276 el2img(Feld[dropx][dropy]), 0);
12278 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12281 /* needed if previous element just changed to "empty" in the last frame */
12282 Changed[dropx][dropy] = 0; /* allow another change */
12286 /* !!! TEST ONLY !!! */
12287 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12288 player->index_bit, drop_side);
12289 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12290 CE_OTHER_GETS_DROPPED,
12291 player->index_bit, drop_side);
12293 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12294 CE_OTHER_GETS_DROPPED,
12295 player->index_bit, drop_side);
12296 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12297 player->index_bit, drop_side);
12300 TestIfElementTouchesCustomElement(dropx, dropy);
12302 else /* player is dropping a dyna bomb */
12304 player->dynabombs_left--;
12307 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12310 Feld[dropx][dropy] = new_element;
12312 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12313 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12314 el2img(Feld[dropx][dropy]), 0);
12316 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12323 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12326 InitField_WithBug1(dropx, dropy, FALSE);
12328 InitField(dropx, dropy, FALSE);
12329 if (CAN_MOVE(Feld[dropx][dropy]))
12330 InitMovDir(dropx, dropy);
12334 new_element = Feld[dropx][dropy]; /* element might have changed */
12336 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12337 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12340 int move_stepsize = element_info[new_element].move_stepsize;
12342 int move_direction, nextx, nexty;
12344 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12345 MovDir[dropx][dropy] = drop_direction;
12347 move_direction = MovDir[dropx][dropy];
12348 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12349 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12352 Changed[dropx][dropy] = 0; /* allow another change */
12353 CheckCollision[dropx][dropy] = 2;
12356 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12359 WasJustMoving[dropx][dropy] = 3;
12362 InitMovingField(dropx, dropy, move_direction);
12363 ContinueMoving(dropx, dropy);
12368 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12371 Changed[dropx][dropy] = 0; /* allow another change */
12374 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12376 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12377 CE_HITTING_SOMETHING, move_direction);
12385 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12390 player->drop_delay = 8 + 8 + 8;
12394 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12399 player->is_dropping = TRUE;
12405 /* ------------------------------------------------------------------------- */
12406 /* game sound playing functions */
12407 /* ------------------------------------------------------------------------- */
12409 static int *loop_sound_frame = NULL;
12410 static int *loop_sound_volume = NULL;
12412 void InitPlayLevelSound()
12414 int num_sounds = getSoundListSize();
12416 checked_free(loop_sound_frame);
12417 checked_free(loop_sound_volume);
12419 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12420 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12423 static void PlayLevelSound(int x, int y, int nr)
12425 int sx = SCREENX(x), sy = SCREENY(y);
12426 int volume, stereo_position;
12427 int max_distance = 8;
12428 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12430 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12431 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12434 if (!IN_LEV_FIELD(x, y) ||
12435 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12436 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12439 volume = SOUND_MAX_VOLUME;
12441 if (!IN_SCR_FIELD(sx, sy))
12443 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12444 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12446 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12449 stereo_position = (SOUND_MAX_LEFT +
12450 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12451 (SCR_FIELDX + 2 * max_distance));
12453 if (IS_LOOP_SOUND(nr))
12455 /* This assures that quieter loop sounds do not overwrite louder ones,
12456 while restarting sound volume comparison with each new game frame. */
12458 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12461 loop_sound_volume[nr] = volume;
12462 loop_sound_frame[nr] = FrameCounter;
12465 PlaySoundExt(nr, volume, stereo_position, type);
12468 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12470 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12471 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12472 y < LEVELY(BY1) ? LEVELY(BY1) :
12473 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12477 static void PlayLevelSoundAction(int x, int y, int action)
12479 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12482 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12484 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12486 if (sound_effect != SND_UNDEFINED)
12487 PlayLevelSound(x, y, sound_effect);
12490 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12493 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12495 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12496 PlayLevelSound(x, y, sound_effect);
12499 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12501 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12503 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12504 PlayLevelSound(x, y, sound_effect);
12507 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12509 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12511 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12512 StopSound(sound_effect);
12515 static void PlayLevelMusic()
12517 if (levelset.music[level_nr] != MUS_UNDEFINED)
12518 PlayMusic(levelset.music[level_nr]); /* from config file */
12520 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12523 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12525 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12528 if (sample == SAMPLE_bug)
12529 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12535 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12539 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12543 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12547 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12551 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12555 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12559 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12562 case SAMPLE_android_clone:
12563 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12566 case SAMPLE_android_move:
12567 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12570 case SAMPLE_spring:
12571 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12575 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12579 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12582 case SAMPLE_eater_eat:
12583 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12587 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12590 case SAMPLE_collect:
12591 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12594 case SAMPLE_diamond:
12595 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12598 case SAMPLE_squash:
12599 /* !!! CHECK THIS !!! */
12601 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12603 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12607 case SAMPLE_wonderfall:
12608 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12612 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12616 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12620 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12624 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12628 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12632 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12635 case SAMPLE_wonder:
12636 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12640 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12643 case SAMPLE_exit_open:
12644 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12647 case SAMPLE_exit_leave:
12648 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12651 case SAMPLE_dynamite:
12652 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12656 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12660 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12664 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12668 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12672 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12676 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12680 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12685 void RaiseScore(int value)
12687 local_player->score += value;
12689 DrawGameValue_Score(local_player->score);
12692 void RaiseScoreElement(int element)
12697 case EL_BD_DIAMOND:
12698 case EL_EMERALD_YELLOW:
12699 case EL_EMERALD_RED:
12700 case EL_EMERALD_PURPLE:
12701 case EL_SP_INFOTRON:
12702 RaiseScore(level.score[SC_EMERALD]);
12705 RaiseScore(level.score[SC_DIAMOND]);
12708 RaiseScore(level.score[SC_CRYSTAL]);
12711 RaiseScore(level.score[SC_PEARL]);
12714 case EL_BD_BUTTERFLY:
12715 case EL_SP_ELECTRON:
12716 RaiseScore(level.score[SC_BUG]);
12719 case EL_BD_FIREFLY:
12720 case EL_SP_SNIKSNAK:
12721 RaiseScore(level.score[SC_SPACESHIP]);
12724 case EL_DARK_YAMYAM:
12725 RaiseScore(level.score[SC_YAMYAM]);
12728 RaiseScore(level.score[SC_ROBOT]);
12731 RaiseScore(level.score[SC_PACMAN]);
12734 RaiseScore(level.score[SC_NUT]);
12737 case EL_SP_DISK_RED:
12738 case EL_DYNABOMB_INCREASE_NUMBER:
12739 case EL_DYNABOMB_INCREASE_SIZE:
12740 case EL_DYNABOMB_INCREASE_POWER:
12741 RaiseScore(level.score[SC_DYNAMITE]);
12743 case EL_SHIELD_NORMAL:
12744 case EL_SHIELD_DEADLY:
12745 RaiseScore(level.score[SC_SHIELD]);
12747 case EL_EXTRA_TIME:
12748 RaiseScore(level.score[SC_TIME_BONUS]);
12762 RaiseScore(level.score[SC_KEY]);
12765 RaiseScore(element_info[element].collect_score);
12770 void RequestQuitGame(boolean ask_if_really_quit)
12772 if (AllPlayersGone ||
12773 !ask_if_really_quit ||
12774 level_editor_test_game ||
12775 Request("Do you really want to quit the game ?",
12776 REQ_ASK | REQ_STAY_CLOSED))
12778 #if defined(NETWORK_AVALIABLE)
12779 if (options.network)
12780 SendToServer_StopPlaying();
12784 game_status = GAME_MODE_MAIN;
12792 if (tape.playing && tape.deactivate_display)
12793 TapeDeactivateDisplayOff(TRUE);
12796 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12799 if (tape.playing && tape.deactivate_display)
12800 TapeDeactivateDisplayOn();
12807 /* ---------- new game button stuff ---------------------------------------- */
12809 /* graphic position values for game buttons */
12810 #define GAME_BUTTON_XSIZE 30
12811 #define GAME_BUTTON_YSIZE 30
12812 #define GAME_BUTTON_XPOS 5
12813 #define GAME_BUTTON_YPOS 215
12814 #define SOUND_BUTTON_XPOS 5
12815 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12817 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12818 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12819 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12820 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12821 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12822 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12829 } gamebutton_info[NUM_GAME_BUTTONS] =
12832 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12837 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12838 GAME_CTRL_ID_PAUSE,
12842 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12847 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12848 SOUND_CTRL_ID_MUSIC,
12849 "background music on/off"
12852 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12853 SOUND_CTRL_ID_LOOPS,
12854 "sound loops on/off"
12857 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12858 SOUND_CTRL_ID_SIMPLE,
12859 "normal sounds on/off"
12863 void CreateGameButtons()
12867 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12869 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12870 struct GadgetInfo *gi;
12873 unsigned long event_mask;
12874 int gd_xoffset, gd_yoffset;
12875 int gd_x1, gd_x2, gd_y1, gd_y2;
12878 gd_xoffset = gamebutton_info[i].x;
12879 gd_yoffset = gamebutton_info[i].y;
12880 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12881 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12883 if (id == GAME_CTRL_ID_STOP ||
12884 id == GAME_CTRL_ID_PAUSE ||
12885 id == GAME_CTRL_ID_PLAY)
12887 button_type = GD_TYPE_NORMAL_BUTTON;
12889 event_mask = GD_EVENT_RELEASED;
12890 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12891 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12895 button_type = GD_TYPE_CHECK_BUTTON;
12897 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12898 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12899 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12900 event_mask = GD_EVENT_PRESSED;
12901 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12902 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12905 gi = CreateGadget(GDI_CUSTOM_ID, id,
12906 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12907 GDI_X, DX + gd_xoffset,
12908 GDI_Y, DY + gd_yoffset,
12909 GDI_WIDTH, GAME_BUTTON_XSIZE,
12910 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12911 GDI_TYPE, button_type,
12912 GDI_STATE, GD_BUTTON_UNPRESSED,
12913 GDI_CHECKED, checked,
12914 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12915 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12916 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12917 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12918 GDI_EVENT_MASK, event_mask,
12919 GDI_CALLBACK_ACTION, HandleGameButtons,
12923 Error(ERR_EXIT, "cannot create gadget");
12925 game_gadget[id] = gi;
12929 void FreeGameButtons()
12933 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12934 FreeGadget(game_gadget[i]);
12937 static void MapGameButtons()
12941 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12942 MapGadget(game_gadget[i]);
12945 void UnmapGameButtons()
12949 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12950 UnmapGadget(game_gadget[i]);
12953 static void HandleGameButtons(struct GadgetInfo *gi)
12955 int id = gi->custom_id;
12957 if (game_status != GAME_MODE_PLAYING)
12962 case GAME_CTRL_ID_STOP:
12963 RequestQuitGame(TRUE);
12966 case GAME_CTRL_ID_PAUSE:
12967 if (options.network)
12969 #if defined(NETWORK_AVALIABLE)
12971 SendToServer_ContinuePlaying();
12973 SendToServer_PausePlaying();
12977 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12980 case GAME_CTRL_ID_PLAY:
12983 #if defined(NETWORK_AVALIABLE)
12984 if (options.network)
12985 SendToServer_ContinuePlaying();
12989 tape.pausing = FALSE;
12990 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12995 case SOUND_CTRL_ID_MUSIC:
12996 if (setup.sound_music)
12998 setup.sound_music = FALSE;
13001 else if (audio.music_available)
13003 setup.sound = setup.sound_music = TRUE;
13005 SetAudioMode(setup.sound);
13011 case SOUND_CTRL_ID_LOOPS:
13012 if (setup.sound_loops)
13013 setup.sound_loops = FALSE;
13014 else if (audio.loops_available)
13016 setup.sound = setup.sound_loops = TRUE;
13017 SetAudioMode(setup.sound);
13021 case SOUND_CTRL_ID_SIMPLE:
13022 if (setup.sound_simple)
13023 setup.sound_simple = FALSE;
13024 else if (audio.sound_available)
13026 setup.sound = setup.sound_simple = TRUE;
13027 SetAudioMode(setup.sound);