1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF (TRUE * 1)
33 #define USE_NEW_MOVE_STYLE (TRUE * USE_NEW_STUFF * 1)
34 #define USE_NEW_MOVE_DELAY (TRUE * USE_NEW_STUFF * 1)
35 #define USE_NEW_PUSH_DELAY (TRUE * USE_NEW_STUFF * 1)
36 #define USE_NEW_BLOCK_STYLE (TRUE * USE_NEW_STUFF * 1)
37 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
38 #define USE_NEW_RANDOMIZE (TRUE * USE_NEW_STUFF * 1)
40 #define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
41 #define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
43 #define USE_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
45 #define USE_BLOCK_DELAY_BUGFIX (TRUE * USE_NEW_STUFF * 1)
47 #define USE_GRAVITY_BUGFIX_NEW (TRUE * USE_NEW_STUFF * 1)
48 #define USE_GRAVITY_BUGFIX_OLD (TRUE * USE_NEW_STUFF * 0)
50 #define USE_PENGUIN_COLLECT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
52 #define USE_IMPACT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
54 #define USE_HITTING_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
55 #define USE_HIT_BY_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
57 #define USE_DROP_BUGFIX (TRUE * USE_NEW_STUFF * 1)
59 #define USE_CHANGE_TO_TRIGGERED (TRUE * USE_NEW_STUFF * 1)
67 /* for MovePlayer() */
68 #define MF_NO_ACTION 0
72 /* for ScrollPlayer() */
74 #define SCROLL_GO_ON 1
77 #define EX_PHASE_START 0
78 #define EX_TYPE_NONE 0
79 #define EX_TYPE_NORMAL (1 << 0)
80 #define EX_TYPE_CENTER (1 << 1)
81 #define EX_TYPE_BORDER (1 << 2)
82 #define EX_TYPE_CROSS (1 << 3)
83 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
85 /* special positions in the game control window (relative to control window) */
88 #define XX_EMERALDS 29
89 #define YY_EMERALDS 54
90 #define XX_DYNAMITE 29
91 #define YY_DYNAMITE 89
100 /* special positions in the game control window (relative to main window) */
101 #define DX_LEVEL (DX + XX_LEVEL)
102 #define DY_LEVEL (DY + YY_LEVEL)
103 #define DX_EMERALDS (DX + XX_EMERALDS)
104 #define DY_EMERALDS (DY + YY_EMERALDS)
105 #define DX_DYNAMITE (DX + XX_DYNAMITE)
106 #define DY_DYNAMITE (DY + YY_DYNAMITE)
107 #define DX_KEYS (DX + XX_KEYS)
108 #define DY_KEYS (DY + YY_KEYS)
109 #define DX_SCORE (DX + XX_SCORE)
110 #define DY_SCORE (DY + YY_SCORE)
111 #define DX_TIME1 (DX + XX_TIME1)
112 #define DX_TIME2 (DX + XX_TIME2)
113 #define DY_TIME (DY + YY_TIME)
115 /* values for initial player move delay (initial delay counter value) */
116 #define INITIAL_MOVE_DELAY_OFF -1
117 #define INITIAL_MOVE_DELAY_ON 0
119 /* values for player movement speed (which is in fact a delay value) */
120 #define MOVE_DELAY_NORMAL_SPEED 8
121 #define MOVE_DELAY_HIGH_SPEED 4
123 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
124 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
125 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
126 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
128 /* values for other actions */
129 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
131 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
132 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
134 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
136 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
137 RND(element_info[e].push_delay_random))
138 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
139 RND(element_info[e].drop_delay_random))
140 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
141 RND(element_info[e].move_delay_random))
142 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
143 (element_info[e].move_delay_random))
145 #define GET_TARGET_ELEMENT(e, ch) \
146 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
147 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
149 #define GET_VALID_PLAYER_ELEMENT(e) \
150 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
152 #define CAN_GROW_INTO(e) \
153 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
155 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
159 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
160 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
161 (CAN_MOVE_INTO_ACID(e) && \
162 Feld[x][y] == EL_ACID) || \
165 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
166 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
167 (CAN_MOVE_INTO_ACID(e) && \
168 Feld[x][y] == EL_ACID) || \
171 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
172 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
176 (DONT_COLLIDE_WITH(e) && \
178 !PLAYER_ENEMY_PROTECTED(x, y))))
181 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
182 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
184 (DONT_COLLIDE_WITH(e) && \
186 !PLAYER_ENEMY_PROTECTED(x, y))))
189 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
193 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
196 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
201 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
204 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
215 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
223 IS_FOOD_PENGUIN(Feld[x][y])))
224 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
230 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
235 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
236 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
237 (CAN_MOVE_INTO_ACID(e) && \
238 Feld[x][y] == EL_ACID) || \
239 Feld[x][y] == EL_DIAMOND))
241 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
242 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
243 (CAN_MOVE_INTO_ACID(e) && \
244 Feld[x][y] == EL_ACID) || \
245 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
247 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
248 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
249 (CAN_MOVE_INTO_ACID(e) && \
250 Feld[x][y] == EL_ACID) || \
251 IS_AMOEBOID(Feld[x][y])))
253 #define PIG_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) || \
257 IS_FOOD_PIG(Feld[x][y])))
259 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
260 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
261 (CAN_MOVE_INTO_ACID(e) && \
262 Feld[x][y] == EL_ACID) || \
263 IS_FOOD_PENGUIN(Feld[x][y]) || \
264 Feld[x][y] == EL_EXIT_OPEN))
266 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
267 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
268 (CAN_MOVE_INTO_ACID(e) && \
269 Feld[x][y] == EL_ACID)))
271 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
272 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
273 (CAN_MOVE_INTO_ACID(e) && \
274 Feld[x][y] == EL_ACID) || \
277 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
278 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
279 (CAN_MOVE_INTO_ACID(e) && \
280 Feld[x][y] == EL_ACID)))
284 #define GROUP_NR(e) ((e) - EL_GROUP_START)
285 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
286 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
287 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
289 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
290 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
293 #define CE_ENTER_FIELD_COND(e, x, y) \
294 (!IS_PLAYER(x, y) && \
295 (Feld[x][y] == EL_ACID || \
296 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
298 #define CE_ENTER_FIELD_COND(e, x, y) \
299 (!IS_PLAYER(x, y) && \
300 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
303 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
304 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
306 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
307 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
309 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
310 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
311 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
312 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
314 /* game button identifiers */
315 #define GAME_CTRL_ID_STOP 0
316 #define GAME_CTRL_ID_PAUSE 1
317 #define GAME_CTRL_ID_PLAY 2
318 #define SOUND_CTRL_ID_MUSIC 3
319 #define SOUND_CTRL_ID_LOOPS 4
320 #define SOUND_CTRL_ID_SIMPLE 5
322 #define NUM_GAME_BUTTONS 6
325 /* forward declaration for internal use */
327 static void AdvanceFrameAndPlayerCounters(int);
329 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
330 static boolean MovePlayer(struct PlayerInfo *, int, int);
331 static void ScrollPlayer(struct PlayerInfo *, int);
332 static void ScrollScreen(struct PlayerInfo *, int);
334 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
336 static void InitBeltMovement(void);
337 static void CloseAllOpenTimegates(void);
338 static void CheckGravityMovement(struct PlayerInfo *);
339 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
340 static void KillHeroUnlessEnemyProtected(int, int);
341 static void KillHeroUnlessExplosionProtected(int, int);
343 static void TestIfPlayerTouchesCustomElement(int, int);
344 static void TestIfElementTouchesCustomElement(int, int);
345 static void TestIfElementHitsCustomElement(int, int, int);
347 static void TestIfElementSmashesCustomElement(int, int, int);
350 static void ChangeElement(int, int, int);
352 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
353 #define CheckTriggeredElementChange(x, y, e, ev) \
354 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
356 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
357 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
358 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
359 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
360 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
361 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
364 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
365 #define CheckElementChange(x, y, e, te, ev) \
366 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
367 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
368 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
369 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
370 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
371 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
372 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
374 static void PlayLevelSound(int, int, int);
375 static void PlayLevelSoundNearest(int, int, int);
376 static void PlayLevelSoundAction(int, int, int);
377 static void PlayLevelSoundElementAction(int, int, int, int);
378 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
379 static void PlayLevelSoundActionIfLoop(int, int, int);
380 static void StopLevelSoundActionIfLoop(int, int, int);
381 static void PlayLevelMusic();
383 static void MapGameButtons();
384 static void HandleGameButtons(struct GadgetInfo *);
386 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
389 /* ------------------------------------------------------------------------- */
390 /* definition of elements that automatically change to other elements after */
391 /* a specified time, eventually calling a function when changing */
392 /* ------------------------------------------------------------------------- */
394 /* forward declaration for changer functions */
395 static void InitBuggyBase(int x, int y);
396 static void WarnBuggyBase(int x, int y);
398 static void InitTrap(int x, int y);
399 static void ActivateTrap(int x, int y);
400 static void ChangeActiveTrap(int x, int y);
402 static void InitRobotWheel(int x, int y);
403 static void RunRobotWheel(int x, int y);
404 static void StopRobotWheel(int x, int y);
406 static void InitTimegateWheel(int x, int y);
407 static void RunTimegateWheel(int x, int y);
409 struct ChangingElementInfo
414 void (*pre_change_function)(int x, int y);
415 void (*change_function)(int x, int y);
416 void (*post_change_function)(int x, int y);
419 static struct ChangingElementInfo change_delay_list[] =
470 EL_SWITCHGATE_OPENING,
478 EL_SWITCHGATE_CLOSING,
479 EL_SWITCHGATE_CLOSED,
511 EL_ACID_SPLASH_RIGHT,
520 EL_SP_BUGGY_BASE_ACTIVATING,
527 EL_SP_BUGGY_BASE_ACTIVATING,
528 EL_SP_BUGGY_BASE_ACTIVE,
535 EL_SP_BUGGY_BASE_ACTIVE,
559 EL_ROBOT_WHEEL_ACTIVE,
567 EL_TIMEGATE_SWITCH_ACTIVE,
588 int push_delay_fixed, push_delay_random;
593 { EL_BALLOON, 0, 0 },
595 { EL_SOKOBAN_OBJECT, 2, 0 },
596 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
597 { EL_SATELLITE, 2, 0 },
598 { EL_SP_DISK_YELLOW, 2, 0 },
600 { EL_UNDEFINED, 0, 0 },
608 move_stepsize_list[] =
610 { EL_AMOEBA_DROP, 2 },
611 { EL_AMOEBA_DROPPING, 2 },
612 { EL_QUICKSAND_FILLING, 1 },
613 { EL_QUICKSAND_EMPTYING, 1 },
614 { EL_MAGIC_WALL_FILLING, 2 },
615 { EL_BD_MAGIC_WALL_FILLING, 2 },
616 { EL_MAGIC_WALL_EMPTYING, 2 },
617 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
627 collect_count_list[] =
630 { EL_BD_DIAMOND, 1 },
631 { EL_EMERALD_YELLOW, 1 },
632 { EL_EMERALD_RED, 1 },
633 { EL_EMERALD_PURPLE, 1 },
635 { EL_SP_INFOTRON, 1 },
647 access_direction_list[] =
649 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
650 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
651 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
652 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
653 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
654 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
655 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
656 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
657 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
658 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
659 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
661 { EL_SP_PORT_LEFT, MV_RIGHT },
662 { EL_SP_PORT_RIGHT, MV_LEFT },
663 { EL_SP_PORT_UP, MV_DOWN },
664 { EL_SP_PORT_DOWN, MV_UP },
665 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
666 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
667 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
668 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
669 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
670 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
671 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
672 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
673 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
674 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
675 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
676 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
677 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
678 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
679 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
681 { EL_UNDEFINED, MV_NO_MOVING }
684 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
686 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
687 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
688 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
689 IS_JUST_CHANGING(x, y))
691 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
694 void GetPlayerConfig()
696 if (!audio.sound_available)
697 setup.sound_simple = FALSE;
699 if (!audio.loops_available)
700 setup.sound_loops = FALSE;
702 if (!audio.music_available)
703 setup.sound_music = FALSE;
705 if (!video.fullscreen_available)
706 setup.fullscreen = FALSE;
708 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
710 SetAudioMode(setup.sound);
714 static int getBeltNrFromBeltElement(int element)
716 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
717 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
718 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
721 static int getBeltNrFromBeltActiveElement(int element)
723 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
724 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
725 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
728 static int getBeltNrFromBeltSwitchElement(int element)
730 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
731 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
732 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
735 static int getBeltDirNrFromBeltSwitchElement(int element)
737 static int belt_base_element[4] =
739 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
740 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
741 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
742 EL_CONVEYOR_BELT_4_SWITCH_LEFT
745 int belt_nr = getBeltNrFromBeltSwitchElement(element);
746 int belt_dir_nr = element - belt_base_element[belt_nr];
748 return (belt_dir_nr % 3);
751 static int getBeltDirFromBeltSwitchElement(int element)
753 static int belt_move_dir[3] =
760 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
762 return belt_move_dir[belt_dir_nr];
765 static void InitPlayerField(int x, int y, int element, boolean init_game)
767 if (element == EL_SP_MURPHY)
771 if (stored_player[0].present)
773 Feld[x][y] = EL_SP_MURPHY_CLONE;
779 stored_player[0].use_murphy_graphic = TRUE;
782 Feld[x][y] = EL_PLAYER_1;
788 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
789 int jx = player->jx, jy = player->jy;
791 player->present = TRUE;
793 player->block_last_field = (element == EL_SP_MURPHY ?
794 level.sp_block_last_field :
795 level.block_last_field);
797 #if USE_NEW_BLOCK_STYLE
800 /* ---------- initialize player's last field block delay --------------- */
802 /* always start with reliable default value (no adjustment needed) */
803 player->block_delay_adjustment = 0;
805 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
806 if (player->block_last_field && element == EL_SP_MURPHY)
807 player->block_delay_adjustment = 1;
809 /* special case 2: in game engines before 3.1.1, blocking was different */
810 if (game.use_block_last_field_bug)
811 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
814 /* blocking the last field when moving was corrected in version 3.1.1 */
815 if (game.use_block_last_field_bug)
817 /* even "not blocking" was blocking the last field for one frame */
818 level.block_delay = (level.block_last_field ? 7 : 1);
819 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
821 level.block_last_field = TRUE;
822 level.sp_block_last_field = TRUE;
826 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
827 level.block_delay = 8; /* when blocking, block 8 frames */
828 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
832 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
838 player->block_delay = (player->block_last_field ?
839 (element == EL_SP_MURPHY ?
840 level.sp_block_delay :
841 level.block_delay) : 0);
843 player->block_delay = (element == EL_SP_MURPHY ?
844 (player->block_last_field ? 7 : 1) :
845 (player->block_last_field ? 7 : 1));
851 printf("::: block_last_field == %d, block_delay = %d\n",
852 player->block_last_field, player->block_delay);
856 if (!options.network || player->connected)
858 player->active = TRUE;
860 /* remove potentially duplicate players */
861 if (StorePlayer[jx][jy] == Feld[x][y])
862 StorePlayer[jx][jy] = 0;
864 StorePlayer[x][y] = Feld[x][y];
868 printf("Player %d activated.\n", player->element_nr);
869 printf("[Local player is %d and currently %s.]\n",
870 local_player->element_nr,
871 local_player->active ? "active" : "not active");
875 Feld[x][y] = EL_EMPTY;
877 player->jx = player->last_jx = x;
878 player->jy = player->last_jy = y;
882 static void InitField(int x, int y, boolean init_game)
884 int element = Feld[x][y];
893 InitPlayerField(x, y, element, init_game);
896 case EL_SOKOBAN_FIELD_PLAYER:
897 element = Feld[x][y] = EL_PLAYER_1;
898 InitField(x, y, init_game);
900 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
901 InitField(x, y, init_game);
904 case EL_SOKOBAN_FIELD_EMPTY:
905 local_player->sokobanfields_still_needed++;
909 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
910 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
911 else if (x > 0 && Feld[x-1][y] == EL_ACID)
912 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
913 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
914 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
915 else if (y > 0 && Feld[x][y-1] == EL_ACID)
916 Feld[x][y] = EL_ACID_POOL_BOTTOM;
917 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
918 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
926 case EL_SPACESHIP_RIGHT:
927 case EL_SPACESHIP_UP:
928 case EL_SPACESHIP_LEFT:
929 case EL_SPACESHIP_DOWN:
931 case EL_BD_BUTTERFLY_RIGHT:
932 case EL_BD_BUTTERFLY_UP:
933 case EL_BD_BUTTERFLY_LEFT:
934 case EL_BD_BUTTERFLY_DOWN:
935 case EL_BD_BUTTERFLY:
936 case EL_BD_FIREFLY_RIGHT:
937 case EL_BD_FIREFLY_UP:
938 case EL_BD_FIREFLY_LEFT:
939 case EL_BD_FIREFLY_DOWN:
941 case EL_PACMAN_RIGHT:
965 if (y == lev_fieldy - 1)
967 Feld[x][y] = EL_AMOEBA_GROWING;
968 Store[x][y] = EL_AMOEBA_WET;
972 case EL_DYNAMITE_ACTIVE:
973 case EL_SP_DISK_RED_ACTIVE:
974 case EL_DYNABOMB_PLAYER_1_ACTIVE:
975 case EL_DYNABOMB_PLAYER_2_ACTIVE:
976 case EL_DYNABOMB_PLAYER_3_ACTIVE:
977 case EL_DYNABOMB_PLAYER_4_ACTIVE:
982 local_player->lights_still_needed++;
986 local_player->friends_still_needed++;
991 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
996 Feld[x][y] = EL_EMPTY;
1001 case EL_EM_KEY_1_FILE:
1002 Feld[x][y] = EL_EM_KEY_1;
1004 case EL_EM_KEY_2_FILE:
1005 Feld[x][y] = EL_EM_KEY_2;
1007 case EL_EM_KEY_3_FILE:
1008 Feld[x][y] = EL_EM_KEY_3;
1010 case EL_EM_KEY_4_FILE:
1011 Feld[x][y] = EL_EM_KEY_4;
1015 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1016 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1017 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1018 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1019 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1020 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1021 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1022 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1023 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1024 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1025 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1026 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1029 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1030 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1031 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1033 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1035 game.belt_dir[belt_nr] = belt_dir;
1036 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1038 else /* more than one switch -- set it like the first switch */
1040 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1045 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1047 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1050 case EL_LIGHT_SWITCH_ACTIVE:
1052 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1056 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1058 else if (IS_GROUP_ELEMENT(element))
1060 struct ElementGroupInfo *group = element_info[element].group;
1061 int last_anim_random_frame = gfx.anim_random_frame;
1064 if (group->choice_mode == ANIM_RANDOM)
1065 gfx.anim_random_frame = RND(group->num_elements_resolved);
1067 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1068 group->choice_mode, 0,
1071 if (group->choice_mode == ANIM_RANDOM)
1072 gfx.anim_random_frame = last_anim_random_frame;
1074 group->choice_pos++;
1076 Feld[x][y] = group->element_resolved[element_pos];
1078 InitField(x, y, init_game);
1084 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1086 InitField(x, y, init_game);
1088 /* not needed to call InitMovDir() -- already done by InitField()! */
1089 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1090 CAN_MOVE(Feld[x][y]))
1094 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1096 int old_element = Feld[x][y];
1098 InitField(x, y, init_game);
1100 /* not needed to call InitMovDir() -- already done by InitField()! */
1101 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1102 CAN_MOVE(old_element) &&
1103 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1106 /* this case is in fact a combination of not less than three bugs:
1107 first, it calls InitMovDir() for elements that can move, although this is
1108 already done by InitField(); then, it checks the element that was at this
1109 field _before_ the call to InitField() (which can change it); lastly, it
1110 was not called for "mole with direction" elements, which were treated as
1111 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1115 inline void DrawGameValue_Emeralds(int value)
1117 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1120 inline void DrawGameValue_Dynamite(int value)
1122 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1125 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1129 /* currently only 4 of 8 possible keys are displayed */
1130 for (i = 0; i < STD_NUM_KEYS; i++)
1132 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1133 el2edimg(EL_KEY_1 + i));
1136 inline void DrawGameValue_Score(int value)
1138 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1141 inline void DrawGameValue_Time(int value)
1144 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1146 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1149 inline void DrawGameValue_Level(int value)
1152 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1155 /* misuse area for displaying emeralds to draw bigger level number */
1156 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1157 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1159 /* now copy it to the area for displaying level number */
1160 BlitBitmap(drawto, drawto,
1161 DX_EMERALDS, DY_EMERALDS + 1,
1162 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1163 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1164 DX_LEVEL - 1, DY_LEVEL + 1);
1166 /* restore the area for displaying emeralds */
1167 DrawGameValue_Emeralds(local_player->gems_still_needed);
1169 /* yes, this is all really ugly :-) */
1173 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1176 int key[MAX_NUM_KEYS];
1179 for (i = 0; i < MAX_NUM_KEYS; i++)
1180 key[i] = key_bits & (1 << i);
1182 DrawGameValue_Level(level_nr);
1184 DrawGameValue_Emeralds(emeralds);
1185 DrawGameValue_Dynamite(dynamite);
1186 DrawGameValue_Score(score);
1187 DrawGameValue_Time(time);
1189 DrawGameValue_Keys(key);
1192 void DrawGameDoorValues()
1196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1198 DrawGameDoorValues_EM();
1203 DrawGameValue_Level(level_nr);
1205 DrawGameValue_Emeralds(local_player->gems_still_needed);
1206 DrawGameValue_Dynamite(local_player->inventory_size);
1207 DrawGameValue_Score(local_player->score);
1208 DrawGameValue_Time(TimeLeft);
1210 for (i = 0; i < MAX_PLAYERS; i++)
1211 DrawGameValue_Keys(stored_player[i].key);
1214 static void resolve_group_element(int group_element, int recursion_depth)
1216 static int group_nr;
1217 static struct ElementGroupInfo *group;
1218 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1221 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1223 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1224 group_element - EL_GROUP_START + 1);
1226 /* replace element which caused too deep recursion by question mark */
1227 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1232 if (recursion_depth == 0) /* initialization */
1234 group = element_info[group_element].group;
1235 group_nr = group_element - EL_GROUP_START;
1237 group->num_elements_resolved = 0;
1238 group->choice_pos = 0;
1241 for (i = 0; i < actual_group->num_elements; i++)
1243 int element = actual_group->element[i];
1245 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1248 if (IS_GROUP_ELEMENT(element))
1249 resolve_group_element(element, recursion_depth + 1);
1252 group->element_resolved[group->num_elements_resolved++] = element;
1253 element_info[element].in_group[group_nr] = TRUE;
1258 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1260 printf("::: group %d: %d resolved elements\n",
1261 group_element - EL_GROUP_START, group->num_elements_resolved);
1262 for (i = 0; i < group->num_elements_resolved; i++)
1263 printf("::: - %d ['%s']\n", group->element_resolved[i],
1264 element_info[group->element_resolved[i]].token_name);
1271 =============================================================================
1273 -----------------------------------------------------------------------------
1274 initialize game engine due to level / tape version number
1275 =============================================================================
1278 static void InitGameEngine()
1282 /* set game engine from tape file when re-playing, else from level file */
1283 game.engine_version = (tape.playing ? tape.engine_version :
1284 level.game_version);
1286 /* ---------------------------------------------------------------------- */
1287 /* set flags for bugs and changes according to active game engine version */
1288 /* ---------------------------------------------------------------------- */
1291 Summary of bugfix/change:
1292 Fixed handling for custom elements that change when pushed by the player.
1294 Fixed/changed in version:
1298 Before 3.1.0, custom elements that "change when pushing" changed directly
1299 after the player started pushing them (until then handled in "DigField()").
1300 Since 3.1.0, these custom elements are not changed until the "pushing"
1301 move of the element is finished (now handled in "ContinueMoving()").
1303 Affected levels/tapes:
1304 The first condition is generally needed for all levels/tapes before version
1305 3.1.0, which might use the old behaviour before it was changed; known tapes
1306 that are affected are some tapes from the level set "Walpurgis Gardens" by
1308 The second condition is an exception from the above case and is needed for
1309 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1310 above (including some development versions of 3.1.0), but before it was
1311 known that this change would break tapes like the above and was fixed in
1312 3.1.1, so that the changed behaviour was active although the engine version
1313 while recording maybe was before 3.1.0. There is at least one tape that is
1314 affected by this exception, which is the tape for the one-level set "Bug
1315 Machine" by Juergen Bonhagen.
1318 game.use_change_when_pushing_bug =
1319 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1321 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1322 tape.game_version < VERSION_IDENT(3,1,1,0)));
1325 Summary of bugfix/change:
1326 Fixed handling for blocking the field the player leaves when moving.
1328 Fixed/changed in version:
1332 Before 3.1.1, when "block last field when moving" was enabled, the field
1333 the player is leaving when moving was blocked for the time of the move,
1334 and was directly unblocked afterwards. This resulted in the last field
1335 being blocked for exactly one less than the number of frames of one player
1336 move. Additionally, even when blocking was disabled, the last field was
1337 blocked for exactly one frame.
1338 Since 3.1.1, due to changes in player movement handling, the last field
1339 is not blocked at all when blocking is disabled. When blocking is enabled,
1340 the last field is blocked for exactly the number of frames of one player
1341 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1342 last field is blocked for exactly one more than the number of frames of
1345 Affected levels/tapes:
1346 (!!! yet to be determined -- probably many !!!)
1349 game.use_block_last_field_bug =
1350 (game.engine_version < VERSION_IDENT(3,1,1,0));
1352 /* ---------------------------------------------------------------------- */
1354 /* dynamically adjust element properties according to game engine version */
1355 InitElementPropertiesEngine(game.engine_version);
1358 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1359 printf(" tape version == %06d [%s] [file: %06d]\n",
1360 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1362 printf(" => game.engine_version == %06d\n", game.engine_version);
1365 /* ---------- recursively resolve group elements ------------------------- */
1367 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1368 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1369 element_info[i].in_group[j] = FALSE;
1371 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1372 resolve_group_element(EL_GROUP_START + i, 0);
1374 /* ---------- initialize player's initial move delay --------------------- */
1376 #if USE_NEW_MOVE_DELAY
1377 /* dynamically adjust player properties according to level information */
1378 game.initial_move_delay_value =
1379 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1381 /* dynamically adjust player properties according to game engine version */
1382 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1383 game.initial_move_delay_value : 0);
1385 /* dynamically adjust player properties according to game engine version */
1386 game.initial_move_delay =
1387 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1388 INITIAL_MOVE_DELAY_OFF);
1390 /* dynamically adjust player properties according to level information */
1391 game.initial_move_delay_value =
1392 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1395 /* ---------- initialize player's initial push delay --------------------- */
1397 /* dynamically adjust player properties according to game engine version */
1398 game.initial_push_delay_value =
1399 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1401 /* ---------- initialize changing elements ------------------------------- */
1403 /* initialize changing elements information */
1404 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1406 struct ElementInfo *ei = &element_info[i];
1408 /* this pointer might have been changed in the level editor */
1409 ei->change = &ei->change_page[0];
1411 if (!IS_CUSTOM_ELEMENT(i))
1413 ei->change->target_element = EL_EMPTY_SPACE;
1414 ei->change->delay_fixed = 0;
1415 ei->change->delay_random = 0;
1416 ei->change->delay_frames = 1;
1419 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1421 ei->has_change_event[j] = FALSE;
1423 ei->event_page_nr[j] = 0;
1424 ei->event_page[j] = &ei->change_page[0];
1428 /* add changing elements from pre-defined list */
1429 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1431 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1432 struct ElementInfo *ei = &element_info[ch_delay->element];
1434 ei->change->target_element = ch_delay->target_element;
1435 ei->change->delay_fixed = ch_delay->change_delay;
1437 ei->change->pre_change_function = ch_delay->pre_change_function;
1438 ei->change->change_function = ch_delay->change_function;
1439 ei->change->post_change_function = ch_delay->post_change_function;
1441 ei->has_change_event[CE_DELAY] = TRUE;
1444 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1449 /* add change events from custom element configuration */
1450 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1452 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1454 for (j = 0; j < ei->num_change_pages; j++)
1456 if (!ei->change_page[j].can_change)
1459 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1461 /* only add event page for the first page found with this event */
1462 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1464 ei->has_change_event[k] = TRUE;
1466 ei->event_page_nr[k] = j;
1467 ei->event_page[k] = &ei->change_page[j];
1475 /* add change events from custom element configuration */
1476 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1478 int element = EL_CUSTOM_START + i;
1480 /* only add custom elements that change after fixed/random frame delay */
1481 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1482 element_info[element].has_change_event[CE_DELAY] = TRUE;
1486 /* ---------- initialize run-time trigger player and element ------------- */
1488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1490 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1492 for (j = 0; j < ei->num_change_pages; j++)
1494 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1495 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1499 /* ---------- initialize trigger events ---------------------------------- */
1501 /* initialize trigger events information */
1502 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1503 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1504 trigger_events[i][j] = FALSE;
1507 /* add trigger events from element change event properties */
1508 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1510 struct ElementInfo *ei = &element_info[i];
1512 for (j = 0; j < ei->num_change_pages; j++)
1514 if (!ei->change_page[j].can_change)
1517 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1519 int trigger_element = ei->change_page[j].trigger_element;
1521 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1523 if (ei->change_page[j].has_event[k])
1525 if (IS_GROUP_ELEMENT(trigger_element))
1527 struct ElementGroupInfo *group =
1528 element_info[trigger_element].group;
1530 for (l = 0; l < group->num_elements_resolved; l++)
1531 trigger_events[group->element_resolved[l]][k] = TRUE;
1534 trigger_events[trigger_element][k] = TRUE;
1541 /* add trigger events from element change event properties */
1542 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1543 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1544 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1545 if (element_info[i].change->has_event[j])
1546 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1549 /* ---------- initialize push delay -------------------------------------- */
1551 /* initialize push delay values to default */
1552 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1554 if (!IS_CUSTOM_ELEMENT(i))
1556 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1557 element_info[i].push_delay_random = game.default_push_delay_random;
1561 /* set push delay value for certain elements from pre-defined list */
1562 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1564 int e = push_delay_list[i].element;
1566 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1567 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1570 /* set push delay value for Supaplex elements for newer engine versions */
1571 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1573 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1575 if (IS_SP_ELEMENT(i))
1577 #if USE_NEW_MOVE_STYLE
1578 /* set SP push delay to just enough to push under a falling zonk */
1579 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1581 element_info[i].push_delay_fixed = delay;
1582 element_info[i].push_delay_random = 0;
1584 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1585 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1591 /* ---------- initialize move stepsize ----------------------------------- */
1593 /* initialize move stepsize values to default */
1594 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1595 if (!IS_CUSTOM_ELEMENT(i))
1596 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1598 /* set move stepsize value for certain elements from pre-defined list */
1599 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1601 int e = move_stepsize_list[i].element;
1603 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1607 /* ---------- initialize move dig/leave ---------------------------------- */
1609 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1611 element_info[i].can_leave_element = FALSE;
1612 element_info[i].can_leave_element_last = FALSE;
1616 /* ---------- initialize gem count --------------------------------------- */
1618 /* initialize gem count values for each element */
1619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1620 if (!IS_CUSTOM_ELEMENT(i))
1621 element_info[i].collect_count = 0;
1623 /* add gem count values for all elements from pre-defined list */
1624 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1625 element_info[collect_count_list[i].element].collect_count =
1626 collect_count_list[i].count;
1628 /* ---------- initialize access direction -------------------------------- */
1630 /* initialize access direction values to default (access from every side) */
1631 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1632 if (!IS_CUSTOM_ELEMENT(i))
1633 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1635 /* set access direction value for certain elements from pre-defined list */
1636 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1637 element_info[access_direction_list[i].element].access_direction =
1638 access_direction_list[i].direction;
1643 =============================================================================
1645 -----------------------------------------------------------------------------
1646 initialize and start new game
1647 =============================================================================
1652 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1653 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1654 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1661 #if USE_NEW_AMOEBA_CODE
1662 printf("Using new amoeba code.\n");
1664 printf("Using old amoeba code.\n");
1669 /* don't play tapes over network */
1670 network_playing = (options.network && !tape.playing);
1672 for (i = 0; i < MAX_PLAYERS; i++)
1674 struct PlayerInfo *player = &stored_player[i];
1676 player->index_nr = i;
1677 player->index_bit = (1 << i);
1678 player->element_nr = EL_PLAYER_1 + i;
1680 player->present = FALSE;
1681 player->active = FALSE;
1684 player->effective_action = 0;
1685 player->programmed_action = 0;
1688 player->gems_still_needed = level.gems_needed;
1689 player->sokobanfields_still_needed = 0;
1690 player->lights_still_needed = 0;
1691 player->friends_still_needed = 0;
1693 for (j = 0; j < MAX_NUM_KEYS; j++)
1694 player->key[j] = FALSE;
1696 player->dynabomb_count = 0;
1697 player->dynabomb_size = 1;
1698 player->dynabombs_left = 0;
1699 player->dynabomb_xl = FALSE;
1701 player->MovDir = MV_NO_MOVING;
1704 player->GfxDir = MV_NO_MOVING;
1705 player->GfxAction = ACTION_DEFAULT;
1707 player->StepFrame = 0;
1709 player->use_murphy_graphic = FALSE;
1711 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1712 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1714 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1716 player->actual_frame_counter = 0;
1718 player->step_counter = 0;
1720 player->last_move_dir = MV_NO_MOVING;
1722 player->is_waiting = FALSE;
1723 player->is_moving = FALSE;
1724 player->is_auto_moving = FALSE;
1725 player->is_digging = FALSE;
1726 player->is_snapping = FALSE;
1727 player->is_collecting = FALSE;
1728 player->is_pushing = FALSE;
1729 player->is_switching = FALSE;
1730 player->is_dropping = FALSE;
1732 player->is_bored = FALSE;
1733 player->is_sleeping = FALSE;
1735 player->frame_counter_bored = -1;
1736 player->frame_counter_sleeping = -1;
1738 player->anim_delay_counter = 0;
1739 player->post_delay_counter = 0;
1741 player->action_waiting = ACTION_DEFAULT;
1742 player->last_action_waiting = ACTION_DEFAULT;
1743 player->special_action_bored = ACTION_DEFAULT;
1744 player->special_action_sleeping = ACTION_DEFAULT;
1746 player->num_special_action_bored = 0;
1747 player->num_special_action_sleeping = 0;
1749 /* determine number of special actions for bored and sleeping animation */
1750 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1752 boolean found = FALSE;
1754 for (k = 0; k < NUM_DIRECTIONS; k++)
1755 if (el_act_dir2img(player->element_nr, j, k) !=
1756 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1760 player->num_special_action_bored++;
1764 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1766 boolean found = FALSE;
1768 for (k = 0; k < NUM_DIRECTIONS; k++)
1769 if (el_act_dir2img(player->element_nr, j, k) !=
1770 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1774 player->num_special_action_sleeping++;
1779 player->switch_x = -1;
1780 player->switch_y = -1;
1783 player->drop_x = -1;
1784 player->drop_y = -1;
1787 player->show_envelope = 0;
1789 player->move_delay = game.initial_move_delay;
1790 player->move_delay_value = game.initial_move_delay_value;
1792 player->move_delay_reset_counter = 0;
1794 #if USE_NEW_PUSH_DELAY
1795 player->push_delay = -1; /* initialized when pushing starts */
1796 player->push_delay_value = game.initial_push_delay_value;
1798 player->push_delay = 0;
1799 player->push_delay_value = game.initial_push_delay_value;
1802 player->drop_delay = 0;
1804 player->last_jx = player->last_jy = 0;
1805 player->jx = player->jy = 0;
1807 player->shield_normal_time_left = 0;
1808 player->shield_deadly_time_left = 0;
1810 player->inventory_infinite_element = EL_UNDEFINED;
1811 player->inventory_size = 0;
1813 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1814 SnapField(player, 0, 0);
1816 player->LevelSolved = FALSE;
1817 player->GameOver = FALSE;
1820 network_player_action_received = FALSE;
1822 #if defined(NETWORK_AVALIABLE)
1823 /* initial null action */
1824 if (network_playing)
1825 SendToServer_MovePlayer(MV_NO_MOVING);
1834 TimeLeft = level.time;
1837 ScreenMovDir = MV_NO_MOVING;
1841 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1843 AllPlayersGone = FALSE;
1845 game.yamyam_content_nr = 0;
1846 game.magic_wall_active = FALSE;
1847 game.magic_wall_time_left = 0;
1848 game.light_time_left = 0;
1849 game.timegate_time_left = 0;
1850 game.switchgate_pos = 0;
1851 game.balloon_dir = MV_NO_MOVING;
1852 game.gravity = level.initial_gravity;
1853 game.explosions_delayed = TRUE;
1855 game.envelope_active = FALSE;
1857 for (i = 0; i < NUM_BELTS; i++)
1859 game.belt_dir[i] = MV_NO_MOVING;
1860 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1863 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1864 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1866 for (x = 0; x < lev_fieldx; x++)
1868 for (y = 0; y < lev_fieldy; y++)
1870 Feld[x][y] = level.field[x][y];
1871 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1872 ChangeDelay[x][y] = 0;
1873 ChangePage[x][y] = -1;
1874 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1876 WasJustMoving[x][y] = 0;
1877 WasJustFalling[x][y] = 0;
1878 CheckCollision[x][y] = 0;
1880 Pushed[x][y] = FALSE;
1882 Changed[x][y] = FALSE;
1883 ChangeEvent[x][y] = -1;
1885 ExplodePhase[x][y] = 0;
1886 ExplodeDelay[x][y] = 0;
1887 ExplodeField[x][y] = EX_TYPE_NONE;
1889 RunnerVisit[x][y] = 0;
1890 PlayerVisit[x][y] = 0;
1893 GfxRandom[x][y] = INIT_GFX_RANDOM();
1894 GfxElement[x][y] = EL_UNDEFINED;
1895 GfxAction[x][y] = ACTION_DEFAULT;
1896 GfxDir[x][y] = MV_NO_MOVING;
1900 for (y = 0; y < lev_fieldy; y++)
1902 for (x = 0; x < lev_fieldx; x++)
1904 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1906 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1908 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1911 InitField(x, y, TRUE);
1917 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1918 emulate_sb ? EMU_SOKOBAN :
1919 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1921 /* initialize explosion and ignition delay */
1922 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1924 if (!IS_CUSTOM_ELEMENT(i))
1927 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1928 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1929 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1930 int last_phase = (num_phase + 1) * delay;
1931 int half_phase = (num_phase / 2) * delay;
1933 element_info[i].explosion_delay = last_phase - 1;
1934 element_info[i].ignition_delay = half_phase;
1937 if (i == EL_BLACK_ORB)
1938 element_info[i].ignition_delay = 0;
1940 if (i == EL_BLACK_ORB)
1941 element_info[i].ignition_delay = 1;
1946 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1947 element_info[i].explosion_delay = 1;
1949 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1950 element_info[i].ignition_delay = 1;
1954 /* correct non-moving belts to start moving left */
1955 for (i = 0; i < NUM_BELTS; i++)
1956 if (game.belt_dir[i] == MV_NO_MOVING)
1957 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1959 /* check if any connected player was not found in playfield */
1960 for (i = 0; i < MAX_PLAYERS; i++)
1962 struct PlayerInfo *player = &stored_player[i];
1964 if (player->connected && !player->present)
1966 for (j = 0; j < MAX_PLAYERS; j++)
1968 struct PlayerInfo *some_player = &stored_player[j];
1969 int jx = some_player->jx, jy = some_player->jy;
1971 /* assign first free player found that is present in the playfield */
1972 if (some_player->present && !some_player->connected)
1974 player->present = TRUE;
1975 player->active = TRUE;
1977 some_player->present = FALSE;
1978 some_player->active = FALSE;
1981 player->element_nr = some_player->element_nr;
1984 #if USE_NEW_BLOCK_STYLE
1985 player->block_last_field = some_player->block_last_field;
1986 player->block_delay_adjustment = some_player->block_delay_adjustment;
1989 StorePlayer[jx][jy] = player->element_nr;
1990 player->jx = player->last_jx = jx;
1991 player->jy = player->last_jy = jy;
2001 /* when playing a tape, eliminate all players which do not participate */
2003 for (i = 0; i < MAX_PLAYERS; i++)
2005 if (stored_player[i].active && !tape.player_participates[i])
2007 struct PlayerInfo *player = &stored_player[i];
2008 int jx = player->jx, jy = player->jy;
2010 player->active = FALSE;
2011 StorePlayer[jx][jy] = 0;
2012 Feld[jx][jy] = EL_EMPTY;
2016 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2018 /* when in single player mode, eliminate all but the first active player */
2020 for (i = 0; i < MAX_PLAYERS; i++)
2022 if (stored_player[i].active)
2024 for (j = i + 1; j < MAX_PLAYERS; j++)
2026 if (stored_player[j].active)
2028 struct PlayerInfo *player = &stored_player[j];
2029 int jx = player->jx, jy = player->jy;
2031 player->active = FALSE;
2032 player->present = FALSE;
2034 StorePlayer[jx][jy] = 0;
2035 Feld[jx][jy] = EL_EMPTY;
2042 /* when recording the game, store which players take part in the game */
2045 for (i = 0; i < MAX_PLAYERS; i++)
2046 if (stored_player[i].active)
2047 tape.player_participates[i] = TRUE;
2052 for (i = 0; i < MAX_PLAYERS; i++)
2054 struct PlayerInfo *player = &stored_player[i];
2056 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2061 if (local_player == player)
2062 printf("Player %d is local player.\n", i+1);
2066 if (BorderElement == EL_EMPTY)
2069 SBX_Right = lev_fieldx - SCR_FIELDX;
2071 SBY_Lower = lev_fieldy - SCR_FIELDY;
2076 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2078 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2081 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2082 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2084 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2085 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2087 /* if local player not found, look for custom element that might create
2088 the player (make some assumptions about the right custom element) */
2089 if (!local_player->present)
2091 int start_x = 0, start_y = 0;
2092 int found_rating = 0;
2093 int found_element = EL_UNDEFINED;
2095 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2097 int element = Feld[x][y];
2102 if (!IS_CUSTOM_ELEMENT(element))
2105 if (CAN_CHANGE(element))
2107 for (i = 0; i < element_info[element].num_change_pages; i++)
2109 content = element_info[element].change_page[i].target_element;
2110 is_player = ELEM_IS_PLAYER(content);
2112 if (is_player && (found_rating < 3 || element < found_element))
2118 found_element = element;
2123 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2125 content = element_info[element].content[xx][yy];
2126 is_player = ELEM_IS_PLAYER(content);
2128 if (is_player && (found_rating < 2 || element < found_element))
2130 start_x = x + xx - 1;
2131 start_y = y + yy - 1;
2134 found_element = element;
2137 if (!CAN_CHANGE(element))
2140 for (i = 0; i < element_info[element].num_change_pages; i++)
2142 content= element_info[element].change_page[i].target_content[xx][yy];
2143 is_player = ELEM_IS_PLAYER(content);
2145 if (is_player && (found_rating < 1 || element < found_element))
2147 start_x = x + xx - 1;
2148 start_y = y + yy - 1;
2151 found_element = element;
2157 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2158 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2161 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2162 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2168 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2169 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2170 local_player->jx - MIDPOSX);
2172 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2173 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2174 local_player->jy - MIDPOSY);
2176 scroll_x = SBX_Left;
2177 scroll_y = SBY_Upper;
2178 if (local_player->jx >= SBX_Left + MIDPOSX)
2179 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2180 local_player->jx - MIDPOSX :
2182 if (local_player->jy >= SBY_Upper + MIDPOSY)
2183 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2184 local_player->jy - MIDPOSY :
2189 CloseDoor(DOOR_CLOSE_1);
2191 /* !!! FIX THIS (START) !!! */
2192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2194 InitGameEngine_EM();
2201 /* after drawing the level, correct some elements */
2202 if (game.timegate_time_left == 0)
2203 CloseAllOpenTimegates();
2205 if (setup.soft_scrolling)
2206 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2208 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2211 /* !!! FIX THIS (END) !!! */
2213 /* copy default game door content to main double buffer */
2214 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2215 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2217 DrawGameDoorValues();
2221 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2222 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2223 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2227 /* copy actual game door content to door double buffer for OpenDoor() */
2228 BlitBitmap(drawto, bitmap_db_door,
2229 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2231 OpenDoor(DOOR_OPEN_ALL);
2233 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2235 if (setup.sound_music)
2238 KeyboardAutoRepeatOffUnlessAutoplay();
2242 for (i = 0; i < MAX_PLAYERS; i++)
2243 printf("Player %d %sactive.\n",
2244 i + 1, (stored_player[i].active ? "" : "not "));
2248 printf("::: starting game [%d]\n", FrameCounter);
2252 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2254 /* this is used for non-R'n'D game engines to update certain engine values */
2256 /* needed to determine if sounds are played within the visible screen area */
2257 scroll_x = actual_scroll_x;
2258 scroll_y = actual_scroll_y;
2261 void InitMovDir(int x, int y)
2263 int i, element = Feld[x][y];
2264 static int xy[4][2] =
2271 static int direction[3][4] =
2273 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2274 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2275 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2284 Feld[x][y] = EL_BUG;
2285 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2288 case EL_SPACESHIP_RIGHT:
2289 case EL_SPACESHIP_UP:
2290 case EL_SPACESHIP_LEFT:
2291 case EL_SPACESHIP_DOWN:
2292 Feld[x][y] = EL_SPACESHIP;
2293 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2296 case EL_BD_BUTTERFLY_RIGHT:
2297 case EL_BD_BUTTERFLY_UP:
2298 case EL_BD_BUTTERFLY_LEFT:
2299 case EL_BD_BUTTERFLY_DOWN:
2300 Feld[x][y] = EL_BD_BUTTERFLY;
2301 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2304 case EL_BD_FIREFLY_RIGHT:
2305 case EL_BD_FIREFLY_UP:
2306 case EL_BD_FIREFLY_LEFT:
2307 case EL_BD_FIREFLY_DOWN:
2308 Feld[x][y] = EL_BD_FIREFLY;
2309 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2312 case EL_PACMAN_RIGHT:
2314 case EL_PACMAN_LEFT:
2315 case EL_PACMAN_DOWN:
2316 Feld[x][y] = EL_PACMAN;
2317 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2320 case EL_SP_SNIKSNAK:
2321 MovDir[x][y] = MV_UP;
2324 case EL_SP_ELECTRON:
2325 MovDir[x][y] = MV_LEFT;
2332 Feld[x][y] = EL_MOLE;
2333 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2337 if (IS_CUSTOM_ELEMENT(element))
2339 struct ElementInfo *ei = &element_info[element];
2340 int move_direction_initial = ei->move_direction_initial;
2341 int move_pattern = ei->move_pattern;
2343 if (move_direction_initial == MV_START_PREVIOUS)
2345 if (MovDir[x][y] != MV_NO_MOVING)
2348 move_direction_initial = MV_START_AUTOMATIC;
2351 if (move_direction_initial == MV_START_RANDOM)
2352 MovDir[x][y] = 1 << RND(4);
2353 else if (move_direction_initial & MV_ANY_DIRECTION)
2354 MovDir[x][y] = move_direction_initial;
2355 else if (move_pattern == MV_ALL_DIRECTIONS ||
2356 move_pattern == MV_TURNING_LEFT ||
2357 move_pattern == MV_TURNING_RIGHT ||
2358 move_pattern == MV_TURNING_LEFT_RIGHT ||
2359 move_pattern == MV_TURNING_RIGHT_LEFT ||
2360 move_pattern == MV_TURNING_RANDOM)
2361 MovDir[x][y] = 1 << RND(4);
2362 else if (move_pattern == MV_HORIZONTAL)
2363 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2364 else if (move_pattern == MV_VERTICAL)
2365 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2366 else if (move_pattern & MV_ANY_DIRECTION)
2367 MovDir[x][y] = element_info[element].move_pattern;
2368 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2369 move_pattern == MV_ALONG_RIGHT_SIDE)
2372 /* use random direction as default start direction */
2373 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2374 MovDir[x][y] = 1 << RND(4);
2377 for (i = 0; i < NUM_DIRECTIONS; i++)
2379 int x1 = x + xy[i][0];
2380 int y1 = y + xy[i][1];
2382 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2384 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2385 MovDir[x][y] = direction[0][i];
2387 MovDir[x][y] = direction[1][i];
2396 MovDir[x][y] = 1 << RND(4);
2398 if (element != EL_BUG &&
2399 element != EL_SPACESHIP &&
2400 element != EL_BD_BUTTERFLY &&
2401 element != EL_BD_FIREFLY)
2404 for (i = 0; i < NUM_DIRECTIONS; i++)
2406 int x1 = x + xy[i][0];
2407 int y1 = y + xy[i][1];
2409 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2411 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2413 MovDir[x][y] = direction[0][i];
2416 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2417 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2419 MovDir[x][y] = direction[1][i];
2428 GfxDir[x][y] = MovDir[x][y];
2431 void InitAmoebaNr(int x, int y)
2434 int group_nr = AmoebeNachbarNr(x, y);
2438 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2440 if (AmoebaCnt[i] == 0)
2448 AmoebaNr[x][y] = group_nr;
2449 AmoebaCnt[group_nr]++;
2450 AmoebaCnt2[group_nr]++;
2456 boolean raise_level = FALSE;
2458 if (local_player->MovPos)
2462 if (tape.auto_play) /* tape might already be stopped here */
2463 tape.auto_play_level_solved = TRUE;
2465 if (tape.playing && tape.auto_play)
2466 tape.auto_play_level_solved = TRUE;
2469 local_player->LevelSolved = FALSE;
2471 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2475 if (!tape.playing && setup.sound_loops)
2476 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2477 SND_CTRL_PLAY_LOOP);
2479 while (TimeLeft > 0)
2481 if (!tape.playing && !setup.sound_loops)
2482 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2483 if (TimeLeft > 0 && !(TimeLeft % 10))
2484 RaiseScore(level.score[SC_TIME_BONUS]);
2485 if (TimeLeft > 100 && !(TimeLeft % 10))
2490 DrawGameValue_Time(TimeLeft);
2498 if (!tape.playing && setup.sound_loops)
2499 StopSound(SND_GAME_LEVELTIME_BONUS);
2501 else if (level.time == 0) /* level without time limit */
2503 if (!tape.playing && setup.sound_loops)
2504 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2505 SND_CTRL_PLAY_LOOP);
2507 while (TimePlayed < 999)
2509 if (!tape.playing && !setup.sound_loops)
2510 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2511 if (TimePlayed < 999 && !(TimePlayed % 10))
2512 RaiseScore(level.score[SC_TIME_BONUS]);
2513 if (TimePlayed < 900 && !(TimePlayed % 10))
2518 DrawGameValue_Time(TimePlayed);
2526 if (!tape.playing && setup.sound_loops)
2527 StopSound(SND_GAME_LEVELTIME_BONUS);
2530 /* close exit door after last player */
2531 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2532 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2533 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2535 int element = Feld[ExitX][ExitY];
2537 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2538 EL_SP_EXIT_CLOSING);
2540 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2543 /* Hero disappears */
2544 if (ExitX >= 0 && ExitY >= 0)
2545 DrawLevelField(ExitX, ExitY);
2552 CloseDoor(DOOR_CLOSE_1);
2557 SaveTape(tape.level_nr); /* Ask to save tape */
2560 if (level_nr == leveldir_current->handicap_level)
2562 leveldir_current->handicap_level++;
2563 SaveLevelSetup_SeriesInfo();
2566 if (level_editor_test_game)
2567 local_player->score = -1; /* no highscore when playing from editor */
2568 else if (level_nr < leveldir_current->last_level)
2569 raise_level = TRUE; /* advance to next level */
2571 if ((hi_pos = NewHiScore()) >= 0)
2573 game_status = GAME_MODE_SCORES;
2574 DrawHallOfFame(hi_pos);
2583 game_status = GAME_MODE_MAIN;
2600 LoadScore(level_nr);
2602 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2603 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2606 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2608 if (local_player->score > highscore[k].Score)
2610 /* player has made it to the hall of fame */
2612 if (k < MAX_SCORE_ENTRIES - 1)
2614 int m = MAX_SCORE_ENTRIES - 1;
2617 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2618 if (!strcmp(setup.player_name, highscore[l].Name))
2620 if (m == k) /* player's new highscore overwrites his old one */
2624 for (l = m; l > k; l--)
2626 strcpy(highscore[l].Name, highscore[l - 1].Name);
2627 highscore[l].Score = highscore[l - 1].Score;
2634 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2635 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2636 highscore[k].Score = local_player->score;
2642 else if (!strncmp(setup.player_name, highscore[k].Name,
2643 MAX_PLAYER_NAME_LEN))
2644 break; /* player already there with a higher score */
2650 SaveScore(level_nr);
2655 inline static int getElementMoveStepsize(int x, int y)
2657 int element = Feld[x][y];
2658 int direction = MovDir[x][y];
2659 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2660 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2661 int horiz_move = (dx != 0);
2662 int sign = (horiz_move ? dx : dy);
2663 int step = sign * element_info[element].move_stepsize;
2665 /* special values for move stepsize for spring and things on conveyor belt */
2669 if (element == EL_SPRING)
2670 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2671 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2672 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2673 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2675 if (CAN_FALL(element) &&
2676 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2677 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2678 else if (element == EL_SPRING)
2679 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2686 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2688 if (player->GfxAction != action || player->GfxDir != dir)
2691 printf("Player frame reset! (%d => %d, %d => %d)\n",
2692 player->GfxAction, action, player->GfxDir, dir);
2695 player->GfxAction = action;
2696 player->GfxDir = dir;
2698 player->StepFrame = 0;
2702 static void ResetRandomAnimationValue(int x, int y)
2704 GfxRandom[x][y] = INIT_GFX_RANDOM();
2707 static void ResetGfxAnimation(int x, int y)
2710 GfxAction[x][y] = ACTION_DEFAULT;
2711 GfxDir[x][y] = MovDir[x][y];
2714 void InitMovingField(int x, int y, int direction)
2716 int element = Feld[x][y];
2717 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2718 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2722 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2723 ResetGfxAnimation(x, y);
2725 #if USE_CAN_MOVE_NOT_MOVING
2727 MovDir[x][y] = direction;
2728 GfxDir[x][y] = direction;
2729 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2730 ACTION_FALLING : ACTION_MOVING);
2732 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2734 if (Feld[newx][newy] == EL_EMPTY)
2735 Feld[newx][newy] = EL_BLOCKED;
2737 MovDir[newx][newy] = MovDir[x][y];
2738 GfxFrame[newx][newy] = GfxFrame[x][y];
2739 GfxRandom[newx][newy] = GfxRandom[x][y];
2740 GfxAction[newx][newy] = GfxAction[x][y];
2741 GfxDir[newx][newy] = GfxDir[x][y];
2746 MovDir[newx][newy] = MovDir[x][y] = direction;
2747 GfxDir[x][y] = direction;
2749 if (Feld[newx][newy] == EL_EMPTY)
2750 Feld[newx][newy] = EL_BLOCKED;
2752 if (direction == MV_DOWN && CAN_FALL(element))
2753 GfxAction[x][y] = ACTION_FALLING;
2755 GfxAction[x][y] = ACTION_MOVING;
2757 GfxFrame[newx][newy] = GfxFrame[x][y];
2758 GfxRandom[newx][newy] = GfxRandom[x][y];
2759 GfxAction[newx][newy] = GfxAction[x][y];
2760 GfxDir[newx][newy] = GfxDir[x][y];
2764 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2766 int direction = MovDir[x][y];
2767 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2768 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2774 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2776 int oldx = x, oldy = y;
2777 int direction = MovDir[x][y];
2779 if (direction == MV_LEFT)
2781 else if (direction == MV_RIGHT)
2783 else if (direction == MV_UP)
2785 else if (direction == MV_DOWN)
2788 *comes_from_x = oldx;
2789 *comes_from_y = oldy;
2792 int MovingOrBlocked2Element(int x, int y)
2794 int element = Feld[x][y];
2796 if (element == EL_BLOCKED)
2800 Blocked2Moving(x, y, &oldx, &oldy);
2801 return Feld[oldx][oldy];
2807 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2809 /* like MovingOrBlocked2Element(), but if element is moving
2810 and (x,y) is the field the moving element is just leaving,
2811 return EL_BLOCKED instead of the element value */
2812 int element = Feld[x][y];
2814 if (IS_MOVING(x, y))
2816 if (element == EL_BLOCKED)
2820 Blocked2Moving(x, y, &oldx, &oldy);
2821 return Feld[oldx][oldy];
2830 static void RemoveField(int x, int y)
2832 Feld[x][y] = EL_EMPTY;
2839 ChangeDelay[x][y] = 0;
2840 ChangePage[x][y] = -1;
2841 Pushed[x][y] = FALSE;
2844 ExplodeField[x][y] = EX_TYPE_NONE;
2847 GfxElement[x][y] = EL_UNDEFINED;
2848 GfxAction[x][y] = ACTION_DEFAULT;
2849 GfxDir[x][y] = MV_NO_MOVING;
2852 void RemoveMovingField(int x, int y)
2854 int oldx = x, oldy = y, newx = x, newy = y;
2855 int element = Feld[x][y];
2856 int next_element = EL_UNDEFINED;
2858 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2861 if (IS_MOVING(x, y))
2863 Moving2Blocked(x, y, &newx, &newy);
2865 if (Feld[newx][newy] != EL_BLOCKED)
2868 if (Feld[newx][newy] != EL_BLOCKED)
2870 /* element is moving, but target field is not free (blocked), but
2871 already occupied by something different (example: acid pool);
2872 in this case, only remove the moving field, but not the target */
2874 RemoveField(oldx, oldy);
2876 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2878 DrawLevelField(oldx, oldy);
2884 else if (element == EL_BLOCKED)
2886 Blocked2Moving(x, y, &oldx, &oldy);
2887 if (!IS_MOVING(oldx, oldy))
2891 if (element == EL_BLOCKED &&
2892 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2893 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2894 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2895 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2896 next_element = get_next_element(Feld[oldx][oldy]);
2898 RemoveField(oldx, oldy);
2899 RemoveField(newx, newy);
2901 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2903 if (next_element != EL_UNDEFINED)
2904 Feld[oldx][oldy] = next_element;
2906 DrawLevelField(oldx, oldy);
2907 DrawLevelField(newx, newy);
2910 void DrawDynamite(int x, int y)
2912 int sx = SCREENX(x), sy = SCREENY(y);
2913 int graphic = el2img(Feld[x][y]);
2916 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2919 if (IS_WALKABLE_INSIDE(Back[x][y]))
2923 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2924 else if (Store[x][y])
2925 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2927 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2930 if (Back[x][y] || Store[x][y])
2931 DrawGraphicThruMask(sx, sy, graphic, frame);
2933 DrawGraphic(sx, sy, graphic, frame);
2935 if (game.emulation == EMU_SUPAPLEX)
2936 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2937 else if (Store[x][y])
2938 DrawGraphicThruMask(sx, sy, graphic, frame);
2940 DrawGraphic(sx, sy, graphic, frame);
2944 void CheckDynamite(int x, int y)
2946 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2950 if (MovDelay[x][y] != 0)
2953 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2960 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2962 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2963 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2964 StopSound(SND_DYNAMITE_ACTIVE);
2966 StopSound(SND_DYNABOMB_ACTIVE);
2972 void DrawRelocatePlayer(struct PlayerInfo *player)
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2977 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2978 int jx = player->jx;
2979 int jy = player->jy;
2981 if (level.instant_relocation)
2984 int offset = (setup.scroll_delay ? 3 : 0);
2986 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2988 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2989 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2990 local_player->jx - MIDPOSX);
2992 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2993 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2994 local_player->jy - MIDPOSY);
2998 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2999 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3000 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3002 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3003 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3004 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3006 /* don't scroll over playfield boundaries */
3007 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3008 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3010 /* don't scroll over playfield boundaries */
3011 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3012 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3015 scroll_x += (local_player->jx - old_jx);
3016 scroll_y += (local_player->jy - old_jy);
3018 /* don't scroll over playfield boundaries */
3019 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3020 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3022 /* don't scroll over playfield boundaries */
3023 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3024 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3027 RedrawPlayfield(TRUE, 0,0,0,0);
3033 int offset = (setup.scroll_delay ? 3 : 0);
3035 int scroll_xx = -999, scroll_yy = -999;
3037 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3039 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3042 int fx = FX, fy = FY;
3044 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3045 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3046 local_player->jx - MIDPOSX);
3048 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3049 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3050 local_player->jy - MIDPOSY);
3052 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3053 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3056 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3059 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3066 fx += dx * TILEX / 2;
3067 fy += dy * TILEY / 2;
3069 ScrollLevel(dx, dy);
3072 /* scroll in two steps of half tile size to make things smoother */
3073 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3075 Delay(wait_delay_value);
3077 /* scroll second step to align at full tile size */
3079 Delay(wait_delay_value);
3082 int scroll_xx = -999, scroll_yy = -999;
3084 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3086 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3089 int fx = FX, fy = FY;
3091 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3092 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3093 local_player->jx - MIDPOSX);
3095 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3096 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3097 local_player->jy - MIDPOSY);
3099 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3100 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3103 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3106 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3113 fx += dx * TILEX / 2;
3114 fy += dy * TILEY / 2;
3116 ScrollLevel(dx, dy);
3119 /* scroll in two steps of half tile size to make things smoother */
3120 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3122 Delay(wait_delay_value);
3124 /* scroll second step to align at full tile size */
3126 Delay(wait_delay_value);
3132 Delay(wait_delay_value);
3136 void RelocatePlayer(int jx, int jy, int el_player_raw)
3139 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3141 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3143 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3144 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3145 boolean no_delay = (tape.warp_forward);
3146 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3147 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3148 int old_jx = player->jx;
3149 int old_jy = player->jy;
3150 int old_element = Feld[old_jx][old_jy];
3151 int element = Feld[jx][jy];
3152 boolean player_relocated = (old_jx != jx || old_jy != jy);
3154 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3155 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3157 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3158 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3159 int leave_side_horiz = move_dir_horiz;
3160 int leave_side_vert = move_dir_vert;
3162 static int trigger_sides[4][2] =
3164 /* enter side leave side */
3165 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3166 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3167 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3168 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3170 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3171 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3172 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3173 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3175 int enter_side = enter_side_horiz | enter_side_vert;
3176 int leave_side = leave_side_horiz | leave_side_vert;
3178 if (player->GameOver) /* do not reanimate dead player */
3181 if (!player_relocated) /* no need to relocate the player */
3184 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3186 RemoveField(jx, jy); /* temporarily remove newly placed player */
3187 DrawLevelField(jx, jy);
3190 if (player->present)
3192 while (player->MovPos)
3194 ScrollPlayer(player, SCROLL_GO_ON);
3195 ScrollScreen(NULL, SCROLL_GO_ON);
3197 #if USE_NEW_MOVE_DELAY
3198 AdvanceFrameAndPlayerCounters(player->index_nr);
3206 Delay(wait_delay_value);
3209 DrawPlayer(player); /* needed here only to cleanup last field */
3210 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3212 player->is_moving = FALSE;
3216 if (IS_CUSTOM_ELEMENT(old_element))
3217 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3219 player->index_bit, leave_side);
3221 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3223 player->index_bit, leave_side);
3226 Feld[jx][jy] = el_player;
3227 InitPlayerField(jx, jy, el_player, TRUE);
3229 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3231 Feld[jx][jy] = element;
3232 InitField(jx, jy, FALSE);
3236 if (player == local_player) /* only visually relocate local player */
3237 DrawRelocatePlayer(player);
3241 TestIfHeroTouchesBadThing(jx, jy);
3242 TestIfPlayerTouchesCustomElement(jx, jy);
3246 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3251 /* needed to allow change of walkable custom element by entering player */
3252 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3253 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3255 /* needed to allow change of walkable custom element by entering player */
3256 Changed[jx][jy] = 0; /* allow another change */
3261 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3262 enter_side == MV_LEFT ? "left" :
3263 enter_side == MV_RIGHT ? "right" :
3264 enter_side == MV_UP ? "top" :
3265 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3269 if (IS_CUSTOM_ELEMENT(element))
3270 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3271 player->index_bit, enter_side);
3273 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3275 player->index_bit, enter_side);
3279 void Explode(int ex, int ey, int phase, int mode)
3286 /* !!! eliminate this variable !!! */
3287 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3292 int last_phase = num_phase * delay;
3293 int half_phase = (num_phase / 2) * delay;
3294 int first_phase_after_start = EX_PHASE_START + 1;
3298 if (game.explosions_delayed)
3300 ExplodeField[ex][ey] = mode;
3304 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3306 int center_element = Feld[ex][ey];
3309 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3313 /* --- This is only really needed (and now handled) in "Impact()". --- */
3314 /* do not explode moving elements that left the explode field in time */
3315 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3316 center_element == EL_EMPTY &&
3317 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3322 if (mode == EX_TYPE_NORMAL ||
3323 mode == EX_TYPE_CENTER ||
3324 mode == EX_TYPE_CROSS)
3325 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3327 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3328 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3331 /* remove things displayed in background while burning dynamite */
3332 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3335 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3337 /* put moving element to center field (and let it explode there) */
3338 center_element = MovingOrBlocked2Element(ex, ey);
3339 RemoveMovingField(ex, ey);
3340 Feld[ex][ey] = center_element;
3346 last_phase = element_info[center_element].explosion_delay + 1;
3348 last_phase = element_info[center_element].explosion_delay;
3352 printf("::: %d -> %d\n", center_element, last_phase);
3356 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3358 int xx = x - ex + 1;
3359 int yy = y - ey + 1;
3364 if (!IN_LEV_FIELD(x, y) ||
3365 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3366 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3369 if (!IN_LEV_FIELD(x, y) ||
3370 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3374 if (!IN_LEV_FIELD(x, y) ||
3375 ((mode != EX_TYPE_NORMAL ||
3376 center_element == EL_AMOEBA_TO_DIAMOND) &&
3377 (x != ex || y != ey)))
3381 element = Feld[x][y];
3383 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3385 element = MovingOrBlocked2Element(x, y);
3387 if (!IS_EXPLOSION_PROOF(element))
3388 RemoveMovingField(x, y);
3394 if (IS_EXPLOSION_PROOF(element))
3397 /* indestructible elements can only explode in center (but not flames) */
3399 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3400 mode == EX_TYPE_BORDER)) ||
3401 element == EL_FLAMES)
3404 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3405 element == EL_FLAMES)
3411 if ((IS_INDESTRUCTIBLE(element) &&
3412 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3413 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3414 element == EL_FLAMES)
3418 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3419 behaviour, for example when touching a yamyam that explodes to rocks
3420 with active deadly shield, a rock is created under the player !!! */
3421 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3423 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3424 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3425 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3427 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3430 if (IS_ACTIVE_BOMB(element))
3432 /* re-activate things under the bomb like gate or penguin */
3434 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3437 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3442 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3443 element_info[Feld[x][y]].token_name,
3444 Store[x][y], Store2[x][y]);
3451 /* save walkable background elements while explosion on same tile */
3453 if (IS_INDESTRUCTIBLE(element))
3454 Back[x][y] = element;
3458 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3459 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3460 Back[x][y] = element;
3462 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3463 (x != ex || y != ey))
3464 Back[x][y] = element;
3467 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3468 Back[x][y] = element;
3472 /* ignite explodable elements reached by other explosion */
3473 if (element == EL_EXPLOSION)
3474 element = Store2[x][y];
3477 if (AmoebaNr[x][y] &&
3478 (element == EL_AMOEBA_FULL ||
3479 element == EL_BD_AMOEBA ||
3480 element == EL_AMOEBA_GROWING))
3482 AmoebaCnt[AmoebaNr[x][y]]--;
3483 AmoebaCnt2[AmoebaNr[x][y]]--;
3489 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3491 switch(StorePlayer[ex][ey])
3494 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3497 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3500 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3504 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3509 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3510 Store[x][y] = EL_EMPTY;
3512 if (game.emulation == EMU_SUPAPLEX)
3513 Store[x][y] = EL_EMPTY;
3516 else if (center_element == EL_MOLE)
3517 Store[x][y] = EL_EMERALD_RED;
3518 else if (center_element == EL_PENGUIN)
3519 Store[x][y] = EL_EMERALD_PURPLE;
3520 else if (center_element == EL_BUG)
3521 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3522 else if (center_element == EL_BD_BUTTERFLY)
3523 Store[x][y] = EL_BD_DIAMOND;
3524 else if (center_element == EL_SP_ELECTRON)
3525 Store[x][y] = EL_SP_INFOTRON;
3526 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3527 Store[x][y] = level.amoeba_content;
3528 else if (center_element == EL_YAMYAM)
3529 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3530 else if (IS_CUSTOM_ELEMENT(center_element) &&
3531 element_info[center_element].content[xx][yy] != EL_EMPTY)
3532 Store[x][y] = element_info[center_element].content[xx][yy];
3533 else if (element == EL_WALL_EMERALD)
3534 Store[x][y] = EL_EMERALD;
3535 else if (element == EL_WALL_DIAMOND)
3536 Store[x][y] = EL_DIAMOND;
3537 else if (element == EL_WALL_BD_DIAMOND)
3538 Store[x][y] = EL_BD_DIAMOND;
3539 else if (element == EL_WALL_EMERALD_YELLOW)
3540 Store[x][y] = EL_EMERALD_YELLOW;
3541 else if (element == EL_WALL_EMERALD_RED)
3542 Store[x][y] = EL_EMERALD_RED;
3543 else if (element == EL_WALL_EMERALD_PURPLE)
3544 Store[x][y] = EL_EMERALD_PURPLE;
3545 else if (element == EL_WALL_PEARL)
3546 Store[x][y] = EL_PEARL;
3547 else if (element == EL_WALL_CRYSTAL)
3548 Store[x][y] = EL_CRYSTAL;
3549 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3550 Store[x][y] = element_info[element].content[1][1];
3552 Store[x][y] = EL_EMPTY;
3554 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3555 center_element == EL_AMOEBA_TO_DIAMOND)
3556 Store2[x][y] = element;
3559 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3560 element_info[Store2[x][y]].token_name);
3564 if (AmoebaNr[x][y] &&
3565 (element == EL_AMOEBA_FULL ||
3566 element == EL_BD_AMOEBA ||
3567 element == EL_AMOEBA_GROWING))
3569 AmoebaCnt[AmoebaNr[x][y]]--;
3570 AmoebaCnt2[AmoebaNr[x][y]]--;
3576 MovDir[x][y] = MovPos[x][y] = 0;
3577 GfxDir[x][y] = MovDir[x][y];
3582 Feld[x][y] = EL_EXPLOSION;
3584 GfxElement[x][y] = center_element;
3586 GfxElement[x][y] = EL_UNDEFINED;
3589 ExplodePhase[x][y] = 1;
3591 ExplodeDelay[x][y] = last_phase;
3596 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3598 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3605 if (center_element == EL_YAMYAM)
3606 game.yamyam_content_nr =
3607 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3610 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3611 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3625 GfxFrame[x][y] = 0; /* restart explosion animation */
3629 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3633 last_phase = ExplodeDelay[x][y];
3636 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3640 /* activate this even in non-DEBUG version until cause for crash in
3641 getGraphicAnimationFrame() (see below) is found and eliminated */
3645 if (GfxElement[x][y] == EL_UNDEFINED)
3648 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3649 printf("Explode(): This should never happen!\n");
3652 GfxElement[x][y] = EL_EMPTY;
3658 border_element = Store2[x][y];
3660 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3661 border_element = StorePlayer[x][y];
3663 if (IS_PLAYER(x, y))
3664 border_element = StorePlayer[x][y];
3668 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3669 element_info[border_element].token_name, Store2[x][y]);
3673 printf("::: phase == %d\n", phase);
3676 if (phase == element_info[border_element].ignition_delay ||
3677 phase == last_phase)
3679 boolean border_explosion = FALSE;
3683 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3684 !PLAYER_EXPLOSION_PROTECTED(x, y))
3686 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3689 if (IS_PLAYER(x, y))
3692 KillHeroUnlessExplosionProtected(x, y);
3693 border_explosion = TRUE;
3696 if (phase == last_phase)
3697 printf("::: IS_PLAYER\n");
3700 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3703 printf("::: %d,%d: %d %s\n", x, y, border_element,
3704 element_info[border_element].token_name);
3707 Feld[x][y] = Store2[x][y];
3710 border_explosion = TRUE;
3713 if (phase == last_phase)
3714 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3717 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3719 AmoebeUmwandeln(x, y);
3721 border_explosion = TRUE;
3724 if (phase == last_phase)
3725 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3726 element_info[border_element].explosion_delay,
3727 element_info[border_element].ignition_delay,
3733 /* if an element just explodes due to another explosion (chain-reaction),
3734 do not immediately end the new explosion when it was the last frame of
3735 the explosion (as it would be done in the following "if"-statement!) */
3736 if (border_explosion && phase == last_phase)
3743 if (phase == first_phase_after_start)
3745 int element = Store2[x][y];
3747 if (element == EL_BLACK_ORB)
3749 Feld[x][y] = Store2[x][y];
3754 else if (phase == half_phase)
3756 int element = Store2[x][y];
3758 if (IS_PLAYER(x, y))
3759 KillHeroUnlessExplosionProtected(x, y);
3760 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3762 Feld[x][y] = Store2[x][y];
3766 else if (element == EL_AMOEBA_TO_DIAMOND)
3767 AmoebeUmwandeln(x, y);
3771 if (phase == last_phase)
3776 printf("::: done: phase == %d\n", phase);
3780 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3783 element = Feld[x][y] = Store[x][y];
3784 Store[x][y] = Store2[x][y] = 0;
3785 GfxElement[x][y] = EL_UNDEFINED;
3787 /* player can escape from explosions and might therefore be still alive */
3788 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3789 element <= EL_PLAYER_IS_EXPLODING_4)
3790 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3792 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3793 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3794 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3797 /* restore probably existing indestructible background element */
3798 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3799 element = Feld[x][y] = Back[x][y];
3802 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3803 GfxDir[x][y] = MV_NO_MOVING;
3804 ChangeDelay[x][y] = 0;
3805 ChangePage[x][y] = -1;
3808 InitField_WithBug2(x, y, FALSE);
3810 InitField(x, y, FALSE);
3812 /* !!! not needed !!! */
3814 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3815 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3818 if (CAN_MOVE(element))
3823 DrawLevelField(x, y);
3825 TestIfElementTouchesCustomElement(x, y);
3827 if (GFX_CRUMBLED(element))
3828 DrawLevelFieldCrumbledSandNeighbours(x, y);
3830 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3831 StorePlayer[x][y] = 0;
3833 if (ELEM_IS_PLAYER(element))
3834 RelocatePlayer(x, y, element);
3837 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3839 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3843 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3845 int stored = Store[x][y];
3846 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3847 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3851 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3853 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3857 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3861 printf("::: %d / %d [%d - %d]\n",
3862 GfxFrame[x][y], phase - delay, phase, delay);
3866 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3867 element_info[GfxElement[x][y]].token_name,
3872 DrawLevelFieldCrumbledSand(x, y);
3874 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3876 DrawLevelElement(x, y, Back[x][y]);
3877 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3879 else if (IS_WALKABLE_UNDER(Back[x][y]))
3881 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3882 DrawLevelElementThruMask(x, y, Back[x][y]);
3884 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3885 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3889 void DynaExplode(int ex, int ey)
3892 int dynabomb_element = Feld[ex][ey];
3893 int dynabomb_size = 1;
3894 boolean dynabomb_xl = FALSE;
3895 struct PlayerInfo *player;
3896 static int xy[4][2] =
3904 if (IS_ACTIVE_BOMB(dynabomb_element))
3906 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3907 dynabomb_size = player->dynabomb_size;
3908 dynabomb_xl = player->dynabomb_xl;
3909 player->dynabombs_left++;
3912 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3914 for (i = 0; i < NUM_DIRECTIONS; i++)
3916 for (j = 1; j <= dynabomb_size; j++)
3918 int x = ex + j * xy[i][0];
3919 int y = ey + j * xy[i][1];
3922 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3925 element = Feld[x][y];
3927 /* do not restart explosions of fields with active bombs */
3928 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3931 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3935 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3936 !IS_DIGGABLE(element) && !dynabomb_xl)
3939 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3940 !CAN_GROW_INTO(element) && !dynabomb_xl)
3944 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3945 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3946 element != EL_SAND && !dynabomb_xl)
3953 void Bang(int x, int y)
3956 int element = MovingOrBlocked2Element(x, y);
3958 int element = Feld[x][y];
3962 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3964 if (IS_PLAYER(x, y))
3967 struct PlayerInfo *player = PLAYERINFO(x, y);
3969 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3970 player->element_nr);
3975 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3977 if (game.emulation == EMU_SUPAPLEX)
3978 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3980 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3985 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3993 case EL_BD_BUTTERFLY:
3996 case EL_DARK_YAMYAM:
4000 RaiseScoreElement(element);
4001 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4003 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4004 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4005 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4006 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4007 case EL_DYNABOMB_INCREASE_NUMBER:
4008 case EL_DYNABOMB_INCREASE_SIZE:
4009 case EL_DYNABOMB_INCREASE_POWER:
4014 case EL_LAMP_ACTIVE:
4016 case EL_AMOEBA_TO_DIAMOND:
4018 if (IS_PLAYER(x, y))
4019 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4021 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4025 if (element_info[element].explosion_type == EXPLODES_CROSS)
4027 if (CAN_EXPLODE_CROSS(element))
4030 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4035 else if (element_info[element].explosion_type == EXPLODES_1X1)
4037 else if (CAN_EXPLODE_1X1(element))
4039 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4041 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4045 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4048 void SplashAcid(int x, int y)
4051 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4052 (!IN_LEV_FIELD(x - 1, y - 2) ||
4053 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4054 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4056 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4057 (!IN_LEV_FIELD(x + 1, y - 2) ||
4058 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4059 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4061 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4063 /* input: position of element entering acid (obsolete) */
4065 int element = Feld[x][y];
4067 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4070 if (element != EL_ACID_SPLASH_LEFT &&
4071 element != EL_ACID_SPLASH_RIGHT)
4073 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4075 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4076 (!IN_LEV_FIELD(x - 1, y - 1) ||
4077 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4078 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4080 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4081 (!IN_LEV_FIELD(x + 1, y - 1) ||
4082 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4083 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4088 static void InitBeltMovement()
4090 static int belt_base_element[4] =
4092 EL_CONVEYOR_BELT_1_LEFT,
4093 EL_CONVEYOR_BELT_2_LEFT,
4094 EL_CONVEYOR_BELT_3_LEFT,
4095 EL_CONVEYOR_BELT_4_LEFT
4097 static int belt_base_active_element[4] =
4099 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4100 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4101 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4102 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4107 /* set frame order for belt animation graphic according to belt direction */
4108 for (i = 0; i < NUM_BELTS; i++)
4112 for (j = 0; j < NUM_BELT_PARTS; j++)
4114 int element = belt_base_active_element[belt_nr] + j;
4115 int graphic = el2img(element);
4117 if (game.belt_dir[i] == MV_LEFT)
4118 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4120 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4124 for (y = 0; y < lev_fieldy; y++)
4126 for (x = 0; x < lev_fieldx; x++)
4128 int element = Feld[x][y];
4130 for (i = 0; i < NUM_BELTS; i++)
4132 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4134 int e_belt_nr = getBeltNrFromBeltElement(element);
4137 if (e_belt_nr == belt_nr)
4139 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4141 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4149 static void ToggleBeltSwitch(int x, int y)
4151 static int belt_base_element[4] =
4153 EL_CONVEYOR_BELT_1_LEFT,
4154 EL_CONVEYOR_BELT_2_LEFT,
4155 EL_CONVEYOR_BELT_3_LEFT,
4156 EL_CONVEYOR_BELT_4_LEFT
4158 static int belt_base_active_element[4] =
4160 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4161 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4162 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4163 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4165 static int belt_base_switch_element[4] =
4167 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4168 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4169 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4170 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4172 static int belt_move_dir[4] =
4180 int element = Feld[x][y];
4181 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4182 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4183 int belt_dir = belt_move_dir[belt_dir_nr];
4186 if (!IS_BELT_SWITCH(element))
4189 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4190 game.belt_dir[belt_nr] = belt_dir;
4192 if (belt_dir_nr == 3)
4195 /* set frame order for belt animation graphic according to belt direction */
4196 for (i = 0; i < NUM_BELT_PARTS; i++)
4198 int element = belt_base_active_element[belt_nr] + i;
4199 int graphic = el2img(element);
4201 if (belt_dir == MV_LEFT)
4202 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4204 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4207 for (yy = 0; yy < lev_fieldy; yy++)
4209 for (xx = 0; xx < lev_fieldx; xx++)
4211 int element = Feld[xx][yy];
4213 if (IS_BELT_SWITCH(element))
4215 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4217 if (e_belt_nr == belt_nr)
4219 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4220 DrawLevelField(xx, yy);
4223 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4225 int e_belt_nr = getBeltNrFromBeltElement(element);
4227 if (e_belt_nr == belt_nr)
4229 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4231 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4232 DrawLevelField(xx, yy);
4235 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4237 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4239 if (e_belt_nr == belt_nr)
4241 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4243 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4244 DrawLevelField(xx, yy);
4251 static void ToggleSwitchgateSwitch(int x, int y)
4255 game.switchgate_pos = !game.switchgate_pos;
4257 for (yy = 0; yy < lev_fieldy; yy++)
4259 for (xx = 0; xx < lev_fieldx; xx++)
4261 int element = Feld[xx][yy];
4263 if (element == EL_SWITCHGATE_SWITCH_UP ||
4264 element == EL_SWITCHGATE_SWITCH_DOWN)
4266 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4267 DrawLevelField(xx, yy);
4269 else if (element == EL_SWITCHGATE_OPEN ||
4270 element == EL_SWITCHGATE_OPENING)
4272 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4274 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4276 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4279 else if (element == EL_SWITCHGATE_CLOSED ||
4280 element == EL_SWITCHGATE_CLOSING)
4282 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4284 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4286 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4293 static int getInvisibleActiveFromInvisibleElement(int element)
4295 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4296 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4297 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4301 static int getInvisibleFromInvisibleActiveElement(int element)
4303 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4304 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4305 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4309 static void RedrawAllLightSwitchesAndInvisibleElements()
4313 for (y = 0; y < lev_fieldy; y++)
4315 for (x = 0; x < lev_fieldx; x++)
4317 int element = Feld[x][y];
4319 if (element == EL_LIGHT_SWITCH &&
4320 game.light_time_left > 0)
4322 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4323 DrawLevelField(x, y);
4325 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4326 game.light_time_left == 0)
4328 Feld[x][y] = EL_LIGHT_SWITCH;
4329 DrawLevelField(x, y);
4331 else if (element == EL_INVISIBLE_STEELWALL ||
4332 element == EL_INVISIBLE_WALL ||
4333 element == EL_INVISIBLE_SAND)
4335 if (game.light_time_left > 0)
4336 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4338 DrawLevelField(x, y);
4340 /* uncrumble neighbour fields, if needed */
4341 if (element == EL_INVISIBLE_SAND)
4342 DrawLevelFieldCrumbledSandNeighbours(x, y);
4344 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4345 element == EL_INVISIBLE_WALL_ACTIVE ||
4346 element == EL_INVISIBLE_SAND_ACTIVE)
4348 if (game.light_time_left == 0)
4349 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4351 DrawLevelField(x, y);
4353 /* re-crumble neighbour fields, if needed */
4354 if (element == EL_INVISIBLE_SAND)
4355 DrawLevelFieldCrumbledSandNeighbours(x, y);
4361 static void ToggleLightSwitch(int x, int y)
4363 int element = Feld[x][y];
4365 game.light_time_left =
4366 (element == EL_LIGHT_SWITCH ?
4367 level.time_light * FRAMES_PER_SECOND : 0);
4369 RedrawAllLightSwitchesAndInvisibleElements();
4372 static void ActivateTimegateSwitch(int x, int y)
4376 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4378 for (yy = 0; yy < lev_fieldy; yy++)
4380 for (xx = 0; xx < lev_fieldx; xx++)
4382 int element = Feld[xx][yy];
4384 if (element == EL_TIMEGATE_CLOSED ||
4385 element == EL_TIMEGATE_CLOSING)
4387 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4388 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4392 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4394 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4395 DrawLevelField(xx, yy);
4402 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4405 void Impact(int x, int y)
4407 boolean lastline = (y == lev_fieldy-1);
4408 boolean object_hit = FALSE;
4409 boolean impact = (lastline || object_hit);
4410 int element = Feld[x][y];
4411 int smashed = EL_STEELWALL;
4414 printf("IMPACT!\n");
4417 if (!lastline) /* check if element below was hit */
4419 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4422 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4423 MovDir[x][y + 1] != MV_DOWN ||
4424 MovPos[x][y + 1] <= TILEY / 2));
4427 object_hit = !IS_FREE(x, y + 1);
4430 /* do not smash moving elements that left the smashed field in time */
4431 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4432 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4436 smashed = MovingOrBlocked2Element(x, y + 1);
4438 impact = (lastline || object_hit);
4441 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4443 SplashAcid(x, y + 1);
4447 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4448 /* only reset graphic animation if graphic really changes after impact */
4450 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4452 ResetGfxAnimation(x, y);
4453 DrawLevelField(x, y);
4456 if (impact && CAN_EXPLODE_IMPACT(element))
4461 else if (impact && element == EL_PEARL)
4463 ResetGfxAnimation(x, y);
4465 Feld[x][y] = EL_PEARL_BREAKING;
4466 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4469 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4471 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4476 if (impact && element == EL_AMOEBA_DROP)
4478 if (object_hit && IS_PLAYER(x, y + 1))
4479 KillHeroUnlessEnemyProtected(x, y + 1);
4480 else if (object_hit && smashed == EL_PENGUIN)
4484 Feld[x][y] = EL_AMOEBA_GROWING;
4485 Store[x][y] = EL_AMOEBA_WET;
4487 ResetRandomAnimationValue(x, y);
4492 if (object_hit) /* check which object was hit */
4494 if (CAN_PASS_MAGIC_WALL(element) &&
4495 (smashed == EL_MAGIC_WALL ||
4496 smashed == EL_BD_MAGIC_WALL))
4499 int activated_magic_wall =
4500 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4501 EL_BD_MAGIC_WALL_ACTIVE);
4503 /* activate magic wall / mill */
4504 for (yy = 0; yy < lev_fieldy; yy++)
4505 for (xx = 0; xx < lev_fieldx; xx++)
4506 if (Feld[xx][yy] == smashed)
4507 Feld[xx][yy] = activated_magic_wall;
4509 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4510 game.magic_wall_active = TRUE;
4512 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4513 SND_MAGIC_WALL_ACTIVATING :
4514 SND_BD_MAGIC_WALL_ACTIVATING));
4517 if (IS_PLAYER(x, y + 1))
4519 if (CAN_SMASH_PLAYER(element))
4521 KillHeroUnlessEnemyProtected(x, y + 1);
4525 else if (smashed == EL_PENGUIN)
4527 if (CAN_SMASH_PLAYER(element))
4533 else if (element == EL_BD_DIAMOND)
4535 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4541 else if (((element == EL_SP_INFOTRON ||
4542 element == EL_SP_ZONK) &&
4543 (smashed == EL_SP_SNIKSNAK ||
4544 smashed == EL_SP_ELECTRON ||
4545 smashed == EL_SP_DISK_ORANGE)) ||
4546 (element == EL_SP_INFOTRON &&
4547 smashed == EL_SP_DISK_YELLOW))
4553 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4559 else if (CAN_SMASH_EVERYTHING(element))
4561 if (IS_CLASSIC_ENEMY(smashed) ||
4562 CAN_EXPLODE_SMASHED(smashed))
4567 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4569 if (smashed == EL_LAMP ||
4570 smashed == EL_LAMP_ACTIVE)
4575 else if (smashed == EL_NUT)
4577 Feld[x][y + 1] = EL_NUT_BREAKING;
4578 PlayLevelSound(x, y, SND_NUT_BREAKING);
4579 RaiseScoreElement(EL_NUT);
4582 else if (smashed == EL_PEARL)
4584 ResetGfxAnimation(x, y);
4586 Feld[x][y + 1] = EL_PEARL_BREAKING;
4587 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4590 else if (smashed == EL_DIAMOND)
4592 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4593 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4596 else if (IS_BELT_SWITCH(smashed))
4598 ToggleBeltSwitch(x, y + 1);
4600 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4601 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4603 ToggleSwitchgateSwitch(x, y + 1);
4605 else if (smashed == EL_LIGHT_SWITCH ||
4606 smashed == EL_LIGHT_SWITCH_ACTIVE)
4608 ToggleLightSwitch(x, y + 1);
4613 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4616 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4619 /* !!! TEST ONLY !!! */
4620 CheckElementChangeBySide(x, y + 1, smashed, element,
4621 CE_SWITCHED, CH_SIDE_TOP);
4622 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4623 CE_SWITCH_OF_X, CH_SIDE_TOP);
4625 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4626 CE_SWITCH_OF_X, CH_SIDE_TOP);
4627 CheckElementChangeBySide(x, y + 1, smashed, element,
4628 CE_SWITCHED, CH_SIDE_TOP);
4634 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4639 /* play sound of magic wall / mill */
4641 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4642 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4644 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4645 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4646 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4647 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4652 /* play sound of object that hits the ground */
4653 if (lastline || object_hit)
4654 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4657 inline static void TurnRoundExt(int x, int y)
4669 { 0, 0 }, { 0, 0 }, { 0, 0 },
4674 int left, right, back;
4678 { MV_DOWN, MV_UP, MV_RIGHT },
4679 { MV_UP, MV_DOWN, MV_LEFT },
4681 { MV_LEFT, MV_RIGHT, MV_DOWN },
4685 { MV_RIGHT, MV_LEFT, MV_UP }
4688 int element = Feld[x][y];
4689 int move_pattern = element_info[element].move_pattern;
4691 int old_move_dir = MovDir[x][y];
4692 int left_dir = turn[old_move_dir].left;
4693 int right_dir = turn[old_move_dir].right;
4694 int back_dir = turn[old_move_dir].back;
4696 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4697 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4698 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4699 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4701 int left_x = x + left_dx, left_y = y + left_dy;
4702 int right_x = x + right_dx, right_y = y + right_dy;
4703 int move_x = x + move_dx, move_y = y + move_dy;
4707 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4709 TestIfBadThingTouchesOtherBadThing(x, y);
4711 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4712 MovDir[x][y] = right_dir;
4713 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4714 MovDir[x][y] = left_dir;
4716 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4718 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4722 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4723 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4725 TestIfBadThingTouchesOtherBadThing(x, y);
4727 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4728 MovDir[x][y] = left_dir;
4729 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4730 MovDir[x][y] = right_dir;
4732 if ((element == EL_SPACESHIP ||
4733 element == EL_SP_SNIKSNAK ||
4734 element == EL_SP_ELECTRON)
4735 && MovDir[x][y] != old_move_dir)
4737 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4741 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4743 TestIfBadThingTouchesOtherBadThing(x, y);
4745 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4746 MovDir[x][y] = left_dir;
4747 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4748 MovDir[x][y] = right_dir;
4750 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4752 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4755 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4757 TestIfBadThingTouchesOtherBadThing(x, y);
4759 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4760 MovDir[x][y] = left_dir;
4761 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4762 MovDir[x][y] = right_dir;
4764 if (MovDir[x][y] != old_move_dir)
4768 else if (element == EL_YAMYAM)
4770 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4771 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4773 if (can_turn_left && can_turn_right)
4774 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4775 else if (can_turn_left)
4776 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4777 else if (can_turn_right)
4778 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4780 MovDir[x][y] = back_dir;
4782 MovDelay[x][y] = 16 + 16 * RND(3);
4784 else if (element == EL_DARK_YAMYAM)
4786 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4788 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4791 if (can_turn_left && can_turn_right)
4792 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4793 else if (can_turn_left)
4794 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4795 else if (can_turn_right)
4796 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4798 MovDir[x][y] = back_dir;
4800 MovDelay[x][y] = 16 + 16 * RND(3);
4802 else if (element == EL_PACMAN)
4804 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4805 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4807 if (can_turn_left && can_turn_right)
4808 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4809 else if (can_turn_left)
4810 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4811 else if (can_turn_right)
4812 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4814 MovDir[x][y] = back_dir;
4816 MovDelay[x][y] = 6 + RND(40);
4818 else if (element == EL_PIG)
4820 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4821 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4822 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4823 boolean should_turn_left, should_turn_right, should_move_on;
4825 int rnd = RND(rnd_value);
4827 should_turn_left = (can_turn_left &&
4829 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4830 y + back_dy + left_dy)));
4831 should_turn_right = (can_turn_right &&
4833 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4834 y + back_dy + right_dy)));
4835 should_move_on = (can_move_on &&
4838 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4839 y + move_dy + left_dy) ||
4840 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4841 y + move_dy + right_dy)));
4843 if (should_turn_left || should_turn_right || should_move_on)
4845 if (should_turn_left && should_turn_right && should_move_on)
4846 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4847 rnd < 2 * rnd_value / 3 ? right_dir :
4849 else if (should_turn_left && should_turn_right)
4850 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4851 else if (should_turn_left && should_move_on)
4852 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4853 else if (should_turn_right && should_move_on)
4854 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4855 else if (should_turn_left)
4856 MovDir[x][y] = left_dir;
4857 else if (should_turn_right)
4858 MovDir[x][y] = right_dir;
4859 else if (should_move_on)
4860 MovDir[x][y] = old_move_dir;
4862 else if (can_move_on && rnd > rnd_value / 8)
4863 MovDir[x][y] = old_move_dir;
4864 else if (can_turn_left && can_turn_right)
4865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4866 else if (can_turn_left && rnd > rnd_value / 8)
4867 MovDir[x][y] = left_dir;
4868 else if (can_turn_right && rnd > rnd_value/8)
4869 MovDir[x][y] = right_dir;
4871 MovDir[x][y] = back_dir;
4873 xx = x + move_xy[MovDir[x][y]].x;
4874 yy = y + move_xy[MovDir[x][y]].y;
4877 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4878 if (!IN_LEV_FIELD(xx, yy) ||
4879 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4880 MovDir[x][y] = old_move_dir;
4882 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4883 MovDir[x][y] = old_move_dir;
4888 else if (element == EL_DRAGON)
4890 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4891 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4892 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4894 int rnd = RND(rnd_value);
4897 if (FrameCounter < 1 && x == 0 && y == 29)
4898 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4901 if (can_move_on && rnd > rnd_value / 8)
4902 MovDir[x][y] = old_move_dir;
4903 else if (can_turn_left && can_turn_right)
4904 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4905 else if (can_turn_left && rnd > rnd_value / 8)
4906 MovDir[x][y] = left_dir;
4907 else if (can_turn_right && rnd > rnd_value / 8)
4908 MovDir[x][y] = right_dir;
4910 MovDir[x][y] = back_dir;
4912 xx = x + move_xy[MovDir[x][y]].x;
4913 yy = y + move_xy[MovDir[x][y]].y;
4916 if (FrameCounter < 1 && x == 0 && y == 29)
4917 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4918 xx, yy, Feld[xx][yy],
4923 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4924 MovDir[x][y] = old_move_dir;
4926 if (!IS_FREE(xx, yy))
4927 MovDir[x][y] = old_move_dir;
4931 if (FrameCounter < 1 && x == 0 && y == 29)
4932 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4937 else if (element == EL_MOLE)
4939 boolean can_move_on =
4940 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4941 IS_AMOEBOID(Feld[move_x][move_y]) ||
4942 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4945 boolean can_turn_left =
4946 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4947 IS_AMOEBOID(Feld[left_x][left_y])));
4949 boolean can_turn_right =
4950 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4951 IS_AMOEBOID(Feld[right_x][right_y])));
4953 if (can_turn_left && can_turn_right)
4954 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4955 else if (can_turn_left)
4956 MovDir[x][y] = left_dir;
4958 MovDir[x][y] = right_dir;
4961 if (MovDir[x][y] != old_move_dir)
4964 else if (element == EL_BALLOON)
4966 MovDir[x][y] = game.balloon_dir;
4969 else if (element == EL_SPRING)
4972 if (MovDir[x][y] & MV_HORIZONTAL &&
4973 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4974 MovDir[x][y] = MV_NO_MOVING;
4976 if (MovDir[x][y] & MV_HORIZONTAL &&
4977 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4978 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4979 MovDir[x][y] = MV_NO_MOVING;
4984 else if (element == EL_ROBOT ||
4985 element == EL_SATELLITE ||
4986 element == EL_PENGUIN)
4988 int attr_x = -1, attr_y = -1;
4999 for (i = 0; i < MAX_PLAYERS; i++)
5001 struct PlayerInfo *player = &stored_player[i];
5002 int jx = player->jx, jy = player->jy;
5004 if (!player->active)
5008 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5017 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5018 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5019 game.engine_version < VERSION_IDENT(3,1,0,0)))
5021 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5028 if (element == EL_PENGUIN)
5031 static int xy[4][2] =
5039 for (i = 0; i < NUM_DIRECTIONS; i++)
5041 int ex = x + xy[i][0];
5042 int ey = y + xy[i][1];
5044 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5053 MovDir[x][y] = MV_NO_MOVING;
5055 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5056 else if (attr_x > x)
5057 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5059 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5060 else if (attr_y > y)
5061 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5063 if (element == EL_ROBOT)
5067 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5068 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5069 Moving2Blocked(x, y, &newx, &newy);
5071 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5072 MovDelay[x][y] = 8 + 8 * !RND(3);
5074 MovDelay[x][y] = 16;
5076 else if (element == EL_PENGUIN)
5082 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5084 boolean first_horiz = RND(2);
5085 int new_move_dir = MovDir[x][y];
5088 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5089 Moving2Blocked(x, y, &newx, &newy);
5091 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5095 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5096 Moving2Blocked(x, y, &newx, &newy);
5098 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5101 MovDir[x][y] = old_move_dir;
5105 else /* (element == EL_SATELLITE) */
5111 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5113 boolean first_horiz = RND(2);
5114 int new_move_dir = MovDir[x][y];
5117 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5118 Moving2Blocked(x, y, &newx, &newy);
5120 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5124 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5125 Moving2Blocked(x, y, &newx, &newy);
5127 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5130 MovDir[x][y] = old_move_dir;
5135 else if (move_pattern == MV_TURNING_LEFT ||
5136 move_pattern == MV_TURNING_RIGHT ||
5137 move_pattern == MV_TURNING_LEFT_RIGHT ||
5138 move_pattern == MV_TURNING_RIGHT_LEFT ||
5139 move_pattern == MV_TURNING_RANDOM ||
5140 move_pattern == MV_ALL_DIRECTIONS)
5142 boolean can_turn_left =
5143 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5144 boolean can_turn_right =
5145 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5147 #if USE_CAN_MOVE_NOT_MOVING
5148 if (element_info[element].move_stepsize == 0) /* not moving */
5152 if (move_pattern == MV_TURNING_LEFT)
5153 MovDir[x][y] = left_dir;
5154 else if (move_pattern == MV_TURNING_RIGHT)
5155 MovDir[x][y] = right_dir;
5156 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5157 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5158 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5159 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5160 else if (move_pattern == MV_TURNING_RANDOM)
5161 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5162 can_turn_right && !can_turn_left ? right_dir :
5163 RND(2) ? left_dir : right_dir);
5164 else if (can_turn_left && can_turn_right)
5165 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5166 else if (can_turn_left)
5167 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5168 else if (can_turn_right)
5169 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5171 MovDir[x][y] = back_dir;
5173 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5175 else if (move_pattern == MV_HORIZONTAL ||
5176 move_pattern == MV_VERTICAL)
5178 if (move_pattern & old_move_dir)
5179 MovDir[x][y] = back_dir;
5180 else if (move_pattern == MV_HORIZONTAL)
5181 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5182 else if (move_pattern == MV_VERTICAL)
5183 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5185 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5187 else if (move_pattern & MV_ANY_DIRECTION)
5189 MovDir[x][y] = move_pattern;
5190 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5192 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5194 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5195 MovDir[x][y] = left_dir;
5196 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5197 MovDir[x][y] = right_dir;
5199 if (MovDir[x][y] != old_move_dir)
5200 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5202 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5204 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5205 MovDir[x][y] = right_dir;
5206 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5207 MovDir[x][y] = left_dir;
5209 if (MovDir[x][y] != old_move_dir)
5210 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5212 else if (move_pattern == MV_TOWARDS_PLAYER ||
5213 move_pattern == MV_AWAY_FROM_PLAYER)
5215 int attr_x = -1, attr_y = -1;
5217 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5228 for (i = 0; i < MAX_PLAYERS; i++)
5230 struct PlayerInfo *player = &stored_player[i];
5231 int jx = player->jx, jy = player->jy;
5233 if (!player->active)
5237 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5245 MovDir[x][y] = MV_NO_MOVING;
5247 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5248 else if (attr_x > x)
5249 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5251 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5252 else if (attr_y > y)
5253 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5255 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5257 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5259 boolean first_horiz = RND(2);
5260 int new_move_dir = MovDir[x][y];
5262 #if USE_CAN_MOVE_NOT_MOVING
5263 if (element_info[element].move_stepsize == 0) /* not moving */
5265 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5266 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5273 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5274 Moving2Blocked(x, y, &newx, &newy);
5276 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5280 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5281 Moving2Blocked(x, y, &newx, &newy);
5283 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5286 MovDir[x][y] = old_move_dir;
5289 else if (move_pattern == MV_WHEN_PUSHED ||
5290 move_pattern == MV_WHEN_DROPPED)
5292 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5293 MovDir[x][y] = MV_NO_MOVING;
5297 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5299 static int test_xy[7][2] =
5309 static int test_dir[7] =
5319 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5320 int move_preference = -1000000; /* start with very low preference */
5321 int new_move_dir = MV_NO_MOVING;
5322 int start_test = RND(4);
5325 for (i = 0; i < NUM_DIRECTIONS; i++)
5327 int move_dir = test_dir[start_test + i];
5328 int move_dir_preference;
5330 xx = x + test_xy[start_test + i][0];
5331 yy = y + test_xy[start_test + i][1];
5333 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5334 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5336 new_move_dir = move_dir;
5341 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5344 move_dir_preference = -1 * RunnerVisit[xx][yy];
5345 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5346 move_dir_preference = PlayerVisit[xx][yy];
5348 if (move_dir_preference > move_preference)
5350 /* prefer field that has not been visited for the longest time */
5351 move_preference = move_dir_preference;
5352 new_move_dir = move_dir;
5354 else if (move_dir_preference == move_preference &&
5355 move_dir == old_move_dir)
5357 /* prefer last direction when all directions are preferred equally */
5358 move_preference = move_dir_preference;
5359 new_move_dir = move_dir;
5363 MovDir[x][y] = new_move_dir;
5364 if (old_move_dir != new_move_dir)
5367 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5375 static void TurnRound(int x, int y)
5377 int direction = MovDir[x][y];
5380 GfxDir[x][y] = MovDir[x][y];
5386 GfxDir[x][y] = MovDir[x][y];
5389 if (direction != MovDir[x][y])
5394 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5397 GfxAction[x][y] = ACTION_WAITING;
5401 static boolean JustBeingPushed(int x, int y)
5405 for (i = 0; i < MAX_PLAYERS; i++)
5407 struct PlayerInfo *player = &stored_player[i];
5409 if (player->active && player->is_pushing && player->MovPos)
5411 int next_jx = player->jx + (player->jx - player->last_jx);
5412 int next_jy = player->jy + (player->jy - player->last_jy);
5414 if (x == next_jx && y == next_jy)
5422 void StartMoving(int x, int y)
5425 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5427 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5428 int element = Feld[x][y];
5434 if (MovDelay[x][y] == 0)
5435 GfxAction[x][y] = ACTION_DEFAULT;
5437 /* !!! this should be handled more generic (not only for mole) !!! */
5438 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5439 GfxAction[x][y] = ACTION_DEFAULT;
5442 if (CAN_FALL(element) && y < lev_fieldy - 1)
5444 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5445 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5446 if (JustBeingPushed(x, y))
5449 if (element == EL_QUICKSAND_FULL)
5451 if (IS_FREE(x, y + 1))
5453 InitMovingField(x, y, MV_DOWN);
5454 started_moving = TRUE;
5456 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5457 Store[x][y] = EL_ROCK;
5459 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5461 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5464 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5466 if (!MovDelay[x][y])
5467 MovDelay[x][y] = TILEY + 1;
5476 Feld[x][y] = EL_QUICKSAND_EMPTY;
5477 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5478 Store[x][y + 1] = Store[x][y];
5481 PlayLevelSoundAction(x, y, ACTION_FILLING);
5483 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5487 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5488 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5490 InitMovingField(x, y, MV_DOWN);
5491 started_moving = TRUE;
5493 Feld[x][y] = EL_QUICKSAND_FILLING;
5494 Store[x][y] = element;
5496 PlayLevelSoundAction(x, y, ACTION_FILLING);
5498 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5501 else if (element == EL_MAGIC_WALL_FULL)
5503 if (IS_FREE(x, y + 1))
5505 InitMovingField(x, y, MV_DOWN);
5506 started_moving = TRUE;
5508 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5509 Store[x][y] = EL_CHANGED(Store[x][y]);
5511 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5513 if (!MovDelay[x][y])
5514 MovDelay[x][y] = TILEY/4 + 1;
5523 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5524 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5525 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5529 else if (element == EL_BD_MAGIC_WALL_FULL)
5531 if (IS_FREE(x, y + 1))
5533 InitMovingField(x, y, MV_DOWN);
5534 started_moving = TRUE;
5536 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5537 Store[x][y] = EL_CHANGED2(Store[x][y]);
5539 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5541 if (!MovDelay[x][y])
5542 MovDelay[x][y] = TILEY/4 + 1;
5551 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5552 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5553 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5557 else if (CAN_PASS_MAGIC_WALL(element) &&
5558 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5559 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5561 InitMovingField(x, y, MV_DOWN);
5562 started_moving = TRUE;
5565 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5566 EL_BD_MAGIC_WALL_FILLING);
5567 Store[x][y] = element;
5570 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5572 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5575 SplashAcid(x, y + 1);
5577 InitMovingField(x, y, MV_DOWN);
5578 started_moving = TRUE;
5580 Store[x][y] = EL_ACID;
5582 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5583 GfxAction[x][y + 1] = ACTION_ACTIVE;
5587 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5588 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5590 #if USE_IMPACT_BUGFIX
5591 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5592 CAN_FALL(element) && WasJustFalling[x][y] &&
5593 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5595 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5596 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5597 (Feld[x][y + 1] == EL_BLOCKED)))
5599 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5600 CAN_SMASH(element) && WasJustFalling[x][y] &&
5601 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5603 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5604 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5605 (Feld[x][y + 1] == EL_BLOCKED)))
5610 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5611 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5612 WasJustMoving[x][y] && !Pushed[x][y + 1])
5614 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5615 WasJustMoving[x][y])
5620 /* this is needed for a special case not covered by calling "Impact()"
5621 from "ContinueMoving()": if an element moves to a tile directly below
5622 another element which was just falling on that tile (which was empty
5623 in the previous frame), the falling element above would just stop
5624 instead of smashing the element below (in previous version, the above
5625 element was just checked for "moving" instead of "falling", resulting
5626 in incorrect smashes caused by horizontal movement of the above
5627 element; also, the case of the player being the element to smash was
5628 simply not covered here... :-/ ) */
5631 WasJustMoving[x][y] = 0;
5632 WasJustFalling[x][y] = 0;
5635 CheckCollision[x][y] = 0;
5638 if (IS_PLAYER(x, y + 1))
5639 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5644 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5646 if (MovDir[x][y] == MV_NO_MOVING)
5648 InitMovingField(x, y, MV_DOWN);
5649 started_moving = TRUE;
5652 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5654 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5655 MovDir[x][y] = MV_DOWN;
5657 InitMovingField(x, y, MV_DOWN);
5658 started_moving = TRUE;
5660 else if (element == EL_AMOEBA_DROP)
5662 Feld[x][y] = EL_AMOEBA_GROWING;
5663 Store[x][y] = EL_AMOEBA_WET;
5665 /* Store[x][y + 1] must be zero, because:
5666 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5669 #if OLD_GAME_BEHAVIOUR
5670 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5672 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5673 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5674 element != EL_DX_SUPABOMB)
5677 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5678 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5679 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5680 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5683 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5684 (IS_FREE(x - 1, y + 1) ||
5685 Feld[x - 1][y + 1] == EL_ACID));
5686 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5687 (IS_FREE(x + 1, y + 1) ||
5688 Feld[x + 1][y + 1] == EL_ACID));
5689 boolean can_fall_any = (can_fall_left || can_fall_right);
5690 boolean can_fall_both = (can_fall_left && can_fall_right);
5692 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5694 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5696 if (slippery_type == SLIPPERY_ONLY_LEFT)
5697 can_fall_right = FALSE;
5698 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5699 can_fall_left = FALSE;
5700 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5701 can_fall_right = FALSE;
5702 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5703 can_fall_left = FALSE;
5705 can_fall_any = (can_fall_left || can_fall_right);
5706 can_fall_both = (can_fall_left && can_fall_right);
5709 #if USE_NEW_SP_SLIPPERY
5710 /* !!! better use the same properties as for custom elements here !!! */
5711 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5712 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5714 can_fall_right = FALSE; /* slip down on left side */
5715 can_fall_both = FALSE;
5722 if (game.emulation == EMU_BOULDERDASH ||
5723 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5724 can_fall_right = FALSE; /* slip down on left side */
5726 can_fall_left = !(can_fall_right = RND(2));
5728 can_fall_both = FALSE;
5735 if (can_fall_both &&
5736 (game.emulation != EMU_BOULDERDASH &&
5737 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5738 can_fall_left = !(can_fall_right = RND(2));
5741 /* if not determined otherwise, prefer left side for slipping down */
5742 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5743 started_moving = TRUE;
5747 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5749 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5752 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5753 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5754 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5755 int belt_dir = game.belt_dir[belt_nr];
5757 if ((belt_dir == MV_LEFT && left_is_free) ||
5758 (belt_dir == MV_RIGHT && right_is_free))
5761 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5764 InitMovingField(x, y, belt_dir);
5765 started_moving = TRUE;
5768 Pushed[x][y] = TRUE;
5769 Pushed[nextx][y] = TRUE;
5772 GfxAction[x][y] = ACTION_DEFAULT;
5776 MovDir[x][y] = 0; /* if element was moving, stop it */
5781 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5783 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5785 if (CAN_MOVE(element) && !started_moving)
5788 int move_pattern = element_info[element].move_pattern;
5793 if (MovDir[x][y] == MV_NO_MOVING)
5795 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5796 x, y, element, element_info[element].token_name);
5797 printf("StartMoving(): This should never happen!\n");
5802 Moving2Blocked(x, y, &newx, &newy);
5805 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5808 if ((element == EL_SATELLITE ||
5809 element == EL_BALLOON ||
5810 element == EL_SPRING)
5811 && JustBeingPushed(x, y))
5818 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5819 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5821 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5822 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5823 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5827 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5828 element, element_info[element].token_name,
5829 WasJustMoving[x][y],
5830 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5831 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5832 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5833 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5837 WasJustMoving[x][y] = 0;
5840 CheckCollision[x][y] = 0;
5842 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5845 if (Feld[x][y] != element) /* element has changed */
5847 element = Feld[x][y];
5848 move_pattern = element_info[element].move_pattern;
5850 if (!CAN_MOVE(element))
5854 if (Feld[x][y] != element) /* element has changed */
5862 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5863 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5865 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5867 Moving2Blocked(x, y, &newx, &newy);
5868 if (Feld[newx][newy] == EL_BLOCKED)
5869 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5875 if (FrameCounter < 1 && x == 0 && y == 29)
5876 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5879 if (!MovDelay[x][y]) /* start new movement phase */
5881 /* all objects that can change their move direction after each step
5882 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5884 if (element != EL_YAMYAM &&
5885 element != EL_DARK_YAMYAM &&
5886 element != EL_PACMAN &&
5887 !(move_pattern & MV_ANY_DIRECTION) &&
5888 move_pattern != MV_TURNING_LEFT &&
5889 move_pattern != MV_TURNING_RIGHT &&
5890 move_pattern != MV_TURNING_LEFT_RIGHT &&
5891 move_pattern != MV_TURNING_RIGHT_LEFT &&
5892 move_pattern != MV_TURNING_RANDOM)
5897 if (FrameCounter < 1 && x == 0 && y == 29)
5898 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5901 if (MovDelay[x][y] && (element == EL_BUG ||
5902 element == EL_SPACESHIP ||
5903 element == EL_SP_SNIKSNAK ||
5904 element == EL_SP_ELECTRON ||
5905 element == EL_MOLE))
5906 DrawLevelField(x, y);
5910 if (MovDelay[x][y]) /* wait some time before next movement */
5915 if (element == EL_YAMYAM)
5918 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5919 DrawLevelElementAnimation(x, y, element);
5923 if (MovDelay[x][y]) /* element still has to wait some time */
5926 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5927 ResetGfxAnimation(x, y);
5931 if (GfxAction[x][y] != ACTION_WAITING)
5932 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5934 GfxAction[x][y] = ACTION_WAITING;
5938 if (element == EL_ROBOT ||
5940 element == EL_PACMAN ||
5942 element == EL_YAMYAM ||
5943 element == EL_DARK_YAMYAM)
5946 DrawLevelElementAnimation(x, y, element);
5948 DrawLevelElementAnimationIfNeeded(x, y, element);
5950 PlayLevelSoundAction(x, y, ACTION_WAITING);
5952 else if (element == EL_SP_ELECTRON)
5953 DrawLevelElementAnimationIfNeeded(x, y, element);
5954 else if (element == EL_DRAGON)
5957 int dir = MovDir[x][y];
5958 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5959 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5960 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5961 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5962 dir == MV_UP ? IMG_FLAMES_1_UP :
5963 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5964 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5967 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5970 GfxAction[x][y] = ACTION_ATTACKING;
5972 if (IS_PLAYER(x, y))
5973 DrawPlayerField(x, y);
5975 DrawLevelField(x, y);
5977 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5979 for (i = 1; i <= 3; i++)
5981 int xx = x + i * dx;
5982 int yy = y + i * dy;
5983 int sx = SCREENX(xx);
5984 int sy = SCREENY(yy);
5985 int flame_graphic = graphic + (i - 1);
5987 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5992 int flamed = MovingOrBlocked2Element(xx, yy);
5996 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5998 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5999 RemoveMovingField(xx, yy);
6001 RemoveField(xx, yy);
6003 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6006 RemoveMovingField(xx, yy);
6010 if (ChangeDelay[xx][yy])
6011 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6012 Feld[xx][yy] == EL_BLOCKED));
6016 ChangeDelay[xx][yy] = 0;
6018 Feld[xx][yy] = EL_FLAMES;
6019 if (IN_SCR_FIELD(sx, sy))
6021 DrawLevelFieldCrumbledSand(xx, yy);
6022 DrawGraphic(sx, sy, flame_graphic, frame);
6027 if (Feld[xx][yy] == EL_FLAMES)
6028 Feld[xx][yy] = EL_EMPTY;
6029 DrawLevelField(xx, yy);
6034 if (MovDelay[x][y]) /* element still has to wait some time */
6036 PlayLevelSoundAction(x, y, ACTION_WAITING);
6042 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6043 for all other elements GfxAction will be set by InitMovingField() */
6044 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6045 GfxAction[x][y] = ACTION_MOVING;
6049 /* now make next step */
6051 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6053 if (DONT_COLLIDE_WITH(element) &&
6054 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6055 !PLAYER_ENEMY_PROTECTED(newx, newy))
6058 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6062 /* player killed by element which is deadly when colliding with */
6064 KillHero(PLAYERINFO(newx, newy));
6071 else if (CAN_MOVE_INTO_ACID(element) &&
6072 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6073 (MovDir[x][y] == MV_DOWN ||
6074 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6076 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6077 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6081 else if ((element == EL_PENGUIN ||
6082 element == EL_ROBOT ||
6083 element == EL_SATELLITE ||
6084 element == EL_BALLOON ||
6085 IS_CUSTOM_ELEMENT(element)) &&
6086 IN_LEV_FIELD(newx, newy) &&
6087 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6090 SplashAcid(newx, newy);
6091 Store[x][y] = EL_ACID;
6093 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6095 if (Feld[newx][newy] == EL_EXIT_OPEN)
6099 DrawLevelField(x, y);
6101 Feld[x][y] = EL_EMPTY;
6102 DrawLevelField(x, y);
6105 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6106 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6107 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6109 local_player->friends_still_needed--;
6110 if (!local_player->friends_still_needed &&
6111 !local_player->GameOver && AllPlayersGone)
6112 local_player->LevelSolved = local_player->GameOver = TRUE;
6116 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6118 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6119 DrawLevelField(newx, newy);
6121 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6123 else if (!IS_FREE(newx, newy))
6125 GfxAction[x][y] = ACTION_WAITING;
6127 if (IS_PLAYER(x, y))
6128 DrawPlayerField(x, y);
6130 DrawLevelField(x, y);
6135 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6137 if (IS_FOOD_PIG(Feld[newx][newy]))
6139 if (IS_MOVING(newx, newy))
6140 RemoveMovingField(newx, newy);
6143 Feld[newx][newy] = EL_EMPTY;
6144 DrawLevelField(newx, newy);
6147 PlayLevelSound(x, y, SND_PIG_DIGGING);
6149 else if (!IS_FREE(newx, newy))
6151 if (IS_PLAYER(x, y))
6152 DrawPlayerField(x, y);
6154 DrawLevelField(x, y);
6163 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6166 else if (IS_CUSTOM_ELEMENT(element) &&
6167 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6171 !IS_FREE(newx, newy)
6176 int new_element = Feld[newx][newy];
6179 printf("::: '%s' digs '%s' [%d]\n",
6180 element_info[element].token_name,
6181 element_info[Feld[newx][newy]].token_name,
6182 StorePlayer[newx][newy]);
6185 if (!IS_FREE(newx, newy))
6187 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6188 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6191 /* no element can dig solid indestructible elements */
6192 if (IS_INDESTRUCTIBLE(new_element) &&
6193 !IS_DIGGABLE(new_element) &&
6194 !IS_COLLECTIBLE(new_element))
6197 if (AmoebaNr[newx][newy] &&
6198 (new_element == EL_AMOEBA_FULL ||
6199 new_element == EL_BD_AMOEBA ||
6200 new_element == EL_AMOEBA_GROWING))
6202 AmoebaCnt[AmoebaNr[newx][newy]]--;
6203 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6206 if (IS_MOVING(newx, newy))
6207 RemoveMovingField(newx, newy);
6210 RemoveField(newx, newy);
6211 DrawLevelField(newx, newy);
6214 /* if digged element was about to explode, prevent the explosion */
6215 ExplodeField[newx][newy] = EX_TYPE_NONE;
6217 PlayLevelSoundAction(x, y, action);
6222 Store[newx][newy] = EL_EMPTY;
6223 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6225 #if USE_CHANGE_TO_TRIGGERED
6226 int move_leave_element = element_info[element].move_leave_element;
6228 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6229 new_element : move_leave_element);
6231 Store[newx][newy] = element_info[element].move_leave_element;
6235 Store[newx][newy] = EL_EMPTY;
6236 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6237 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6238 Store[newx][newy] = element_info[element].move_leave_element;
6241 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6242 element_info[element].can_leave_element = TRUE;
6245 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6247 RunnerVisit[x][y] = FrameCounter;
6248 PlayerVisit[x][y] /= 8; /* expire player visit path */
6254 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6256 if (!IS_FREE(newx, newy))
6258 if (IS_PLAYER(x, y))
6259 DrawPlayerField(x, y);
6261 DrawLevelField(x, y);
6267 boolean wanna_flame = !RND(10);
6268 int dx = newx - x, dy = newy - y;
6269 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6270 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6271 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6272 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6273 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6274 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6277 IS_CLASSIC_ENEMY(element1) ||
6278 IS_CLASSIC_ENEMY(element2)) &&
6279 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6280 element1 != EL_FLAMES && element2 != EL_FLAMES)
6283 ResetGfxAnimation(x, y);
6284 GfxAction[x][y] = ACTION_ATTACKING;
6287 if (IS_PLAYER(x, y))
6288 DrawPlayerField(x, y);
6290 DrawLevelField(x, y);
6292 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6294 MovDelay[x][y] = 50;
6298 RemoveField(newx, newy);
6300 Feld[newx][newy] = EL_FLAMES;
6301 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6304 RemoveField(newx1, newy1);
6306 Feld[newx1][newy1] = EL_FLAMES;
6308 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6311 RemoveField(newx2, newy2);
6313 Feld[newx2][newy2] = EL_FLAMES;
6320 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6321 Feld[newx][newy] == EL_DIAMOND)
6323 if (IS_MOVING(newx, newy))
6324 RemoveMovingField(newx, newy);
6327 Feld[newx][newy] = EL_EMPTY;
6328 DrawLevelField(newx, newy);
6331 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6333 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6334 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6336 if (AmoebaNr[newx][newy])
6338 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6339 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6340 Feld[newx][newy] == EL_BD_AMOEBA)
6341 AmoebaCnt[AmoebaNr[newx][newy]]--;
6346 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6348 if (IS_MOVING(newx, newy))
6351 RemoveMovingField(newx, newy);
6355 Feld[newx][newy] = EL_EMPTY;
6356 DrawLevelField(newx, newy);
6359 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6361 else if ((element == EL_PACMAN || element == EL_MOLE)
6362 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6364 if (AmoebaNr[newx][newy])
6366 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6367 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6368 Feld[newx][newy] == EL_BD_AMOEBA)
6369 AmoebaCnt[AmoebaNr[newx][newy]]--;
6372 if (element == EL_MOLE)
6374 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6375 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6377 ResetGfxAnimation(x, y);
6378 GfxAction[x][y] = ACTION_DIGGING;
6379 DrawLevelField(x, y);
6381 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6383 return; /* wait for shrinking amoeba */
6385 else /* element == EL_PACMAN */
6387 Feld[newx][newy] = EL_EMPTY;
6388 DrawLevelField(newx, newy);
6389 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6392 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6393 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6394 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6396 /* wait for shrinking amoeba to completely disappear */
6399 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6401 /* object was running against a wall */
6406 if (move_pattern & MV_ANY_DIRECTION &&
6407 move_pattern == MovDir[x][y])
6409 int blocking_element =
6410 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6413 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6414 element_info[element].token_name,
6415 element_info[blocking_element].token_name,
6419 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6422 element = Feld[x][y]; /* element might have changed */
6427 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6428 DrawLevelElementAnimation(x, y, element);
6430 if (element == EL_BUG ||
6431 element == EL_SPACESHIP ||
6432 element == EL_SP_SNIKSNAK)
6433 DrawLevelField(x, y);
6434 else if (element == EL_MOLE)
6435 DrawLevelField(x, y);
6436 else if (element == EL_BD_BUTTERFLY ||
6437 element == EL_BD_FIREFLY)
6438 DrawLevelElementAnimationIfNeeded(x, y, element);
6439 else if (element == EL_SATELLITE)
6440 DrawLevelElementAnimationIfNeeded(x, y, element);
6441 else if (element == EL_SP_ELECTRON)
6442 DrawLevelElementAnimationIfNeeded(x, y, element);
6445 if (DONT_TOUCH(element))
6446 TestIfBadThingTouchesHero(x, y);
6449 PlayLevelSoundAction(x, y, ACTION_WAITING);
6455 InitMovingField(x, y, MovDir[x][y]);
6457 PlayLevelSoundAction(x, y, ACTION_MOVING);
6461 ContinueMoving(x, y);
6464 void ContinueMoving(int x, int y)
6466 int element = Feld[x][y];
6467 int stored = Store[x][y];
6468 struct ElementInfo *ei = &element_info[element];
6469 int direction = MovDir[x][y];
6470 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6471 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6472 int newx = x + dx, newy = y + dy;
6474 int nextx = newx + dx, nexty = newy + dy;
6477 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6478 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6480 boolean pushed_by_player = Pushed[x][y];
6483 MovPos[x][y] += getElementMoveStepsize(x, y);
6486 if (pushed_by_player && IS_PLAYER(x, y))
6488 /* special case: moving object pushed by player */
6489 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6492 if (pushed_by_player) /* special case: moving object pushed by player */
6493 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6496 if (ABS(MovPos[x][y]) < TILEX)
6498 DrawLevelField(x, y);
6500 return; /* element is still moving */
6503 /* element reached destination field */
6505 Feld[x][y] = EL_EMPTY;
6506 Feld[newx][newy] = element;
6507 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6510 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6512 element = Feld[newx][newy] = EL_ACID;
6515 else if (element == EL_MOLE)
6517 Feld[x][y] = EL_SAND;
6519 DrawLevelFieldCrumbledSandNeighbours(x, y);
6521 else if (element == EL_QUICKSAND_FILLING)
6523 element = Feld[newx][newy] = get_next_element(element);
6524 Store[newx][newy] = Store[x][y];
6526 else if (element == EL_QUICKSAND_EMPTYING)
6528 Feld[x][y] = get_next_element(element);
6529 element = Feld[newx][newy] = Store[x][y];
6531 else if (element == EL_MAGIC_WALL_FILLING)
6533 element = Feld[newx][newy] = get_next_element(element);
6534 if (!game.magic_wall_active)
6535 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6536 Store[newx][newy] = Store[x][y];
6538 else if (element == EL_MAGIC_WALL_EMPTYING)
6540 Feld[x][y] = get_next_element(element);
6541 if (!game.magic_wall_active)
6542 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6543 element = Feld[newx][newy] = Store[x][y];
6545 else if (element == EL_BD_MAGIC_WALL_FILLING)
6547 element = Feld[newx][newy] = get_next_element(element);
6548 if (!game.magic_wall_active)
6549 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6550 Store[newx][newy] = Store[x][y];
6552 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6554 Feld[x][y] = get_next_element(element);
6555 if (!game.magic_wall_active)
6556 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6557 element = Feld[newx][newy] = Store[x][y];
6559 else if (element == EL_AMOEBA_DROPPING)
6561 Feld[x][y] = get_next_element(element);
6562 element = Feld[newx][newy] = Store[x][y];
6564 else if (element == EL_SOKOBAN_OBJECT)
6567 Feld[x][y] = Back[x][y];
6569 if (Back[newx][newy])
6570 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6572 Back[x][y] = Back[newx][newy] = 0;
6575 else if (Store[x][y] == EL_ACID)
6577 element = Feld[newx][newy] = EL_ACID;
6581 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6582 ei->move_leave_element != EL_EMPTY &&
6583 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6584 Store[x][y] != EL_EMPTY))
6586 /* some elements can leave other elements behind after moving */
6588 Feld[x][y] = ei->move_leave_element;
6589 InitField(x, y, FALSE);
6591 if (GFX_CRUMBLED(Feld[x][y]))
6592 DrawLevelFieldCrumbledSandNeighbours(x, y);
6596 Store[x][y] = EL_EMPTY;
6600 MovDelay[newx][newy] = 0;
6602 if (CAN_CHANGE(element))
6604 /* copy element change control values to new field */
6605 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6606 ChangePage[newx][newy] = ChangePage[x][y];
6607 Changed[newx][newy] = Changed[x][y];
6608 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6611 ChangeDelay[x][y] = 0;
6612 ChangePage[x][y] = -1;
6613 Changed[x][y] = FALSE;
6614 ChangeEvent[x][y] = -1;
6616 /* copy animation control values to new field */
6617 GfxFrame[newx][newy] = GfxFrame[x][y];
6618 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6619 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6620 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6622 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6625 /* do this after checking for left-behind element */
6626 ResetGfxAnimation(x, y); /* reset animation values for old field */
6630 /* some elements can leave other elements behind after moving */
6632 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6633 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6634 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6636 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6637 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6641 int move_leave_element = ei->move_leave_element;
6643 #if USE_CHANGE_TO_TRIGGERED
6644 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6645 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6646 move_leave_element = stored;
6649 Feld[x][y] = move_leave_element;
6651 #if USE_PREVIOUS_MOVE_DIR
6652 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6653 MovDir[x][y] = direction;
6656 InitField(x, y, FALSE);
6658 if (GFX_CRUMBLED(Feld[x][y]))
6659 DrawLevelFieldCrumbledSandNeighbours(x, y);
6661 if (ELEM_IS_PLAYER(move_leave_element))
6662 RelocatePlayer(x, y, move_leave_element);
6667 /* some elements can leave other elements behind after moving */
6668 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6669 ei->move_leave_element != EL_EMPTY &&
6670 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6671 ei->can_leave_element_last))
6673 Feld[x][y] = ei->move_leave_element;
6674 InitField(x, y, FALSE);
6676 if (GFX_CRUMBLED(Feld[x][y]))
6677 DrawLevelFieldCrumbledSandNeighbours(x, y);
6680 ei->can_leave_element_last = ei->can_leave_element;
6681 ei->can_leave_element = FALSE;
6685 /* do this after checking for left-behind element */
6686 ResetGfxAnimation(x, y); /* reset animation values for old field */
6690 /* 2.1.1 (does not work correctly for spring) */
6691 if (!CAN_MOVE(element))
6692 MovDir[newx][newy] = 0;
6696 /* (does not work for falling objects that slide horizontally) */
6697 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6698 MovDir[newx][newy] = 0;
6701 if (!CAN_MOVE(element) ||
6702 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6703 MovDir[newx][newy] = 0;
6707 if (!CAN_MOVE(element) ||
6708 (CAN_FALL(element) && direction == MV_DOWN))
6709 GfxDir[x][y] = MovDir[newx][newy] = 0;
6711 if (!CAN_MOVE(element) ||
6712 (CAN_FALL(element) && direction == MV_DOWN &&
6713 (element == EL_SPRING ||
6714 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6715 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6716 GfxDir[x][y] = MovDir[newx][newy] = 0;
6722 DrawLevelField(x, y);
6723 DrawLevelField(newx, newy);
6725 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6727 /* prevent pushed element from moving on in pushed direction */
6728 if (pushed_by_player && CAN_MOVE(element) &&
6729 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6730 !(element_info[element].move_pattern & direction))
6731 TurnRound(newx, newy);
6734 /* prevent elements on conveyor belt from moving on in last direction */
6735 if (pushed_by_conveyor && CAN_FALL(element) &&
6736 direction & MV_HORIZONTAL)
6739 if (CAN_MOVE(element))
6740 InitMovDir(newx, newy);
6742 MovDir[newx][newy] = 0;
6744 MovDir[newx][newy] = 0;
6749 if (!pushed_by_player)
6751 int nextx = newx + dx, nexty = newy + dy;
6752 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6754 WasJustMoving[newx][newy] = 3;
6756 if (CAN_FALL(element) && direction == MV_DOWN)
6757 WasJustFalling[newx][newy] = 3;
6759 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6760 CheckCollision[newx][newy] = 2;
6763 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6765 TestIfBadThingTouchesHero(newx, newy);
6766 TestIfBadThingTouchesFriend(newx, newy);
6768 if (!IS_CUSTOM_ELEMENT(element))
6769 TestIfBadThingTouchesOtherBadThing(newx, newy);
6771 else if (element == EL_PENGUIN)
6772 TestIfFriendTouchesBadThing(newx, newy);
6774 #if USE_NEW_MOVE_STYLE
6776 if (CAN_FALL(element) && direction == MV_DOWN &&
6777 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6778 IS_PLAYER(x, newy + 1))
6779 printf("::: we would now kill the player [%d]\n", FrameCounter);
6782 /* give the player one last chance (one more frame) to move away */
6783 if (CAN_FALL(element) && direction == MV_DOWN &&
6784 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6785 ((newy < lev_fieldy - 1 && !IS_PLAYER(x, newy + 1)) ||
6786 game.engine_version < VERSION_IDENT(3,1,1,0)))
6789 if (CAN_FALL(element) && direction == MV_DOWN &&
6790 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6798 if (pushed_by_player && !game.use_change_when_pushing_bug)
6800 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6803 if (pushed_by_player)
6808 int dig_side = MV_DIR_OPPOSITE(direction);
6810 static int trigger_sides[4] =
6812 CH_SIDE_RIGHT, /* moving left */
6813 CH_SIDE_LEFT, /* moving right */
6814 CH_SIDE_BOTTOM, /* moving up */
6815 CH_SIDE_TOP, /* moving down */
6817 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6819 struct PlayerInfo *player = PLAYERINFO(x, y);
6821 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6822 player->index_bit, dig_side);
6823 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6824 player->index_bit, dig_side);
6829 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6833 if (ChangePage[newx][newy] != -1) /* delayed change */
6834 ChangeElement(newx, newy, ChangePage[newx][newy]);
6839 TestIfElementHitsCustomElement(newx, newy, direction);
6843 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6845 int hitting_element = Feld[newx][newy];
6847 /* !!! fix side (direction) orientation here and elsewhere !!! */
6848 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6852 if (IN_LEV_FIELD(nextx, nexty))
6854 int opposite_direction = MV_DIR_OPPOSITE(direction);
6855 int hitting_side = direction;
6856 int touched_side = opposite_direction;
6857 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6858 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6859 MovDir[nextx][nexty] != direction ||
6860 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6866 CheckElementChangeBySide(nextx, nexty, touched_element,
6867 CE_HIT_BY_SOMETHING, opposite_direction);
6869 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6870 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6872 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6874 struct ElementChangeInfo *change =
6875 &element_info[hitting_element].change_page[i];
6877 if (change->can_change &&
6878 change->has_event[CE_HITTING_X] &&
6879 change->trigger_side & touched_side &&
6880 change->trigger_element == touched_element)
6882 CheckElementChangeByPage(newx, newy, hitting_element,
6883 touched_element, CE_HITTING_X, i);
6889 if (IS_CUSTOM_ELEMENT(touched_element) &&
6890 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6892 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6894 struct ElementChangeInfo *change =
6895 &element_info[touched_element].change_page[i];
6897 if (change->can_change &&
6898 change->has_event[CE_HIT_BY_X] &&
6899 change->trigger_side & hitting_side &&
6900 change->trigger_element == hitting_element)
6902 CheckElementChangeByPage(nextx, nexty, touched_element,
6903 hitting_element, CE_HIT_BY_X,i);
6914 TestIfPlayerTouchesCustomElement(newx, newy);
6915 TestIfElementTouchesCustomElement(newx, newy);
6918 int AmoebeNachbarNr(int ax, int ay)
6921 int element = Feld[ax][ay];
6923 static int xy[4][2] =
6931 for (i = 0; i < NUM_DIRECTIONS; i++)
6933 int x = ax + xy[i][0];
6934 int y = ay + xy[i][1];
6936 if (!IN_LEV_FIELD(x, y))
6939 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6940 group_nr = AmoebaNr[x][y];
6946 void AmoebenVereinigen(int ax, int ay)
6948 int i, x, y, xx, yy;
6949 int new_group_nr = AmoebaNr[ax][ay];
6950 static int xy[4][2] =
6958 if (new_group_nr == 0)
6961 for (i = 0; i < NUM_DIRECTIONS; i++)
6966 if (!IN_LEV_FIELD(x, y))
6969 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6970 Feld[x][y] == EL_BD_AMOEBA ||
6971 Feld[x][y] == EL_AMOEBA_DEAD) &&
6972 AmoebaNr[x][y] != new_group_nr)
6974 int old_group_nr = AmoebaNr[x][y];
6976 if (old_group_nr == 0)
6979 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6980 AmoebaCnt[old_group_nr] = 0;
6981 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6982 AmoebaCnt2[old_group_nr] = 0;
6984 for (yy = 0; yy < lev_fieldy; yy++)
6986 for (xx = 0; xx < lev_fieldx; xx++)
6988 if (AmoebaNr[xx][yy] == old_group_nr)
6989 AmoebaNr[xx][yy] = new_group_nr;
6996 void AmoebeUmwandeln(int ax, int ay)
7000 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7002 int group_nr = AmoebaNr[ax][ay];
7007 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7008 printf("AmoebeUmwandeln(): This should never happen!\n");
7013 for (y = 0; y < lev_fieldy; y++)
7015 for (x = 0; x < lev_fieldx; x++)
7017 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7020 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7024 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7025 SND_AMOEBA_TURNING_TO_GEM :
7026 SND_AMOEBA_TURNING_TO_ROCK));
7031 static int xy[4][2] =
7039 for (i = 0; i < NUM_DIRECTIONS; i++)
7044 if (!IN_LEV_FIELD(x, y))
7047 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7049 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7050 SND_AMOEBA_TURNING_TO_GEM :
7051 SND_AMOEBA_TURNING_TO_ROCK));
7058 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7061 int group_nr = AmoebaNr[ax][ay];
7062 boolean done = FALSE;
7067 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7068 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7073 for (y = 0; y < lev_fieldy; y++)
7075 for (x = 0; x < lev_fieldx; x++)
7077 if (AmoebaNr[x][y] == group_nr &&
7078 (Feld[x][y] == EL_AMOEBA_DEAD ||
7079 Feld[x][y] == EL_BD_AMOEBA ||
7080 Feld[x][y] == EL_AMOEBA_GROWING))
7083 Feld[x][y] = new_element;
7084 InitField(x, y, FALSE);
7085 DrawLevelField(x, y);
7092 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7093 SND_BD_AMOEBA_TURNING_TO_ROCK :
7094 SND_BD_AMOEBA_TURNING_TO_GEM));
7097 void AmoebeWaechst(int x, int y)
7099 static unsigned long sound_delay = 0;
7100 static unsigned long sound_delay_value = 0;
7102 if (!MovDelay[x][y]) /* start new growing cycle */
7106 if (DelayReached(&sound_delay, sound_delay_value))
7109 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7111 if (Store[x][y] == EL_BD_AMOEBA)
7112 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7114 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7116 sound_delay_value = 30;
7120 if (MovDelay[x][y]) /* wait some time before growing bigger */
7123 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7125 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7126 6 - MovDelay[x][y]);
7128 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7131 if (!MovDelay[x][y])
7133 Feld[x][y] = Store[x][y];
7135 DrawLevelField(x, y);
7140 void AmoebaDisappearing(int x, int y)
7142 static unsigned long sound_delay = 0;
7143 static unsigned long sound_delay_value = 0;
7145 if (!MovDelay[x][y]) /* start new shrinking cycle */
7149 if (DelayReached(&sound_delay, sound_delay_value))
7150 sound_delay_value = 30;
7153 if (MovDelay[x][y]) /* wait some time before shrinking */
7156 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7158 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7159 6 - MovDelay[x][y]);
7161 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7164 if (!MovDelay[x][y])
7166 Feld[x][y] = EL_EMPTY;
7167 DrawLevelField(x, y);
7169 /* don't let mole enter this field in this cycle;
7170 (give priority to objects falling to this field from above) */
7176 void AmoebeAbleger(int ax, int ay)
7179 int element = Feld[ax][ay];
7180 int graphic = el2img(element);
7181 int newax = ax, neway = ay;
7182 static int xy[4][2] =
7190 if (!level.amoeba_speed)
7192 Feld[ax][ay] = EL_AMOEBA_DEAD;
7193 DrawLevelField(ax, ay);
7197 if (IS_ANIMATED(graphic))
7198 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7200 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7201 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7203 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7206 if (MovDelay[ax][ay])
7210 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7213 int x = ax + xy[start][0];
7214 int y = ay + xy[start][1];
7216 if (!IN_LEV_FIELD(x, y))
7220 if (IS_FREE(x, y) ||
7221 CAN_GROW_INTO(Feld[x][y]) ||
7222 Feld[x][y] == EL_QUICKSAND_EMPTY)
7228 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7229 if (IS_FREE(x, y) ||
7230 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7237 if (newax == ax && neway == ay)
7240 else /* normal or "filled" (BD style) amoeba */
7243 boolean waiting_for_player = FALSE;
7245 for (i = 0; i < NUM_DIRECTIONS; i++)
7247 int j = (start + i) % 4;
7248 int x = ax + xy[j][0];
7249 int y = ay + xy[j][1];
7251 if (!IN_LEV_FIELD(x, y))
7255 if (IS_FREE(x, y) ||
7256 CAN_GROW_INTO(Feld[x][y]) ||
7257 Feld[x][y] == EL_QUICKSAND_EMPTY)
7264 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7265 if (IS_FREE(x, y) ||
7266 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7273 else if (IS_PLAYER(x, y))
7274 waiting_for_player = TRUE;
7277 if (newax == ax && neway == ay) /* amoeba cannot grow */
7280 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7282 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7285 Feld[ax][ay] = EL_AMOEBA_DEAD;
7286 DrawLevelField(ax, ay);
7287 AmoebaCnt[AmoebaNr[ax][ay]]--;
7289 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7291 if (element == EL_AMOEBA_FULL)
7292 AmoebeUmwandeln(ax, ay);
7293 else if (element == EL_BD_AMOEBA)
7294 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7299 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7301 /* amoeba gets larger by growing in some direction */
7303 int new_group_nr = AmoebaNr[ax][ay];
7306 if (new_group_nr == 0)
7308 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7309 printf("AmoebeAbleger(): This should never happen!\n");
7314 AmoebaNr[newax][neway] = new_group_nr;
7315 AmoebaCnt[new_group_nr]++;
7316 AmoebaCnt2[new_group_nr]++;
7318 /* if amoeba touches other amoeba(s) after growing, unify them */
7319 AmoebenVereinigen(newax, neway);
7321 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7323 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7329 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7330 (neway == lev_fieldy - 1 && newax != ax))
7332 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7333 Store[newax][neway] = element;
7335 else if (neway == ay)
7337 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7339 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7341 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7346 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7347 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7348 Store[ax][ay] = EL_AMOEBA_DROP;
7349 ContinueMoving(ax, ay);
7353 DrawLevelField(newax, neway);
7356 void Life(int ax, int ay)
7359 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7361 int element = Feld[ax][ay];
7362 int graphic = el2img(element);
7363 boolean changed = FALSE;
7365 if (IS_ANIMATED(graphic))
7366 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7371 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7372 MovDelay[ax][ay] = life_time;
7374 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7377 if (MovDelay[ax][ay])
7381 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7383 int xx = ax+x1, yy = ay+y1;
7386 if (!IN_LEV_FIELD(xx, yy))
7389 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7391 int x = xx+x2, y = yy+y2;
7393 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7396 if (((Feld[x][y] == element ||
7397 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7399 (IS_FREE(x, y) && Stop[x][y]))
7403 if (xx == ax && yy == ay) /* field in the middle */
7405 if (nachbarn < life[0] || nachbarn > life[1])
7407 Feld[xx][yy] = EL_EMPTY;
7409 DrawLevelField(xx, yy);
7410 Stop[xx][yy] = TRUE;
7415 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7416 { /* free border field */
7417 if (nachbarn >= life[2] && nachbarn <= life[3])
7419 Feld[xx][yy] = element;
7420 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7422 DrawLevelField(xx, yy);
7423 Stop[xx][yy] = TRUE;
7428 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7429 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7430 { /* free border field */
7431 if (nachbarn >= life[2] && nachbarn <= life[3])
7433 Feld[xx][yy] = element;
7434 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7436 DrawLevelField(xx, yy);
7437 Stop[xx][yy] = TRUE;
7445 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7446 SND_GAME_OF_LIFE_GROWING);
7449 static void InitRobotWheel(int x, int y)
7451 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7454 static void RunRobotWheel(int x, int y)
7456 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7459 static void StopRobotWheel(int x, int y)
7461 if (ZX == x && ZY == y)
7465 static void InitTimegateWheel(int x, int y)
7468 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7470 /* another brainless, "type style" bug ... :-( */
7471 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7475 static void RunTimegateWheel(int x, int y)
7477 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7480 void CheckExit(int x, int y)
7482 if (local_player->gems_still_needed > 0 ||
7483 local_player->sokobanfields_still_needed > 0 ||
7484 local_player->lights_still_needed > 0)
7486 int element = Feld[x][y];
7487 int graphic = el2img(element);
7489 if (IS_ANIMATED(graphic))
7490 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7495 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7498 Feld[x][y] = EL_EXIT_OPENING;
7500 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7503 void CheckExitSP(int x, int y)
7505 if (local_player->gems_still_needed > 0)
7507 int element = Feld[x][y];
7508 int graphic = el2img(element);
7510 if (IS_ANIMATED(graphic))
7511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7516 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7519 Feld[x][y] = EL_SP_EXIT_OPENING;
7521 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7524 static void CloseAllOpenTimegates()
7528 for (y = 0; y < lev_fieldy; y++)
7530 for (x = 0; x < lev_fieldx; x++)
7532 int element = Feld[x][y];
7534 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7536 Feld[x][y] = EL_TIMEGATE_CLOSING;
7538 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7540 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7547 void EdelsteinFunkeln(int x, int y)
7549 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7552 if (Feld[x][y] == EL_BD_DIAMOND)
7555 if (MovDelay[x][y] == 0) /* next animation frame */
7556 MovDelay[x][y] = 11 * !SimpleRND(500);
7558 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7562 if (setup.direct_draw && MovDelay[x][y])
7563 SetDrawtoField(DRAW_BUFFERED);
7565 DrawLevelElementAnimation(x, y, Feld[x][y]);
7567 if (MovDelay[x][y] != 0)
7569 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7570 10 - MovDelay[x][y]);
7572 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7574 if (setup.direct_draw)
7578 dest_x = FX + SCREENX(x) * TILEX;
7579 dest_y = FY + SCREENY(y) * TILEY;
7581 BlitBitmap(drawto_field, window,
7582 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7583 SetDrawtoField(DRAW_DIRECT);
7589 void MauerWaechst(int x, int y)
7593 if (!MovDelay[x][y]) /* next animation frame */
7594 MovDelay[x][y] = 3 * delay;
7596 if (MovDelay[x][y]) /* wait some time before next frame */
7600 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7602 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7603 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7605 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7608 if (!MovDelay[x][y])
7610 if (MovDir[x][y] == MV_LEFT)
7612 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7613 DrawLevelField(x - 1, y);
7615 else if (MovDir[x][y] == MV_RIGHT)
7617 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7618 DrawLevelField(x + 1, y);
7620 else if (MovDir[x][y] == MV_UP)
7622 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7623 DrawLevelField(x, y - 1);
7627 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7628 DrawLevelField(x, y + 1);
7631 Feld[x][y] = Store[x][y];
7633 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7634 DrawLevelField(x, y);
7639 void MauerAbleger(int ax, int ay)
7641 int element = Feld[ax][ay];
7642 int graphic = el2img(element);
7643 boolean oben_frei = FALSE, unten_frei = FALSE;
7644 boolean links_frei = FALSE, rechts_frei = FALSE;
7645 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7646 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7647 boolean new_wall = FALSE;
7649 if (IS_ANIMATED(graphic))
7650 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7652 if (!MovDelay[ax][ay]) /* start building new wall */
7653 MovDelay[ax][ay] = 6;
7655 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7658 if (MovDelay[ax][ay])
7662 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7664 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7666 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7668 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7671 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7672 element == EL_EXPANDABLE_WALL_ANY)
7676 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7677 Store[ax][ay-1] = element;
7678 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7679 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7680 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7681 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7686 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7687 Store[ax][ay+1] = element;
7688 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7689 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7690 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7691 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7696 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7697 element == EL_EXPANDABLE_WALL_ANY ||
7698 element == EL_EXPANDABLE_WALL)
7702 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7703 Store[ax-1][ay] = element;
7704 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7705 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7706 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7707 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7713 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7714 Store[ax+1][ay] = element;
7715 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7716 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7717 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7718 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7723 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7724 DrawLevelField(ax, ay);
7726 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7728 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7729 unten_massiv = TRUE;
7730 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7731 links_massiv = TRUE;
7732 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7733 rechts_massiv = TRUE;
7735 if (((oben_massiv && unten_massiv) ||
7736 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7737 element == EL_EXPANDABLE_WALL) &&
7738 ((links_massiv && rechts_massiv) ||
7739 element == EL_EXPANDABLE_WALL_VERTICAL))
7740 Feld[ax][ay] = EL_WALL;
7744 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7746 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7750 void CheckForDragon(int x, int y)
7753 boolean dragon_found = FALSE;
7754 static int xy[4][2] =
7762 for (i = 0; i < NUM_DIRECTIONS; i++)
7764 for (j = 0; j < 4; j++)
7766 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7768 if (IN_LEV_FIELD(xx, yy) &&
7769 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7771 if (Feld[xx][yy] == EL_DRAGON)
7772 dragon_found = TRUE;
7781 for (i = 0; i < NUM_DIRECTIONS; i++)
7783 for (j = 0; j < 3; j++)
7785 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7787 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7789 Feld[xx][yy] = EL_EMPTY;
7790 DrawLevelField(xx, yy);
7799 static void InitBuggyBase(int x, int y)
7801 int element = Feld[x][y];
7802 int activating_delay = FRAMES_PER_SECOND / 4;
7805 (element == EL_SP_BUGGY_BASE ?
7806 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7807 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7809 element == EL_SP_BUGGY_BASE_ACTIVE ?
7810 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7813 static void WarnBuggyBase(int x, int y)
7816 static int xy[4][2] =
7824 for (i = 0; i < NUM_DIRECTIONS; i++)
7826 int xx = x + xy[i][0], yy = y + xy[i][1];
7828 if (IS_PLAYER(xx, yy))
7830 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7837 static void InitTrap(int x, int y)
7839 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7842 static void ActivateTrap(int x, int y)
7844 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7847 static void ChangeActiveTrap(int x, int y)
7849 int graphic = IMG_TRAP_ACTIVE;
7851 /* if new animation frame was drawn, correct crumbled sand border */
7852 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7853 DrawLevelFieldCrumbledSand(x, y);
7856 static void ChangeElementNowExt(int x, int y, int target_element)
7858 int previous_move_direction = MovDir[x][y];
7860 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7861 IS_WALKABLE(Feld[x][y]));
7863 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7864 IS_WALKABLE(Feld[x][y]) &&
7868 /* check if element under player changes from accessible to unaccessible
7869 (needed for special case of dropping element which then changes) */
7870 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7871 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7874 printf("::: BOOOM! [%d, '%s']\n", target_element,
7875 element_info[target_element].token_name);
7887 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7888 RemoveMovingField(x, y);
7892 Feld[x][y] = target_element;
7895 Feld[x][y] = target_element;
7898 ResetGfxAnimation(x, y);
7899 ResetRandomAnimationValue(x, y);
7901 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7902 MovDir[x][y] = previous_move_direction;
7905 InitField_WithBug1(x, y, FALSE);
7907 InitField(x, y, FALSE);
7908 if (CAN_MOVE(Feld[x][y]))
7912 DrawLevelField(x, y);
7914 if (GFX_CRUMBLED(Feld[x][y]))
7915 DrawLevelFieldCrumbledSandNeighbours(x, y);
7919 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7923 TestIfBadThingTouchesHero(x, y);
7924 TestIfPlayerTouchesCustomElement(x, y);
7925 TestIfElementTouchesCustomElement(x, y);
7928 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7929 if (ELEM_IS_PLAYER(target_element))
7930 RelocatePlayer(x, y, target_element);
7933 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7935 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7939 TestIfBadThingTouchesHero(x, y);
7940 TestIfPlayerTouchesCustomElement(x, y);
7941 TestIfElementTouchesCustomElement(x, y);
7945 static boolean ChangeElementNow(int x, int y, int element, int page)
7947 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7949 int old_element = Feld[x][y];
7951 /* always use default change event to prevent running into a loop */
7952 if (ChangeEvent[x][y] == -1)
7953 ChangeEvent[x][y] = CE_DELAY;
7955 if (ChangeEvent[x][y] == CE_DELAY)
7957 /* reset actual trigger element and player */
7958 change->actual_trigger_element = EL_EMPTY;
7959 change->actual_trigger_player = EL_PLAYER_1;
7963 /* do not change any elements that have already changed in this frame */
7967 /* do not change already changed elements with same change event */
7968 if (Changed[x][y] & ChangeEvent[x][y])
7973 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7975 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7979 /* !!! indirect change before direct change !!! */
7980 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
7983 if (change->explode)
7990 if (change->use_target_content)
7992 boolean complete_replace = TRUE;
7993 boolean can_replace[3][3];
7996 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7999 boolean is_walkable;
8000 boolean is_diggable;
8001 boolean is_collectible;
8002 boolean is_removable;
8003 boolean is_destructible;
8004 int ex = x + xx - 1;
8005 int ey = y + yy - 1;
8006 int content_element = change->target_content[xx][yy];
8009 can_replace[xx][yy] = TRUE;
8011 if (ex == x && ey == y) /* do not check changing element itself */
8014 if (content_element == EL_EMPTY_SPACE)
8016 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8021 if (!IN_LEV_FIELD(ex, ey))
8023 can_replace[xx][yy] = FALSE;
8024 complete_replace = FALSE;
8030 if (Changed[ex][ey]) /* do not change already changed elements */
8032 can_replace[xx][yy] = FALSE;
8033 complete_replace = FALSE;
8041 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8042 e = MovingOrBlocked2Element(ex, ey);
8047 is_empty = (IS_FREE(ex, ey) ||
8048 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8049 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8050 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8054 is_empty = (IS_FREE(ex, ey) ||
8055 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8057 is_empty = (IS_FREE(ex, ey) ||
8058 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8063 is_walkable = (is_empty || IS_WALKABLE(e));
8064 is_diggable = (is_empty || IS_DIGGABLE(e));
8065 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8066 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8067 is_removable = (is_diggable || is_collectible);
8069 can_replace[xx][yy] =
8070 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8071 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8072 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8073 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8074 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8075 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8076 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8078 if (!can_replace[xx][yy])
8079 complete_replace = FALSE;
8081 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8082 IS_WALKABLE(content_element)));
8084 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8086 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8089 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8090 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8091 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8093 can_replace[xx][yy] = FALSE;
8094 complete_replace = FALSE;
8099 if (!change->only_if_complete || complete_replace)
8101 boolean something_has_changed = FALSE;
8103 if (change->only_if_complete && change->use_random_replace &&
8104 RND(100) < change->random_percentage)
8107 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8109 int ex = x + xx - 1;
8110 int ey = y + yy - 1;
8111 int content_element;
8113 if (can_replace[xx][yy] && (!change->use_random_replace ||
8114 RND(100) < change->random_percentage))
8116 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8117 RemoveMovingField(ex, ey);
8119 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8121 content_element = change->target_content[xx][yy];
8122 target_element = GET_TARGET_ELEMENT(content_element, change);
8124 ChangeElementNowExt(ex, ey, target_element);
8126 something_has_changed = TRUE;
8128 /* for symmetry reasons, freeze newly created border elements */
8129 if (ex != x || ey != y)
8130 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8134 if (something_has_changed)
8135 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8140 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8142 ChangeElementNowExt(x, y, target_element);
8144 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8148 /* this uses direct change before indirect change */
8149 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8155 static void ChangeElement(int x, int y, int page)
8157 int element = MovingOrBlocked2Element(x, y);
8158 struct ElementInfo *ei = &element_info[element];
8159 struct ElementChangeInfo *change = &ei->change_page[page];
8162 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8165 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8166 x, y, element, element_info[element].token_name);
8167 printf("ChangeElement(): This should never happen!\n");
8172 /* this can happen with classic bombs on walkable, changing elements */
8173 if (!CAN_CHANGE(element))
8176 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8177 ChangeDelay[x][y] = 0;
8183 if (ChangeDelay[x][y] == 0) /* initialize element change */
8185 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8186 RND(change->delay_random * change->delay_frames)) + 1;
8188 ResetGfxAnimation(x, y);
8189 ResetRandomAnimationValue(x, y);
8191 if (change->pre_change_function)
8192 change->pre_change_function(x, y);
8195 ChangeDelay[x][y]--;
8197 if (ChangeDelay[x][y] != 0) /* continue element change */
8199 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8201 if (IS_ANIMATED(graphic))
8202 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8204 if (change->change_function)
8205 change->change_function(x, y);
8207 else /* finish element change */
8209 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8211 page = ChangePage[x][y];
8212 ChangePage[x][y] = -1;
8214 change = &ei->change_page[page];
8218 if (IS_MOVING(x, y) && !change->explode)
8220 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8223 ChangeDelay[x][y] = 1; /* try change after next move step */
8224 ChangePage[x][y] = page; /* remember page to use for change */
8229 if (ChangeElementNow(x, y, element, page))
8231 if (change->post_change_function)
8232 change->post_change_function(x, y);
8237 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8238 int trigger_element,
8245 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8247 if (!(trigger_events[trigger_element][trigger_event]))
8250 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8252 int element = EL_CUSTOM_START + i;
8254 boolean change_element = FALSE;
8257 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8260 for (j = 0; j < element_info[element].num_change_pages; j++)
8262 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8264 if (change->can_change &&
8265 change->has_event[trigger_event] &&
8266 change->trigger_side & trigger_side &&
8267 change->trigger_player & trigger_player &&
8268 change->trigger_page & trigger_page_bits &&
8269 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8272 if (!(change->has_event[trigger_event]))
8273 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8274 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8277 change_element = TRUE;
8280 change->actual_trigger_element = trigger_element;
8281 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8287 if (!change_element)
8290 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8293 if (x == lx && y == ly) /* do not change trigger element itself */
8297 if (Feld[x][y] == element)
8299 ChangeDelay[x][y] = 1;
8300 ChangeEvent[x][y] = trigger_event;
8301 ChangeElement(x, y, page);
8309 static boolean CheckElementChangeExt(int x, int y,
8311 int trigger_element,
8317 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8320 if (Feld[x][y] == EL_BLOCKED)
8322 Blocked2Moving(x, y, &x, &y);
8323 element = Feld[x][y];
8327 if (Feld[x][y] != element) /* check if element has already changed */
8330 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8331 Feld[x][y], element_info[Feld[x][y]].token_name,
8332 element, element_info[element].token_name,
8341 if (trigger_page < 0)
8343 boolean change_element = FALSE;
8346 for (i = 0; i < element_info[element].num_change_pages; i++)
8348 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8350 if (change->can_change &&
8351 change->has_event[trigger_event] &&
8352 change->trigger_side & trigger_side &&
8353 change->trigger_player & trigger_player)
8355 change_element = TRUE;
8358 change->actual_trigger_element = trigger_element;
8359 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8365 if (!change_element)
8370 struct ElementInfo *ei = &element_info[element];
8371 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8373 change->actual_trigger_element = trigger_element;
8374 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8379 /* !!! this check misses pages with same event, but different side !!! */
8381 if (trigger_page < 0)
8382 trigger_page = element_info[element].event_page_nr[trigger_event];
8384 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8388 ChangeDelay[x][y] = 1;
8389 ChangeEvent[x][y] = trigger_event;
8390 ChangeElement(x, y, trigger_page);
8395 static void PlayPlayerSound(struct PlayerInfo *player)
8397 int jx = player->jx, jy = player->jy;
8398 int element = player->element_nr;
8399 int last_action = player->last_action_waiting;
8400 int action = player->action_waiting;
8402 if (player->is_waiting)
8404 if (action != last_action)
8405 PlayLevelSoundElementAction(jx, jy, element, action);
8407 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8411 if (action != last_action)
8412 StopSound(element_info[element].sound[last_action]);
8414 if (last_action == ACTION_SLEEPING)
8415 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8419 static void PlayAllPlayersSound()
8423 for (i = 0; i < MAX_PLAYERS; i++)
8424 if (stored_player[i].active)
8425 PlayPlayerSound(&stored_player[i]);
8428 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8430 boolean last_waiting = player->is_waiting;
8431 int move_dir = player->MovDir;
8433 player->last_action_waiting = player->action_waiting;
8437 if (!last_waiting) /* not waiting -> waiting */
8439 player->is_waiting = TRUE;
8441 player->frame_counter_bored =
8443 game.player_boring_delay_fixed +
8444 SimpleRND(game.player_boring_delay_random);
8445 player->frame_counter_sleeping =
8447 game.player_sleeping_delay_fixed +
8448 SimpleRND(game.player_sleeping_delay_random);
8450 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8453 if (game.player_sleeping_delay_fixed +
8454 game.player_sleeping_delay_random > 0 &&
8455 player->anim_delay_counter == 0 &&
8456 player->post_delay_counter == 0 &&
8457 FrameCounter >= player->frame_counter_sleeping)
8458 player->is_sleeping = TRUE;
8459 else if (game.player_boring_delay_fixed +
8460 game.player_boring_delay_random > 0 &&
8461 FrameCounter >= player->frame_counter_bored)
8462 player->is_bored = TRUE;
8464 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8465 player->is_bored ? ACTION_BORING :
8468 if (player->is_sleeping)
8470 if (player->num_special_action_sleeping > 0)
8472 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8474 int last_special_action = player->special_action_sleeping;
8475 int num_special_action = player->num_special_action_sleeping;
8476 int special_action =
8477 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8478 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8479 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8480 last_special_action + 1 : ACTION_SLEEPING);
8481 int special_graphic =
8482 el_act_dir2img(player->element_nr, special_action, move_dir);
8484 player->anim_delay_counter =
8485 graphic_info[special_graphic].anim_delay_fixed +
8486 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8487 player->post_delay_counter =
8488 graphic_info[special_graphic].post_delay_fixed +
8489 SimpleRND(graphic_info[special_graphic].post_delay_random);
8491 player->special_action_sleeping = special_action;
8494 if (player->anim_delay_counter > 0)
8496 player->action_waiting = player->special_action_sleeping;
8497 player->anim_delay_counter--;
8499 else if (player->post_delay_counter > 0)
8501 player->post_delay_counter--;
8505 else if (player->is_bored)
8507 if (player->num_special_action_bored > 0)
8509 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8511 int special_action =
8512 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8513 int special_graphic =
8514 el_act_dir2img(player->element_nr, special_action, move_dir);
8516 player->anim_delay_counter =
8517 graphic_info[special_graphic].anim_delay_fixed +
8518 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8519 player->post_delay_counter =
8520 graphic_info[special_graphic].post_delay_fixed +
8521 SimpleRND(graphic_info[special_graphic].post_delay_random);
8523 player->special_action_bored = special_action;
8526 if (player->anim_delay_counter > 0)
8528 player->action_waiting = player->special_action_bored;
8529 player->anim_delay_counter--;
8531 else if (player->post_delay_counter > 0)
8533 player->post_delay_counter--;
8538 else if (last_waiting) /* waiting -> not waiting */
8540 player->is_waiting = FALSE;
8541 player->is_bored = FALSE;
8542 player->is_sleeping = FALSE;
8544 player->frame_counter_bored = -1;
8545 player->frame_counter_sleeping = -1;
8547 player->anim_delay_counter = 0;
8548 player->post_delay_counter = 0;
8550 player->action_waiting = ACTION_DEFAULT;
8552 player->special_action_bored = ACTION_DEFAULT;
8553 player->special_action_sleeping = ACTION_DEFAULT;
8558 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8561 static byte stored_player_action[MAX_PLAYERS];
8562 static int num_stored_actions = 0;
8564 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8565 int left = player_action & JOY_LEFT;
8566 int right = player_action & JOY_RIGHT;
8567 int up = player_action & JOY_UP;
8568 int down = player_action & JOY_DOWN;
8569 int button1 = player_action & JOY_BUTTON_1;
8570 int button2 = player_action & JOY_BUTTON_2;
8571 int dx = (left ? -1 : right ? 1 : 0);
8572 int dy = (up ? -1 : down ? 1 : 0);
8575 stored_player_action[player->index_nr] = 0;
8576 num_stored_actions++;
8580 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8583 if (!player->active || tape.pausing)
8587 printf("::: [%d %d %d %d] [%d %d]\n",
8588 left, right, up, down, button1, button2);
8594 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8599 if (player->MovPos == 0)
8600 CheckGravityMovement(player);
8603 snapped = SnapField(player, dx, dy);
8607 dropped = DropElement(player);
8609 moved = MovePlayer(player, dx, dy);
8612 if (tape.single_step && tape.recording && !tape.pausing)
8614 if (button1 || (dropped && !moved))
8616 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8617 SnapField(player, 0, 0); /* stop snapping */
8621 SetPlayerWaiting(player, FALSE);
8624 return player_action;
8626 stored_player_action[player->index_nr] = player_action;
8632 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8635 /* no actions for this player (no input at player's configured device) */
8637 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8638 SnapField(player, 0, 0);
8639 CheckGravityMovementWhenNotMoving(player);
8641 if (player->MovPos == 0)
8642 SetPlayerWaiting(player, TRUE);
8644 if (player->MovPos == 0) /* needed for tape.playing */
8645 player->is_moving = FALSE;
8647 player->is_dropping = FALSE;
8653 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8655 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8657 TapeRecordAction(stored_player_action);
8658 num_stored_actions = 0;
8665 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8667 static byte stored_player_action[MAX_PLAYERS];
8668 static int num_stored_actions = 0;
8669 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8670 int left = player_action & JOY_LEFT;
8671 int right = player_action & JOY_RIGHT;
8672 int up = player_action & JOY_UP;
8673 int down = player_action & JOY_DOWN;
8674 int button1 = player_action & JOY_BUTTON_1;
8675 int button2 = player_action & JOY_BUTTON_2;
8676 int dx = (left ? -1 : right ? 1 : 0);
8677 int dy = (up ? -1 : down ? 1 : 0);
8679 stored_player_action[player->index_nr] = 0;
8680 num_stored_actions++;
8682 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8684 if (!player->active || tape.pausing)
8689 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8692 snapped = SnapField(player, dx, dy);
8696 dropped = DropElement(player);
8698 moved = MovePlayer(player, dx, dy);
8701 if (tape.single_step && tape.recording && !tape.pausing)
8703 if (button1 || (dropped && !moved))
8705 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8706 SnapField(player, 0, 0); /* stop snapping */
8710 stored_player_action[player->index_nr] = player_action;
8714 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8716 /* no actions for this player (no input at player's configured device) */
8718 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8719 SnapField(player, 0, 0);
8720 CheckGravityMovementWhenNotMoving(player);
8722 if (player->MovPos == 0)
8723 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8725 if (player->MovPos == 0) /* needed for tape.playing */
8726 player->is_moving = FALSE;
8729 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8731 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8733 TapeRecordAction(stored_player_action);
8734 num_stored_actions = 0;
8739 void AdvanceFrameAndPlayerCounters(int player_nr)
8743 /* advance frame counters (global frame counter and time frame counter) */
8747 /* advance player counters (counters for move delay, move animation etc.) */
8748 for (i = 0; i < MAX_PLAYERS; i++)
8750 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8752 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8754 if (!advance_player_counters) /* not all players may be affected */
8757 stored_player[i].Frame += move_frames;
8759 if (stored_player[i].MovPos != 0)
8760 stored_player[i].StepFrame += move_frames;
8762 #if USE_NEW_MOVE_DELAY
8763 if (stored_player[i].move_delay > 0)
8764 stored_player[i].move_delay--;
8767 #if USE_NEW_PUSH_DELAY
8768 /* due to bugs in previous versions, counter must count up, not down */
8769 if (stored_player[i].push_delay != -1)
8770 stored_player[i].push_delay++;
8773 if (stored_player[i].drop_delay > 0)
8774 stored_player[i].drop_delay--;
8780 static unsigned long game_frame_delay = 0;
8781 unsigned long game_frame_delay_value;
8782 int magic_wall_x = 0, magic_wall_y = 0;
8783 int i, x, y, element, graphic;
8784 byte *recorded_player_action;
8785 byte summarized_player_action = 0;
8787 byte tape_action[MAX_PLAYERS];
8790 if (game_status != GAME_MODE_PLAYING)
8793 game_frame_delay_value =
8794 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8796 if (tape.playing && tape.warp_forward && !tape.pausing)
8797 game_frame_delay_value = 0;
8799 /* ---------- main game synchronization point ---------- */
8801 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8803 if (network_playing && !network_player_action_received)
8807 printf("DEBUG: try to get network player actions in time\n");
8811 #if defined(NETWORK_AVALIABLE)
8812 /* last chance to get network player actions without main loop delay */
8816 if (game_status != GAME_MODE_PLAYING)
8819 if (!network_player_action_received)
8823 printf("DEBUG: failed to get network player actions in time\n");
8834 printf("::: getting new tape action [%d]\n", FrameCounter);
8837 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8840 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8841 if (recorded_player_action == NULL && tape.pausing)
8846 printf("::: %d\n", stored_player[0].action);
8850 if (recorded_player_action != NULL)
8851 for (i = 0; i < MAX_PLAYERS; i++)
8852 stored_player[i].action = recorded_player_action[i];
8855 for (i = 0; i < MAX_PLAYERS; i++)
8857 summarized_player_action |= stored_player[i].action;
8859 if (!network_playing)
8860 stored_player[i].effective_action = stored_player[i].action;
8863 #if defined(NETWORK_AVALIABLE)
8864 if (network_playing)
8865 SendToServer_MovePlayer(summarized_player_action);
8868 if (!options.network && !setup.team_mode)
8869 local_player->effective_action = summarized_player_action;
8872 if (recorded_player_action != NULL)
8873 for (i = 0; i < MAX_PLAYERS; i++)
8874 stored_player[i].effective_action = recorded_player_action[i];
8878 for (i = 0; i < MAX_PLAYERS; i++)
8880 tape_action[i] = stored_player[i].effective_action;
8882 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8883 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8886 /* only save actions from input devices, but not programmed actions */
8888 TapeRecordAction(tape_action);
8891 for (i = 0; i < MAX_PLAYERS; i++)
8893 int actual_player_action = stored_player[i].effective_action;
8896 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8897 - rnd_equinox_tetrachloride 048
8898 - rnd_equinox_tetrachloride_ii 096
8899 - rnd_emanuel_schmieg 002
8900 - doctor_sloan_ww 001, 020
8902 if (stored_player[i].MovPos == 0)
8903 CheckGravityMovement(&stored_player[i]);
8907 /* overwrite programmed action with tape action */
8908 if (stored_player[i].programmed_action)
8909 actual_player_action = stored_player[i].programmed_action;
8913 if (stored_player[i].programmed_action)
8914 printf("::: %d\n", stored_player[i].programmed_action);
8917 if (recorded_player_action)
8920 if (stored_player[i].programmed_action &&
8921 stored_player[i].programmed_action != recorded_player_action[i])
8922 printf("::: %d: %d <-> %d\n", i,
8923 stored_player[i].programmed_action, recorded_player_action[i]);
8927 actual_player_action = recorded_player_action[i];
8932 /* overwrite tape action with programmed action */
8933 if (stored_player[i].programmed_action)
8934 actual_player_action = stored_player[i].programmed_action;
8939 printf("::: action: %d: %x [%d]\n",
8940 stored_player[i].MovPos, actual_player_action, FrameCounter);
8944 PlayerActions(&stored_player[i], actual_player_action);
8946 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8948 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8949 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8952 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8957 TapeRecordAction(tape_action);
8960 network_player_action_received = FALSE;
8962 ScrollScreen(NULL, SCROLL_GO_ON);
8968 for (i = 0; i < MAX_PLAYERS; i++)
8969 stored_player[i].Frame++;
8973 /* for backwards compatibility, the following code emulates a fixed bug that
8974 occured when pushing elements (causing elements that just made their last
8975 pushing step to already (if possible) make their first falling step in the
8976 same game frame, which is bad); this code is also needed to use the famous
8977 "spring push bug" which is used in older levels and might be wanted to be
8978 used also in newer levels, but in this case the buggy pushing code is only
8979 affecting the "spring" element and no other elements */
8982 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8984 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8987 for (i = 0; i < MAX_PLAYERS; i++)
8989 struct PlayerInfo *player = &stored_player[i];
8994 if (player->active && player->is_pushing && player->is_moving &&
8996 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8997 Feld[x][y] == EL_SPRING))
8999 if (player->active && player->is_pushing && player->is_moving &&
9003 ContinueMoving(x, y);
9005 /* continue moving after pushing (this is actually a bug) */
9006 if (!IS_MOVING(x, y))
9015 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9017 Changed[x][y] = FALSE;
9018 ChangeEvent[x][y] = -1;
9020 #if USE_NEW_BLOCK_STYLE
9021 /* this must be handled before main playfield loop */
9022 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9025 if (MovDelay[x][y] <= 0)
9031 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9033 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9034 printf("GameActions(): This should never happen!\n");
9036 ChangePage[x][y] = -1;
9041 if (WasJustMoving[x][y] > 0)
9042 WasJustMoving[x][y]--;
9043 if (WasJustFalling[x][y] > 0)
9044 WasJustFalling[x][y]--;
9045 if (CheckCollision[x][y] > 0)
9046 CheckCollision[x][y]--;
9051 /* reset finished pushing action (not done in ContinueMoving() to allow
9052 continous pushing animation for elements with zero push delay) */
9053 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9055 ResetGfxAnimation(x, y);
9056 DrawLevelField(x, y);
9061 if (IS_BLOCKED(x, y))
9065 Blocked2Moving(x, y, &oldx, &oldy);
9066 if (!IS_MOVING(oldx, oldy))
9068 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9069 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9070 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9071 printf("GameActions(): This should never happen!\n");
9077 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9079 element = Feld[x][y];
9081 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9083 graphic = el2img(element);
9089 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9091 element = graphic = 0;
9095 if (graphic_info[graphic].anim_global_sync)
9096 GfxFrame[x][y] = FrameCounter;
9098 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9099 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9100 ResetRandomAnimationValue(x, y);
9102 SetRandomAnimationValue(x, y);
9105 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9108 if (IS_INACTIVE(element))
9110 if (IS_ANIMATED(graphic))
9111 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9117 /* this may take place after moving, so 'element' may have changed */
9119 if (IS_CHANGING(x, y))
9121 if (IS_CHANGING(x, y) &&
9122 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9126 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9127 element_info[element].event_page_nr[CE_DELAY]);
9129 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9132 element = Feld[x][y];
9133 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9137 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9142 element = Feld[x][y];
9143 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9145 if (element == EL_MOLE)
9146 printf("::: %d, %d, %d [%d]\n",
9147 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9151 if (element == EL_YAMYAM)
9152 printf("::: %d, %d, %d\n",
9153 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9157 if (IS_ANIMATED(graphic) &&
9161 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9164 if (element == EL_BUG)
9165 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9169 if (element == EL_MOLE)
9170 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9174 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9175 EdelsteinFunkeln(x, y);
9177 else if ((element == EL_ACID ||
9178 element == EL_EXIT_OPEN ||
9179 element == EL_SP_EXIT_OPEN ||
9180 element == EL_SP_TERMINAL ||
9181 element == EL_SP_TERMINAL_ACTIVE ||
9182 element == EL_EXTRA_TIME ||
9183 element == EL_SHIELD_NORMAL ||
9184 element == EL_SHIELD_DEADLY) &&
9185 IS_ANIMATED(graphic))
9186 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9187 else if (IS_MOVING(x, y))
9188 ContinueMoving(x, y);
9189 else if (IS_ACTIVE_BOMB(element))
9190 CheckDynamite(x, y);
9192 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9193 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9195 else if (element == EL_AMOEBA_GROWING)
9196 AmoebeWaechst(x, y);
9197 else if (element == EL_AMOEBA_SHRINKING)
9198 AmoebaDisappearing(x, y);
9200 #if !USE_NEW_AMOEBA_CODE
9201 else if (IS_AMOEBALIVE(element))
9202 AmoebeAbleger(x, y);
9205 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9207 else if (element == EL_EXIT_CLOSED)
9209 else if (element == EL_SP_EXIT_CLOSED)
9211 else if (element == EL_EXPANDABLE_WALL_GROWING)
9213 else if (element == EL_EXPANDABLE_WALL ||
9214 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9215 element == EL_EXPANDABLE_WALL_VERTICAL ||
9216 element == EL_EXPANDABLE_WALL_ANY)
9218 else if (element == EL_FLAMES)
9219 CheckForDragon(x, y);
9221 else if (IS_AUTO_CHANGING(element))
9222 ChangeElement(x, y);
9224 else if (element == EL_EXPLOSION)
9225 ; /* drawing of correct explosion animation is handled separately */
9226 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9227 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9230 /* this may take place after moving, so 'element' may have changed */
9231 if (IS_AUTO_CHANGING(Feld[x][y]))
9232 ChangeElement(x, y);
9235 if (IS_BELT_ACTIVE(element))
9236 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9238 if (game.magic_wall_active)
9240 int jx = local_player->jx, jy = local_player->jy;
9242 /* play the element sound at the position nearest to the player */
9243 if ((element == EL_MAGIC_WALL_FULL ||
9244 element == EL_MAGIC_WALL_ACTIVE ||
9245 element == EL_MAGIC_WALL_EMPTYING ||
9246 element == EL_BD_MAGIC_WALL_FULL ||
9247 element == EL_BD_MAGIC_WALL_ACTIVE ||
9248 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9249 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9257 #if USE_NEW_AMOEBA_CODE
9258 /* new experimental amoeba growth stuff */
9260 if (!(FrameCounter % 8))
9263 static unsigned long random = 1684108901;
9265 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9268 x = (random >> 10) % lev_fieldx;
9269 y = (random >> 20) % lev_fieldy;
9271 x = RND(lev_fieldx);
9272 y = RND(lev_fieldy);
9274 element = Feld[x][y];
9277 if (!IS_PLAYER(x,y) &&
9278 (element == EL_EMPTY ||
9279 CAN_GROW_INTO(element) ||
9280 element == EL_QUICKSAND_EMPTY ||
9281 element == EL_ACID_SPLASH_LEFT ||
9282 element == EL_ACID_SPLASH_RIGHT))
9284 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9285 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9286 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9287 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9288 Feld[x][y] = EL_AMOEBA_DROP;
9291 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9292 if (!IS_PLAYER(x,y) &&
9293 (element == EL_EMPTY ||
9294 element == EL_SAND ||
9295 element == EL_QUICKSAND_EMPTY ||
9296 element == EL_ACID_SPLASH_LEFT ||
9297 element == EL_ACID_SPLASH_RIGHT))
9299 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9300 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9301 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9302 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9303 Feld[x][y] = EL_AMOEBA_DROP;
9307 random = random * 129 + 1;
9313 if (game.explosions_delayed)
9316 game.explosions_delayed = FALSE;
9318 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9320 element = Feld[x][y];
9322 if (ExplodeField[x][y])
9323 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9324 else if (element == EL_EXPLOSION)
9325 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9327 ExplodeField[x][y] = EX_TYPE_NONE;
9330 game.explosions_delayed = TRUE;
9333 if (game.magic_wall_active)
9335 if (!(game.magic_wall_time_left % 4))
9337 int element = Feld[magic_wall_x][magic_wall_y];
9339 if (element == EL_BD_MAGIC_WALL_FULL ||
9340 element == EL_BD_MAGIC_WALL_ACTIVE ||
9341 element == EL_BD_MAGIC_WALL_EMPTYING)
9342 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9344 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9347 if (game.magic_wall_time_left > 0)
9349 game.magic_wall_time_left--;
9350 if (!game.magic_wall_time_left)
9352 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9354 element = Feld[x][y];
9356 if (element == EL_MAGIC_WALL_ACTIVE ||
9357 element == EL_MAGIC_WALL_FULL)
9359 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9360 DrawLevelField(x, y);
9362 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9363 element == EL_BD_MAGIC_WALL_FULL)
9365 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9366 DrawLevelField(x, y);
9370 game.magic_wall_active = FALSE;
9375 if (game.light_time_left > 0)
9377 game.light_time_left--;
9379 if (game.light_time_left == 0)
9380 RedrawAllLightSwitchesAndInvisibleElements();
9383 if (game.timegate_time_left > 0)
9385 game.timegate_time_left--;
9387 if (game.timegate_time_left == 0)
9388 CloseAllOpenTimegates();
9391 for (i = 0; i < MAX_PLAYERS; i++)
9393 struct PlayerInfo *player = &stored_player[i];
9395 if (SHIELD_ON(player))
9397 if (player->shield_deadly_time_left)
9398 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9399 else if (player->shield_normal_time_left)
9400 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9404 if (TimeFrames >= FRAMES_PER_SECOND)
9409 for (i = 0; i < MAX_PLAYERS; i++)
9411 struct PlayerInfo *player = &stored_player[i];
9413 if (SHIELD_ON(player))
9415 player->shield_normal_time_left--;
9417 if (player->shield_deadly_time_left > 0)
9418 player->shield_deadly_time_left--;
9422 if (!level.use_step_counter)
9430 if (TimeLeft <= 10 && setup.time_limit)
9431 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9433 DrawGameValue_Time(TimeLeft);
9435 if (!TimeLeft && setup.time_limit)
9436 for (i = 0; i < MAX_PLAYERS; i++)
9437 KillHero(&stored_player[i]);
9439 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9440 DrawGameValue_Time(TimePlayed);
9443 if (tape.recording || tape.playing)
9444 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9448 PlayAllPlayersSound();
9450 if (options.debug) /* calculate frames per second */
9452 static unsigned long fps_counter = 0;
9453 static int fps_frames = 0;
9454 unsigned long fps_delay_ms = Counter() - fps_counter;
9458 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9460 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9463 fps_counter = Counter();
9466 redraw_mask |= REDRAW_FPS;
9470 if (stored_player[0].jx != stored_player[0].last_jx ||
9471 stored_player[0].jy != stored_player[0].last_jy)
9472 printf("::: %d, %d, %d, %d, %d\n",
9473 stored_player[0].MovDir,
9474 stored_player[0].MovPos,
9475 stored_player[0].GfxPos,
9476 stored_player[0].Frame,
9477 stored_player[0].StepFrame);
9480 #if USE_NEW_MOVE_DELAY
9481 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9486 for (i = 0; i < MAX_PLAYERS; i++)
9489 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9491 stored_player[i].Frame += move_frames;
9493 if (stored_player[i].MovPos != 0)
9494 stored_player[i].StepFrame += move_frames;
9496 #if USE_NEW_MOVE_DELAY
9497 if (stored_player[i].move_delay > 0)
9498 stored_player[i].move_delay--;
9501 if (stored_player[i].drop_delay > 0)
9502 stored_player[i].drop_delay--;
9507 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9509 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9511 local_player->show_envelope = 0;
9515 #if USE_NEW_RANDOMIZE
9516 /* use random number generator in every frame to make it less predictable */
9517 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9522 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9524 int min_x = x, min_y = y, max_x = x, max_y = y;
9527 for (i = 0; i < MAX_PLAYERS; i++)
9529 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9531 if (!stored_player[i].active || &stored_player[i] == player)
9534 min_x = MIN(min_x, jx);
9535 min_y = MIN(min_y, jy);
9536 max_x = MAX(max_x, jx);
9537 max_y = MAX(max_y, jy);
9540 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9543 static boolean AllPlayersInVisibleScreen()
9547 for (i = 0; i < MAX_PLAYERS; i++)
9549 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9551 if (!stored_player[i].active)
9554 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9561 void ScrollLevel(int dx, int dy)
9563 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9566 BlitBitmap(drawto_field, drawto_field,
9567 FX + TILEX * (dx == -1) - softscroll_offset,
9568 FY + TILEY * (dy == -1) - softscroll_offset,
9569 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9570 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9571 FX + TILEX * (dx == 1) - softscroll_offset,
9572 FY + TILEY * (dy == 1) - softscroll_offset);
9576 x = (dx == 1 ? BX1 : BX2);
9577 for (y = BY1; y <= BY2; y++)
9578 DrawScreenField(x, y);
9583 y = (dy == 1 ? BY1 : BY2);
9584 for (x = BX1; x <= BX2; x++)
9585 DrawScreenField(x, y);
9588 redraw_mask |= REDRAW_FIELD;
9592 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9594 int nextx = x + dx, nexty = y + dy;
9595 int element = Feld[x][y];
9598 element != EL_SP_PORT_LEFT &&
9599 element != EL_SP_GRAVITY_PORT_LEFT &&
9600 element != EL_SP_PORT_HORIZONTAL &&
9601 element != EL_SP_PORT_ANY) ||
9603 element != EL_SP_PORT_RIGHT &&
9604 element != EL_SP_GRAVITY_PORT_RIGHT &&
9605 element != EL_SP_PORT_HORIZONTAL &&
9606 element != EL_SP_PORT_ANY) ||
9608 element != EL_SP_PORT_UP &&
9609 element != EL_SP_GRAVITY_PORT_UP &&
9610 element != EL_SP_PORT_VERTICAL &&
9611 element != EL_SP_PORT_ANY) ||
9613 element != EL_SP_PORT_DOWN &&
9614 element != EL_SP_GRAVITY_PORT_DOWN &&
9615 element != EL_SP_PORT_VERTICAL &&
9616 element != EL_SP_PORT_ANY) ||
9617 !IN_LEV_FIELD(nextx, nexty) ||
9618 !IS_FREE(nextx, nexty))
9625 static boolean canFallDown(struct PlayerInfo *player)
9627 int jx = player->jx, jy = player->jy;
9629 return (IN_LEV_FIELD(jx, jy + 1) &&
9630 (IS_FREE(jx, jy + 1) ||
9631 #if USE_NEW_BLOCK_STYLE
9632 #if USE_GRAVITY_BUGFIX_OLD
9633 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9636 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9637 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9638 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9641 static boolean canPassField(int x, int y, int move_dir)
9643 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9644 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9645 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9648 int element = Feld[x][y];
9650 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9651 !CAN_MOVE(element) &&
9652 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9653 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9654 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9657 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9659 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9660 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9661 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9665 int nextx = newx + dx;
9666 int nexty = newy + dy;
9670 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9671 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9673 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9675 (IS_DIGGABLE(Feld[newx][newy]) ||
9676 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9677 canPassField(newx, newy, move_dir)));
9680 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9681 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9682 (IS_DIGGABLE(Feld[newx][newy]) ||
9683 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9684 canPassField(newx, newy, move_dir)));
9687 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9688 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9689 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9690 canPassField(newx, newy, move_dir)));
9692 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9693 (IS_DIGGABLE(Feld[newx][newy]) ||
9694 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9695 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9696 !CAN_MOVE(Feld[newx][newy]) &&
9697 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9698 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9699 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9705 static void CheckGravityMovement(struct PlayerInfo *player)
9707 if (game.gravity && !player->programmed_action)
9710 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9711 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9713 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9714 int move_dir_vertical = player->action & MV_VERTICAL;
9718 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9720 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9723 int jx = player->jx, jy = player->jy;
9725 boolean player_is_moving_to_valid_field =
9726 (!player_is_snapping &&
9727 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9728 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9732 (player->last_move_dir & MV_HORIZONTAL ?
9733 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9734 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9738 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9739 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9740 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9741 int new_jx = jx + dx, new_jy = jy + dy;
9742 int nextx = new_jx + dx, nexty = new_jy + dy;
9748 boolean player_can_fall_down = canFallDown(player);
9750 boolean player_can_fall_down =
9751 (IN_LEV_FIELD(jx, jy + 1) &&
9752 (IS_FREE(jx, jy + 1) ||
9753 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9757 boolean player_can_fall_down =
9758 (IN_LEV_FIELD(jx, jy + 1) &&
9759 (IS_FREE(jx, jy + 1)));
9763 boolean player_is_moving_to_valid_field =
9766 !player_is_snapping &&
9770 IN_LEV_FIELD(new_jx, new_jy) &&
9771 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9772 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9773 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9774 IN_LEV_FIELD(nextx, nexty) &&
9775 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9777 IN_LEV_FIELD(new_jx, new_jy) &&
9778 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9779 Feld[new_jx][new_jy] == EL_SAND ||
9780 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9781 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9782 /* !!! extend EL_SAND to anything diggable !!! */
9788 boolean player_is_standing_on_valid_field =
9789 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9790 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9794 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9795 player_can_fall_down,
9796 player_is_standing_on_valid_field,
9797 player_is_moving_to_valid_field,
9798 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9799 player->effective_action,
9800 player->can_fall_into_acid);
9803 if (player_can_fall_down &&
9805 !player_is_standing_on_valid_field &&
9807 !player_is_moving_to_valid_field)
9810 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9811 jx, jy, FrameCounter);
9814 player->programmed_action = MV_DOWN;
9819 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9822 return CheckGravityMovement(player);
9825 if (game.gravity && !player->programmed_action)
9827 int jx = player->jx, jy = player->jy;
9828 boolean field_under_player_is_free =
9829 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9830 boolean player_is_standing_on_valid_field =
9831 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9832 (IS_WALKABLE(Feld[jx][jy]) &&
9833 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9835 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9836 player->programmed_action = MV_DOWN;
9842 -----------------------------------------------------------------------------
9843 dx, dy: direction (non-diagonal) to try to move the player to
9844 real_dx, real_dy: direction as read from input device (can be diagonal)
9847 boolean MovePlayerOneStep(struct PlayerInfo *player,
9848 int dx, int dy, int real_dx, int real_dy)
9851 static int trigger_sides[4][2] =
9853 /* enter side leave side */
9854 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9855 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9856 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9857 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9859 int move_direction = (dx == -1 ? MV_LEFT :
9860 dx == +1 ? MV_RIGHT :
9862 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9863 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9864 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9866 int jx = player->jx, jy = player->jy;
9867 int new_jx = jx + dx, new_jy = jy + dy;
9871 if (!player->active || (!dx && !dy))
9872 return MF_NO_ACTION;
9874 player->MovDir = (dx < 0 ? MV_LEFT :
9877 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9879 if (!IN_LEV_FIELD(new_jx, new_jy))
9880 return MF_NO_ACTION;
9882 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9883 return MF_NO_ACTION;
9886 element = MovingOrBlocked2Element(new_jx, new_jy);
9888 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9891 if (DONT_RUN_INTO(element))
9893 if (element == EL_ACID && dx == 0 && dy == 1)
9895 SplashAcid(new_jx, new_jy);
9896 Feld[jx][jy] = EL_PLAYER_1;
9897 InitMovingField(jx, jy, MV_DOWN);
9898 Store[jx][jy] = EL_ACID;
9899 ContinueMoving(jx, jy);
9903 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9908 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9909 if (can_move != MF_MOVING)
9912 /* check if DigField() has caused relocation of the player */
9913 if (player->jx != jx || player->jy != jy)
9914 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9916 StorePlayer[jx][jy] = 0;
9917 player->last_jx = jx;
9918 player->last_jy = jy;
9919 player->jx = new_jx;
9920 player->jy = new_jy;
9921 StorePlayer[new_jx][new_jy] = player->element_nr;
9924 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9926 player->step_counter++;
9929 player->drop_delay = 0;
9932 PlayerVisit[jx][jy] = FrameCounter;
9934 ScrollPlayer(player, SCROLL_INIT);
9937 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9939 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9941 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9944 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9946 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9947 CE_PLAYER_ENTERS_X, enter_side);
9948 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9949 CE_ENTERED_BY_PLAYER, enter_side);
9956 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9958 int jx = player->jx, jy = player->jy;
9959 int old_jx = jx, old_jy = jy;
9960 int moved = MF_NO_ACTION;
9963 if (!player->active)
9968 if (player->MovPos == 0)
9970 player->is_moving = FALSE;
9971 player->is_digging = FALSE;
9972 player->is_collecting = FALSE;
9973 player->is_snapping = FALSE;
9974 player->is_pushing = FALSE;
9980 if (!player->active || (!dx && !dy))
9985 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9993 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9994 player->move_delay + player->move_delay_value);
9997 #if USE_NEW_MOVE_DELAY
9998 if (player->move_delay > 0)
10000 if (!FrameReached(&player->move_delay, player->move_delay_value))
10004 printf("::: can NOT move\n");
10010 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10011 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10018 printf("::: COULD move now\n");
10021 #if USE_NEW_MOVE_DELAY
10022 player->move_delay = -1; /* set to "uninitialized" value */
10025 /* store if player is automatically moved to next field */
10026 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10028 /* remove the last programmed player action */
10029 player->programmed_action = 0;
10031 if (player->MovPos)
10033 /* should only happen if pre-1.2 tape recordings are played */
10034 /* this is only for backward compatibility */
10036 int original_move_delay_value = player->move_delay_value;
10039 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10043 /* scroll remaining steps with finest movement resolution */
10044 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10046 while (player->MovPos)
10048 ScrollPlayer(player, SCROLL_GO_ON);
10049 ScrollScreen(NULL, SCROLL_GO_ON);
10051 #if USE_NEW_MOVE_DELAY
10052 AdvanceFrameAndPlayerCounters(player->index_nr);
10061 player->move_delay_value = original_move_delay_value;
10064 if (player->last_move_dir & MV_HORIZONTAL)
10066 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10067 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10071 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10072 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10078 if (moved & MF_MOVING && !ScreenMovPos &&
10079 (player == local_player || !options.network))
10081 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10082 int offset = (setup.scroll_delay ? 3 : 0);
10084 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10086 /* actual player has left the screen -- scroll in that direction */
10087 if (jx != old_jx) /* player has moved horizontally */
10088 scroll_x += (jx - old_jx);
10089 else /* player has moved vertically */
10090 scroll_y += (jy - old_jy);
10094 if (jx != old_jx) /* player has moved horizontally */
10096 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10097 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10098 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10100 /* don't scroll over playfield boundaries */
10101 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10102 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10104 /* don't scroll more than one field at a time */
10105 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10107 /* don't scroll against the player's moving direction */
10108 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10109 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10110 scroll_x = old_scroll_x;
10112 else /* player has moved vertically */
10114 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10115 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10116 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10118 /* don't scroll over playfield boundaries */
10119 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10120 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10122 /* don't scroll more than one field at a time */
10123 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10125 /* don't scroll against the player's moving direction */
10126 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10127 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10128 scroll_y = old_scroll_y;
10132 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10134 if (!options.network && !AllPlayersInVisibleScreen())
10136 scroll_x = old_scroll_x;
10137 scroll_y = old_scroll_y;
10141 ScrollScreen(player, SCROLL_INIT);
10142 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10149 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10151 if (!(moved & MF_MOVING) && !player->is_pushing)
10156 player->StepFrame = 0;
10158 if (moved & MF_MOVING)
10161 printf("::: REALLY moves now\n");
10164 if (old_jx != jx && old_jy == jy)
10165 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10166 else if (old_jx == jx && old_jy != jy)
10167 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10169 DrawLevelField(jx, jy); /* for "crumbled sand" */
10171 player->last_move_dir = player->MovDir;
10172 player->is_moving = TRUE;
10174 player->is_snapping = FALSE;
10178 player->is_switching = FALSE;
10181 player->is_dropping = FALSE;
10185 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10188 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10191 int move_direction = player->MovDir;
10193 int enter_side = MV_DIR_OPPOSITE(move_direction);
10194 int leave_side = move_direction;
10196 static int trigger_sides[4][2] =
10198 /* enter side leave side */
10199 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10200 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10201 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10202 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10204 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10205 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10207 int old_element = Feld[old_jx][old_jy];
10208 int new_element = Feld[jx][jy];
10211 /* !!! TEST ONLY !!! */
10212 if (IS_CUSTOM_ELEMENT(old_element))
10213 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10215 player->index_bit, leave_side);
10217 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10218 CE_PLAYER_LEAVES_X,
10219 player->index_bit, leave_side);
10221 if (IS_CUSTOM_ELEMENT(new_element))
10222 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10223 player->index_bit, enter_side);
10225 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10226 CE_PLAYER_ENTERS_X,
10227 player->index_bit, enter_side);
10237 CheckGravityMovementWhenNotMoving(player);
10240 player->last_move_dir = MV_NO_MOVING;
10242 player->is_moving = FALSE;
10244 #if USE_NEW_MOVE_STYLE
10245 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10246 /* ensure that the player is also allowed to move in the next frame */
10247 /* (currently, the player is forced to wait eight frames before he can try
10250 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10251 player->move_delay = 0; /* allow direct movement in the next frame */
10255 #if USE_NEW_MOVE_DELAY
10256 if (player->move_delay == -1) /* not yet initialized by DigField() */
10257 player->move_delay = player->move_delay_value;
10260 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10262 TestIfHeroTouchesBadThing(jx, jy);
10263 TestIfPlayerTouchesCustomElement(jx, jy);
10266 if (!player->active)
10267 RemoveHero(player);
10272 void ScrollPlayer(struct PlayerInfo *player, int mode)
10274 int jx = player->jx, jy = player->jy;
10275 int last_jx = player->last_jx, last_jy = player->last_jy;
10276 int move_stepsize = TILEX / player->move_delay_value;
10278 if (!player->active || !player->MovPos)
10281 if (mode == SCROLL_INIT)
10283 player->actual_frame_counter = FrameCounter;
10284 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10287 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10289 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10290 player->block_delay);
10293 #if USE_NEW_BLOCK_STYLE
10296 if (player->block_delay <= 0)
10297 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10300 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10301 Feld[last_jx][last_jy] == EL_EMPTY)
10303 int last_field_block_delay = 0; /* start with no blocking at all */
10304 int block_delay_adjustment = player->block_delay_adjustment;
10306 /* if player blocks last field, add delay for exactly one move */
10307 if (player->block_last_field)
10309 last_field_block_delay += player->move_delay_value;
10311 #if USE_GRAVITY_BUGFIX_NEW
10312 /* when blocking enabled, prevent moving up despite gravity */
10313 if (game.gravity && player->MovDir == MV_UP)
10314 block_delay_adjustment = -1;
10318 /* add block delay adjustment (also possible when not blocking) */
10319 last_field_block_delay += block_delay_adjustment;
10322 #if USE_BLOCK_DELAY_BUGFIX
10323 /* when blocking enabled, correct block delay for fast movement */
10324 if (player->block_last_field &&
10325 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10326 last_field_block_delay =
10327 player->move_delay_value + player->block_delay_adjustment;
10332 #if USE_GRAVITY_BUGFIX_NEW
10333 /* when blocking enabled, correct block delay for gravity movement */
10334 if (player->block_last_field &&
10335 game.gravity && player->MovDir == MV_UP)
10336 last_field_block_delay = player->move_delay_value - 1;
10340 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10341 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10344 #if USE_NEW_MOVE_STYLE
10345 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10346 player->block_last_field) &&
10347 Feld[last_jx][last_jy] == EL_EMPTY)
10348 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10350 if (Feld[last_jx][last_jy] == EL_EMPTY)
10351 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10356 DrawPlayer(player);
10361 else if (!FrameReached(&player->actual_frame_counter, 1))
10364 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10365 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10367 #if USE_NEW_BLOCK_STYLE
10369 if (!player->block_last_field &&
10370 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10372 RemoveField(last_jx, last_jy);
10374 Feld[last_jx][last_jy] = EL_EMPTY;
10378 /* before DrawPlayer() to draw correct player graphic for this case */
10379 if (player->MovPos == 0)
10380 CheckGravityMovement(player);
10383 DrawPlayer(player); /* needed here only to cleanup last field */
10386 if (player->MovPos == 0) /* player reached destination field */
10389 if (player->move_delay_reset_counter > 0)
10391 player->move_delay_reset_counter--;
10393 if (player->move_delay_reset_counter == 0)
10395 /* continue with normal speed after quickly moving through gate */
10396 HALVE_PLAYER_SPEED(player);
10398 /* be able to make the next move without delay */
10399 player->move_delay = 0;
10403 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10405 /* continue with normal speed after quickly moving through gate */
10406 HALVE_PLAYER_SPEED(player);
10408 /* be able to make the next move without delay */
10409 player->move_delay = 0;
10413 #if USE_NEW_BLOCK_STYLE
10415 if (player->block_last_field &&
10416 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10418 RemoveField(last_jx, last_jy);
10420 Feld[last_jx][last_jy] = EL_EMPTY;
10424 player->last_jx = jx;
10425 player->last_jy = jy;
10427 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10428 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10429 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10431 DrawPlayer(player); /* needed here only to cleanup last field */
10432 RemoveHero(player);
10434 if (local_player->friends_still_needed == 0 ||
10435 IS_SP_ELEMENT(Feld[jx][jy]))
10436 player->LevelSolved = player->GameOver = TRUE;
10440 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10441 /* this breaks one level: "machine", level 000 */
10443 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10446 int move_direction = player->MovDir;
10448 int enter_side = MV_DIR_OPPOSITE(move_direction);
10449 int leave_side = move_direction;
10451 static int trigger_sides[4][2] =
10453 /* enter side leave side */
10454 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10455 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10456 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10457 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10459 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10460 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10462 int old_jx = last_jx;
10463 int old_jy = last_jy;
10464 int old_element = Feld[old_jx][old_jy];
10465 int new_element = Feld[jx][jy];
10468 /* !!! TEST ONLY !!! */
10469 if (IS_CUSTOM_ELEMENT(old_element))
10470 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10472 player->index_bit, leave_side);
10474 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10475 CE_PLAYER_LEAVES_X,
10476 player->index_bit, leave_side);
10478 if (IS_CUSTOM_ELEMENT(new_element))
10479 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10480 player->index_bit, enter_side);
10482 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10483 CE_PLAYER_ENTERS_X,
10484 player->index_bit, enter_side);
10490 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10492 TestIfHeroTouchesBadThing(jx, jy);
10493 TestIfPlayerTouchesCustomElement(jx, jy);
10496 /* needed because pushed element has not yet reached its destination,
10497 so it would trigger a change event at its previous field location */
10498 if (!player->is_pushing)
10500 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10503 if (!player->active)
10504 RemoveHero(player);
10507 if (level.use_step_counter)
10517 if (TimeLeft <= 10 && setup.time_limit)
10518 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10520 DrawGameValue_Time(TimeLeft);
10522 if (!TimeLeft && setup.time_limit)
10523 for (i = 0; i < MAX_PLAYERS; i++)
10524 KillHero(&stored_player[i]);
10526 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10527 DrawGameValue_Time(TimePlayed);
10530 if (tape.single_step && tape.recording && !tape.pausing &&
10531 !player->programmed_action)
10532 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10536 void ScrollScreen(struct PlayerInfo *player, int mode)
10538 static unsigned long screen_frame_counter = 0;
10540 if (mode == SCROLL_INIT)
10542 /* set scrolling step size according to actual player's moving speed */
10543 ScrollStepSize = TILEX / player->move_delay_value;
10545 screen_frame_counter = FrameCounter;
10546 ScreenMovDir = player->MovDir;
10547 ScreenMovPos = player->MovPos;
10548 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10551 else if (!FrameReached(&screen_frame_counter, 1))
10556 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10557 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10558 redraw_mask |= REDRAW_FIELD;
10561 ScreenMovDir = MV_NO_MOVING;
10564 void TestIfPlayerTouchesCustomElement(int x, int y)
10566 static int xy[4][2] =
10573 static int trigger_sides[4][2] =
10575 /* center side border side */
10576 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10577 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10578 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10579 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10581 static int touch_dir[4] =
10583 MV_LEFT | MV_RIGHT,
10588 int center_element = Feld[x][y]; /* should always be non-moving! */
10591 for (i = 0; i < NUM_DIRECTIONS; i++)
10593 int xx = x + xy[i][0];
10594 int yy = y + xy[i][1];
10595 int center_side = trigger_sides[i][0];
10596 int border_side = trigger_sides[i][1];
10597 int border_element;
10599 if (!IN_LEV_FIELD(xx, yy))
10602 if (IS_PLAYER(x, y))
10604 struct PlayerInfo *player = PLAYERINFO(x, y);
10606 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10607 border_element = Feld[xx][yy]; /* may be moving! */
10608 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10609 border_element = Feld[xx][yy];
10610 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10611 border_element = MovingOrBlocked2Element(xx, yy);
10613 continue; /* center and border element do not touch */
10616 /* !!! TEST ONLY !!! */
10617 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10618 player->index_bit, border_side);
10619 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10620 CE_PLAYER_TOUCHES_X,
10621 player->index_bit, border_side);
10623 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10624 CE_PLAYER_TOUCHES_X,
10625 player->index_bit, border_side);
10626 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10627 player->index_bit, border_side);
10630 else if (IS_PLAYER(xx, yy))
10632 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10634 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10636 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10637 continue; /* center and border element do not touch */
10641 /* !!! TEST ONLY !!! */
10642 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10643 player->index_bit, center_side);
10644 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10645 CE_PLAYER_TOUCHES_X,
10646 player->index_bit, center_side);
10648 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10649 CE_PLAYER_TOUCHES_X,
10650 player->index_bit, center_side);
10651 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10652 player->index_bit, center_side);
10660 void TestIfElementTouchesCustomElement(int x, int y)
10662 static int xy[4][2] =
10669 static int trigger_sides[4][2] =
10671 /* center side border side */
10672 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10673 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10674 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10675 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10677 static int touch_dir[4] =
10679 MV_LEFT | MV_RIGHT,
10684 boolean change_center_element = FALSE;
10685 int center_element_change_page = 0;
10686 int center_element = Feld[x][y]; /* should always be non-moving! */
10687 int border_trigger_element = EL_UNDEFINED;
10690 for (i = 0; i < NUM_DIRECTIONS; i++)
10692 int xx = x + xy[i][0];
10693 int yy = y + xy[i][1];
10694 int center_side = trigger_sides[i][0];
10695 int border_side = trigger_sides[i][1];
10696 int border_element;
10698 if (!IN_LEV_FIELD(xx, yy))
10701 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10702 border_element = Feld[xx][yy]; /* may be moving! */
10703 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10704 border_element = Feld[xx][yy];
10705 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10706 border_element = MovingOrBlocked2Element(xx, yy);
10708 continue; /* center and border element do not touch */
10710 /* check for change of center element (but change it only once) */
10711 if (IS_CUSTOM_ELEMENT(center_element) &&
10712 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10713 !change_center_element)
10715 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10717 struct ElementChangeInfo *change =
10718 &element_info[center_element].change_page[j];
10720 if (change->can_change &&
10721 change->has_event[CE_TOUCHING_X] &&
10722 change->trigger_side & border_side &&
10724 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10726 change->trigger_element == border_element
10730 change_center_element = TRUE;
10731 center_element_change_page = j;
10732 border_trigger_element = border_element;
10739 /* check for change of border element */
10740 if (IS_CUSTOM_ELEMENT(border_element) &&
10741 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10743 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10745 struct ElementChangeInfo *change =
10746 &element_info[border_element].change_page[j];
10748 if (change->can_change &&
10749 change->has_event[CE_TOUCHING_X] &&
10750 change->trigger_side & center_side &&
10752 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10754 change->trigger_element == center_element
10759 printf("::: border_element %d, %d\n", x, y);
10762 CheckElementChangeByPage(xx, yy, border_element, center_element,
10770 if (change_center_element)
10773 printf("::: center_element %d, %d\n", x, y);
10776 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10777 CE_TOUCHING_X, center_element_change_page);
10781 void TestIfElementHitsCustomElement(int x, int y, int direction)
10783 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10784 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10785 int hitx = x + dx, hity = y + dy;
10786 int hitting_element = Feld[x][y];
10787 int touched_element;
10789 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10790 !IS_FREE(hitx, hity) &&
10791 (!IS_MOVING(hitx, hity) ||
10792 MovDir[hitx][hity] != direction ||
10793 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10796 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10800 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10804 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10805 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10807 #if !USE_HITTING_SOMETHING_BUGFIX
10808 /* "hitting something" is also true when hitting the playfield border */
10809 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10810 CE_HITTING_SOMETHING, direction);
10813 if (IN_LEV_FIELD(hitx, hity))
10815 int opposite_direction = MV_DIR_OPPOSITE(direction);
10816 int hitting_side = direction;
10817 int touched_side = opposite_direction;
10819 int touched_element = MovingOrBlocked2Element(hitx, hity);
10822 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10823 MovDir[hitx][hity] != direction ||
10824 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10833 #if !USE_HIT_BY_SOMETHING_BUGFIX
10834 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10835 CE_HIT_BY_SOMETHING, opposite_direction);
10838 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10839 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10841 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10843 struct ElementChangeInfo *change =
10844 &element_info[hitting_element].change_page[i];
10846 if (change->can_change &&
10847 change->has_event[CE_HITTING_X] &&
10848 change->trigger_side & touched_side &&
10851 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10853 change->trigger_element == touched_element
10857 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10864 if (IS_CUSTOM_ELEMENT(touched_element) &&
10865 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10867 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10869 struct ElementChangeInfo *change =
10870 &element_info[touched_element].change_page[i];
10872 if (change->can_change &&
10873 change->has_event[CE_HIT_BY_X] &&
10874 change->trigger_side & hitting_side &&
10876 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10878 change->trigger_element == hitting_element
10882 CheckElementChangeByPage(hitx, hity, touched_element,
10883 hitting_element, CE_HIT_BY_X, i);
10889 #if USE_HIT_BY_SOMETHING_BUGFIX
10890 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10891 CE_HIT_BY_SOMETHING, opposite_direction);
10896 #if USE_HITTING_SOMETHING_BUGFIX
10897 /* "hitting something" is also true when hitting the playfield border */
10898 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10899 CE_HITTING_SOMETHING, direction);
10904 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10906 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10907 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10908 int hitx = x + dx, hity = y + dy;
10909 int hitting_element = Feld[x][y];
10910 int touched_element;
10912 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10913 !IS_FREE(hitx, hity) &&
10914 (!IS_MOVING(hitx, hity) ||
10915 MovDir[hitx][hity] != direction ||
10916 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10919 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10923 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10927 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10928 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10930 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10931 EP_CAN_SMASH_EVERYTHING, direction);
10933 if (IN_LEV_FIELD(hitx, hity))
10935 int opposite_direction = MV_DIR_OPPOSITE(direction);
10936 int hitting_side = direction;
10937 int touched_side = opposite_direction;
10939 int touched_element = MovingOrBlocked2Element(hitx, hity);
10942 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10943 MovDir[hitx][hity] != direction ||
10944 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10953 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10954 CE_SMASHED_BY_SOMETHING, opposite_direction);
10956 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10957 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10959 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10961 struct ElementChangeInfo *change =
10962 &element_info[hitting_element].change_page[i];
10964 if (change->can_change &&
10965 change->has_event[CE_OTHER_IS_SMASHING] &&
10966 change->trigger_side & touched_side &&
10969 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10971 change->trigger_element == touched_element
10975 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10976 CE_OTHER_IS_SMASHING, i);
10982 if (IS_CUSTOM_ELEMENT(touched_element) &&
10983 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10985 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10987 struct ElementChangeInfo *change =
10988 &element_info[touched_element].change_page[i];
10990 if (change->can_change &&
10991 change->has_event[CE_OTHER_GETS_SMASHED] &&
10992 change->trigger_side & hitting_side &&
10994 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10996 change->trigger_element == hitting_element
11000 CheckElementChangeByPage(hitx, hity, touched_element,
11001 hitting_element, CE_OTHER_GETS_SMASHED,i);
11011 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11013 int i, kill_x = -1, kill_y = -1;
11014 int bad_element = -1;
11015 static int test_xy[4][2] =
11022 static int test_dir[4] =
11030 for (i = 0; i < NUM_DIRECTIONS; i++)
11032 int test_x, test_y, test_move_dir, test_element;
11034 test_x = good_x + test_xy[i][0];
11035 test_y = good_y + test_xy[i][1];
11037 if (!IN_LEV_FIELD(test_x, test_y))
11041 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11044 test_element = Feld[test_x][test_y];
11046 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11049 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11050 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11052 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11053 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11057 bad_element = test_element;
11063 if (kill_x != -1 || kill_y != -1)
11065 if (IS_PLAYER(good_x, good_y))
11067 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11070 if (player->shield_deadly_time_left > 0 &&
11071 !IS_INDESTRUCTIBLE(bad_element))
11072 Bang(kill_x, kill_y);
11073 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11076 if (player->shield_deadly_time_left > 0)
11077 Bang(kill_x, kill_y);
11078 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11083 Bang(good_x, good_y);
11087 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11089 int i, kill_x = -1, kill_y = -1;
11090 int bad_element = Feld[bad_x][bad_y];
11091 static int test_xy[4][2] =
11098 static int touch_dir[4] =
11100 MV_LEFT | MV_RIGHT,
11105 static int test_dir[4] =
11113 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11116 for (i = 0; i < NUM_DIRECTIONS; i++)
11118 int test_x, test_y, test_move_dir, test_element;
11120 test_x = bad_x + test_xy[i][0];
11121 test_y = bad_y + test_xy[i][1];
11122 if (!IN_LEV_FIELD(test_x, test_y))
11126 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11128 test_element = Feld[test_x][test_y];
11130 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11131 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11133 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11134 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11136 /* good thing is player or penguin that does not move away */
11137 if (IS_PLAYER(test_x, test_y))
11139 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11141 if (bad_element == EL_ROBOT && player->is_moving)
11142 continue; /* robot does not kill player if he is moving */
11144 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11146 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11147 continue; /* center and border element do not touch */
11154 else if (test_element == EL_PENGUIN)
11163 if (kill_x != -1 || kill_y != -1)
11165 if (IS_PLAYER(kill_x, kill_y))
11167 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11170 if (player->shield_deadly_time_left > 0 &&
11171 !IS_INDESTRUCTIBLE(bad_element))
11172 Bang(bad_x, bad_y);
11173 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11176 if (player->shield_deadly_time_left > 0)
11177 Bang(bad_x, bad_y);
11178 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11183 Bang(kill_x, kill_y);
11187 void TestIfHeroTouchesBadThing(int x, int y)
11189 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11192 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11194 TestIfGoodThingHitsBadThing(x, y, move_dir);
11197 void TestIfBadThingTouchesHero(int x, int y)
11199 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11202 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11204 TestIfBadThingHitsGoodThing(x, y, move_dir);
11207 void TestIfFriendTouchesBadThing(int x, int y)
11209 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11212 void TestIfBadThingTouchesFriend(int x, int y)
11214 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11217 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11219 int i, kill_x = bad_x, kill_y = bad_y;
11220 static int xy[4][2] =
11228 for (i = 0; i < NUM_DIRECTIONS; i++)
11232 x = bad_x + xy[i][0];
11233 y = bad_y + xy[i][1];
11234 if (!IN_LEV_FIELD(x, y))
11237 element = Feld[x][y];
11238 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11239 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11247 if (kill_x != bad_x || kill_y != bad_y)
11248 Bang(bad_x, bad_y);
11251 void KillHero(struct PlayerInfo *player)
11253 int jx = player->jx, jy = player->jy;
11255 if (!player->active)
11258 /* remove accessible field at the player's position */
11259 Feld[jx][jy] = EL_EMPTY;
11261 /* deactivate shield (else Bang()/Explode() would not work right) */
11262 player->shield_normal_time_left = 0;
11263 player->shield_deadly_time_left = 0;
11269 static void KillHeroUnlessEnemyProtected(int x, int y)
11271 if (!PLAYER_ENEMY_PROTECTED(x, y))
11272 KillHero(PLAYERINFO(x, y));
11275 static void KillHeroUnlessExplosionProtected(int x, int y)
11277 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11278 KillHero(PLAYERINFO(x, y));
11281 void BuryHero(struct PlayerInfo *player)
11283 int jx = player->jx, jy = player->jy;
11285 if (!player->active)
11289 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11291 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11293 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11295 player->GameOver = TRUE;
11296 RemoveHero(player);
11299 void RemoveHero(struct PlayerInfo *player)
11301 int jx = player->jx, jy = player->jy;
11302 int i, found = FALSE;
11304 player->present = FALSE;
11305 player->active = FALSE;
11307 if (!ExplodeField[jx][jy])
11308 StorePlayer[jx][jy] = 0;
11310 for (i = 0; i < MAX_PLAYERS; i++)
11311 if (stored_player[i].active)
11315 AllPlayersGone = TRUE;
11322 =============================================================================
11323 checkDiagonalPushing()
11324 -----------------------------------------------------------------------------
11325 check if diagonal input device direction results in pushing of object
11326 (by checking if the alternative direction is walkable, diggable, ...)
11327 =============================================================================
11330 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11331 int x, int y, int real_dx, int real_dy)
11333 int jx, jy, dx, dy, xx, yy;
11335 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11338 /* diagonal direction: check alternative direction */
11343 xx = jx + (dx == 0 ? real_dx : 0);
11344 yy = jy + (dy == 0 ? real_dy : 0);
11346 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11350 =============================================================================
11352 -----------------------------------------------------------------------------
11353 x, y: field next to player (non-diagonal) to try to dig to
11354 real_dx, real_dy: direction as read from input device (can be diagonal)
11355 =============================================================================
11358 int DigField(struct PlayerInfo *player,
11359 int oldx, int oldy, int x, int y,
11360 int real_dx, int real_dy, int mode)
11363 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11365 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11366 boolean player_was_pushing = player->is_pushing;
11367 int jx = oldx, jy = oldy;
11368 int dx = x - jx, dy = y - jy;
11369 int nextx = x + dx, nexty = y + dy;
11370 int move_direction = (dx == -1 ? MV_LEFT :
11371 dx == +1 ? MV_RIGHT :
11373 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11374 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11376 int dig_side = MV_DIR_OPPOSITE(move_direction);
11378 static int trigger_sides[4] =
11380 CH_SIDE_RIGHT, /* moving left */
11381 CH_SIDE_LEFT, /* moving right */
11382 CH_SIDE_BOTTOM, /* moving up */
11383 CH_SIDE_TOP, /* moving down */
11385 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11387 int old_element = Feld[jx][jy];
11390 if (is_player) /* function can also be called by EL_PENGUIN */
11392 if (player->MovPos == 0)
11394 player->is_digging = FALSE;
11395 player->is_collecting = FALSE;
11398 if (player->MovPos == 0) /* last pushing move finished */
11399 player->is_pushing = FALSE;
11401 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11403 player->is_switching = FALSE;
11404 #if USE_NEW_PUSH_DELAY
11405 player->push_delay = -1;
11407 player->push_delay = 0;
11410 return MF_NO_ACTION;
11414 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11415 return MF_NO_ACTION;
11420 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11422 if (IS_TUBE(Feld[jx][jy]) ||
11423 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11427 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11428 int tube_leave_directions[][2] =
11430 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11431 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11432 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11433 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11434 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11435 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11436 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11437 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11438 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11439 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11440 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11441 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11444 while (tube_leave_directions[i][0] != tube_element)
11447 if (tube_leave_directions[i][0] == -1) /* should not happen */
11451 if (!(tube_leave_directions[i][1] & move_direction))
11452 return MF_NO_ACTION; /* tube has no opening in this direction */
11457 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11458 old_element = Back[jx][jy];
11462 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11463 return MF_NO_ACTION; /* field has no opening in this direction */
11465 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11466 return MF_NO_ACTION; /* field has no opening in this direction */
11468 element = Feld[x][y];
11470 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11471 return MF_NO_ACTION;
11473 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11474 game.engine_version >= VERSION_IDENT(2,2,0,0))
11475 return MF_NO_ACTION;
11478 if (game.gravity && is_player && !player->is_auto_moving &&
11479 canFallDown(player) && move_direction != MV_DOWN &&
11480 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11481 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11485 if (element == EL_EMPTY_SPACE &&
11486 game.gravity && !player->is_auto_moving &&
11487 canFallDown(player) && move_direction != MV_DOWN)
11488 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11494 case EL_SP_PORT_LEFT:
11495 case EL_SP_PORT_RIGHT:
11496 case EL_SP_PORT_UP:
11497 case EL_SP_PORT_DOWN:
11498 case EL_SP_PORT_HORIZONTAL:
11499 case EL_SP_PORT_VERTICAL:
11500 case EL_SP_PORT_ANY:
11501 case EL_SP_GRAVITY_PORT_LEFT:
11502 case EL_SP_GRAVITY_PORT_RIGHT:
11503 case EL_SP_GRAVITY_PORT_UP:
11504 case EL_SP_GRAVITY_PORT_DOWN:
11506 if (!canEnterSupaplexPort(x, y, dx, dy))
11507 return MF_NO_ACTION;
11510 element != EL_SP_PORT_LEFT &&
11511 element != EL_SP_GRAVITY_PORT_LEFT &&
11512 element != EL_SP_PORT_HORIZONTAL &&
11513 element != EL_SP_PORT_ANY) ||
11515 element != EL_SP_PORT_RIGHT &&
11516 element != EL_SP_GRAVITY_PORT_RIGHT &&
11517 element != EL_SP_PORT_HORIZONTAL &&
11518 element != EL_SP_PORT_ANY) ||
11520 element != EL_SP_PORT_UP &&
11521 element != EL_SP_GRAVITY_PORT_UP &&
11522 element != EL_SP_PORT_VERTICAL &&
11523 element != EL_SP_PORT_ANY) ||
11525 element != EL_SP_PORT_DOWN &&
11526 element != EL_SP_GRAVITY_PORT_DOWN &&
11527 element != EL_SP_PORT_VERTICAL &&
11528 element != EL_SP_PORT_ANY) ||
11529 !IN_LEV_FIELD(nextx, nexty) ||
11530 !IS_FREE(nextx, nexty))
11531 return MF_NO_ACTION;
11534 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11535 element == EL_SP_GRAVITY_PORT_RIGHT ||
11536 element == EL_SP_GRAVITY_PORT_UP ||
11537 element == EL_SP_GRAVITY_PORT_DOWN)
11538 game.gravity = !game.gravity;
11540 /* automatically move to the next field with double speed */
11541 player->programmed_action = move_direction;
11543 if (player->move_delay_reset_counter == 0)
11545 player->move_delay_reset_counter = 2; /* two double speed steps */
11547 DOUBLE_PLAYER_SPEED(player);
11550 player->move_delay_reset_counter = 2;
11552 DOUBLE_PLAYER_SPEED(player);
11556 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11559 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11565 case EL_TUBE_VERTICAL:
11566 case EL_TUBE_HORIZONTAL:
11567 case EL_TUBE_VERTICAL_LEFT:
11568 case EL_TUBE_VERTICAL_RIGHT:
11569 case EL_TUBE_HORIZONTAL_UP:
11570 case EL_TUBE_HORIZONTAL_DOWN:
11571 case EL_TUBE_LEFT_UP:
11572 case EL_TUBE_LEFT_DOWN:
11573 case EL_TUBE_RIGHT_UP:
11574 case EL_TUBE_RIGHT_DOWN:
11577 int tube_enter_directions[][2] =
11579 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11580 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11581 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11582 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11583 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11584 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11585 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11586 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11587 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11588 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11589 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11590 { -1, MV_NO_MOVING }
11593 while (tube_enter_directions[i][0] != element)
11596 if (tube_enter_directions[i][0] == -1) /* should not happen */
11600 if (!(tube_enter_directions[i][1] & move_direction))
11601 return MF_NO_ACTION; /* tube has no opening in this direction */
11603 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11611 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11613 if (IS_WALKABLE(element))
11616 int sound_element = SND_ELEMENT(element);
11617 int sound_action = ACTION_WALKING;
11620 if (!ACCESS_FROM(element, opposite_direction))
11621 return MF_NO_ACTION; /* field not accessible from this direction */
11625 if (element == EL_EMPTY_SPACE &&
11626 game.gravity && !player->is_auto_moving &&
11627 canFallDown(player) && move_direction != MV_DOWN)
11628 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11631 if (IS_RND_GATE(element))
11633 if (!player->key[RND_GATE_NR(element)])
11634 return MF_NO_ACTION;
11636 else if (IS_RND_GATE_GRAY(element))
11638 if (!player->key[RND_GATE_GRAY_NR(element)])
11639 return MF_NO_ACTION;
11641 else if (element == EL_EXIT_OPEN ||
11642 element == EL_SP_EXIT_OPEN ||
11643 element == EL_SP_EXIT_OPENING)
11645 sound_action = ACTION_PASSING; /* player is passing exit */
11647 else if (element == EL_EMPTY)
11649 sound_action = ACTION_MOVING; /* nothing to walk on */
11652 /* play sound from background or player, whatever is available */
11653 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11654 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11656 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11661 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11663 else if (IS_PASSABLE(element))
11667 if (!canPassField(x, y, move_direction))
11668 return MF_NO_ACTION;
11673 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11674 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11675 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11676 return MF_NO_ACTION;
11678 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11679 return MF_NO_ACTION;
11684 if (!ACCESS_FROM(element, opposite_direction))
11685 return MF_NO_ACTION; /* field not accessible from this direction */
11687 if (IS_CUSTOM_ELEMENT(element) &&
11688 !ACCESS_FROM(element, opposite_direction))
11689 return MF_NO_ACTION; /* field not accessible from this direction */
11693 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11694 return MF_NO_ACTION;
11699 if (IS_EM_GATE(element))
11701 if (!player->key[EM_GATE_NR(element)])
11702 return MF_NO_ACTION;
11704 else if (IS_EM_GATE_GRAY(element))
11706 if (!player->key[EM_GATE_GRAY_NR(element)])
11707 return MF_NO_ACTION;
11709 else if (IS_SP_PORT(element))
11711 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11712 element == EL_SP_GRAVITY_PORT_RIGHT ||
11713 element == EL_SP_GRAVITY_PORT_UP ||
11714 element == EL_SP_GRAVITY_PORT_DOWN)
11715 game.gravity = !game.gravity;
11716 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11717 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11718 element == EL_SP_GRAVITY_ON_PORT_UP ||
11719 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11720 game.gravity = TRUE;
11721 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11722 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11723 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11724 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11725 game.gravity = FALSE;
11728 /* automatically move to the next field with double speed */
11729 player->programmed_action = move_direction;
11731 if (player->move_delay_reset_counter == 0)
11733 player->move_delay_reset_counter = 2; /* two double speed steps */
11735 DOUBLE_PLAYER_SPEED(player);
11738 player->move_delay_reset_counter = 2;
11740 DOUBLE_PLAYER_SPEED(player);
11743 PlayLevelSoundAction(x, y, ACTION_PASSING);
11747 else if (IS_DIGGABLE(element))
11751 if (mode != DF_SNAP)
11754 GfxElement[x][y] = GFX_ELEMENT(element);
11757 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11759 player->is_digging = TRUE;
11762 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11764 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11765 player->index_bit, dig_side);
11768 if (mode == DF_SNAP)
11769 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11774 else if (IS_COLLECTIBLE(element))
11778 if (is_player && mode != DF_SNAP)
11780 GfxElement[x][y] = element;
11781 player->is_collecting = TRUE;
11784 if (element == EL_SPEED_PILL)
11785 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11786 else if (element == EL_EXTRA_TIME && level.time > 0)
11789 DrawGameValue_Time(TimeLeft);
11791 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11793 player->shield_normal_time_left += 10;
11794 if (element == EL_SHIELD_DEADLY)
11795 player->shield_deadly_time_left += 10;
11797 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11799 if (player->inventory_size < MAX_INVENTORY_SIZE)
11800 player->inventory_element[player->inventory_size++] = element;
11802 DrawGameValue_Dynamite(local_player->inventory_size);
11804 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11806 player->dynabomb_count++;
11807 player->dynabombs_left++;
11809 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11811 player->dynabomb_size++;
11813 else if (element == EL_DYNABOMB_INCREASE_POWER)
11815 player->dynabomb_xl = TRUE;
11817 else if (IS_KEY(element))
11819 player->key[KEY_NR(element)] = TRUE;
11821 DrawGameValue_Keys(player->key);
11823 redraw_mask |= REDRAW_DOOR_1;
11825 else if (IS_ENVELOPE(element))
11828 player->show_envelope = element;
11830 ShowEnvelope(element - EL_ENVELOPE_1);
11833 else if (IS_DROPPABLE(element) ||
11834 IS_THROWABLE(element)) /* can be collected and dropped */
11838 if (element_info[element].collect_count == 0)
11839 player->inventory_infinite_element = element;
11841 for (i = 0; i < element_info[element].collect_count; i++)
11842 if (player->inventory_size < MAX_INVENTORY_SIZE)
11843 player->inventory_element[player->inventory_size++] = element;
11845 DrawGameValue_Dynamite(local_player->inventory_size);
11847 else if (element_info[element].collect_count > 0)
11849 local_player->gems_still_needed -=
11850 element_info[element].collect_count;
11851 if (local_player->gems_still_needed < 0)
11852 local_player->gems_still_needed = 0;
11854 DrawGameValue_Emeralds(local_player->gems_still_needed);
11857 RaiseScoreElement(element);
11858 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11861 CheckTriggeredElementChangeByPlayer(x, y, element,
11862 CE_PLAYER_COLLECTS_X,
11863 player->index_bit, dig_side);
11866 if (mode == DF_SNAP)
11867 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11872 else if (IS_PUSHABLE(element))
11874 if (mode == DF_SNAP && element != EL_BD_ROCK)
11875 return MF_NO_ACTION;
11877 if (CAN_FALL(element) && dy)
11878 return MF_NO_ACTION;
11880 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11881 !(element == EL_SPRING && level.use_spring_bug))
11882 return MF_NO_ACTION;
11885 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11886 ((move_direction & MV_VERTICAL &&
11887 ((element_info[element].move_pattern & MV_LEFT &&
11888 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11889 (element_info[element].move_pattern & MV_RIGHT &&
11890 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11891 (move_direction & MV_HORIZONTAL &&
11892 ((element_info[element].move_pattern & MV_UP &&
11893 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11894 (element_info[element].move_pattern & MV_DOWN &&
11895 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11896 return MF_NO_ACTION;
11900 /* do not push elements already moving away faster than player */
11901 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11902 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11903 return MF_NO_ACTION;
11905 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11906 return MF_NO_ACTION;
11912 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11914 if (player->push_delay_value == -1 || !player_was_pushing)
11915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11917 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11919 if (player->push_delay_value == -1)
11920 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11923 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11925 if (player->push_delay_value == -1 || !player_was_pushing)
11926 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11929 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11931 if (!player->is_pushing)
11932 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11936 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11937 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11938 !player_is_pushing))
11939 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11942 if (!player->is_pushing &&
11943 game.engine_version >= VERSION_IDENT(2,2,0,7))
11944 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11948 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11949 player->push_delay, player->push_delay_value,
11950 FrameCounter, game.engine_version,
11951 player_was_pushing, player->is_pushing,
11952 element, element_info[element].token_name,
11953 GET_NEW_PUSH_DELAY(element));
11956 player->is_pushing = TRUE;
11958 if (!(IN_LEV_FIELD(nextx, nexty) &&
11959 (IS_FREE(nextx, nexty) ||
11960 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11961 IS_SB_ELEMENT(element)))))
11962 return MF_NO_ACTION;
11964 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11965 return MF_NO_ACTION;
11967 #if USE_NEW_PUSH_DELAY
11970 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11971 printf("::: ALERT: %d, %d [%d / %d]\n",
11972 player->push_delay, player->push_delay2,
11973 FrameCounter, FrameCounter / 50);
11976 if (player->push_delay == -1) /* new pushing; restart delay */
11977 player->push_delay = 0;
11979 if (player->push_delay == 0) /* new pushing; restart delay */
11980 player->push_delay = FrameCounter;
11983 #if USE_NEW_PUSH_DELAY
11985 if ( (player->push_delay > 0) != (!xxx_fr) )
11986 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11987 player->push_delay,
11988 xxx_pdv2, player->push_delay2, player->push_delay_value,
11989 FrameCounter, FrameCounter / 50);
11993 if (player->push_delay > 0 &&
11994 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11995 element != EL_SPRING && element != EL_BALLOON)
11998 if (player->push_delay < player->push_delay_value &&
11999 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12000 element != EL_SPRING && element != EL_BALLOON)
12004 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12005 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12006 element != EL_SPRING && element != EL_BALLOON)
12009 /* make sure that there is no move delay before next try to push */
12010 #if USE_NEW_MOVE_DELAY
12011 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12012 player->move_delay = 0;
12014 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12015 player->move_delay = INITIAL_MOVE_DELAY_OFF;
12018 return MF_NO_ACTION;
12022 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12025 if (IS_SB_ELEMENT(element))
12027 if (element == EL_SOKOBAN_FIELD_FULL)
12029 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12030 local_player->sokobanfields_still_needed++;
12033 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12035 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12036 local_player->sokobanfields_still_needed--;
12039 Feld[x][y] = EL_SOKOBAN_OBJECT;
12041 if (Back[x][y] == Back[nextx][nexty])
12042 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12043 else if (Back[x][y] != 0)
12044 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12047 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12050 if (local_player->sokobanfields_still_needed == 0 &&
12051 game.emulation == EMU_SOKOBAN)
12053 player->LevelSolved = player->GameOver = TRUE;
12054 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12058 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12060 InitMovingField(x, y, move_direction);
12061 GfxAction[x][y] = ACTION_PUSHING;
12063 if (mode == DF_SNAP)
12064 ContinueMoving(x, y);
12066 MovPos[x][y] = (dx != 0 ? dx : dy);
12068 Pushed[x][y] = TRUE;
12069 Pushed[nextx][nexty] = TRUE;
12071 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12072 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12074 player->push_delay_value = -1; /* get new value later */
12076 #if USE_PUSH_BUGFIX
12077 /* now: check for element change _after_ element has been pushed! */
12079 if (game.use_change_when_pushing_bug)
12081 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12084 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12085 player->index_bit, dig_side);
12086 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12087 player->index_bit, dig_side);
12093 /* check for element change _after_ element has been pushed! */
12097 /* !!! TEST ONLY !!! */
12098 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12099 player->index_bit, dig_side);
12100 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12101 player->index_bit, dig_side);
12103 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12104 player->index_bit, dig_side);
12105 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12106 player->index_bit, dig_side);
12114 else if (IS_SWITCHABLE(element))
12116 if (PLAYER_SWITCHING(player, x, y))
12118 CheckTriggeredElementChangeByPlayer(x,y, element,
12119 CE_PLAYER_PRESSES_X,
12120 player->index_bit, dig_side);
12125 player->is_switching = TRUE;
12126 player->switch_x = x;
12127 player->switch_y = y;
12129 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12131 if (element == EL_ROBOT_WHEEL)
12133 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12137 DrawLevelField(x, y);
12139 else if (element == EL_SP_TERMINAL)
12143 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12145 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12147 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12148 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12151 else if (IS_BELT_SWITCH(element))
12153 ToggleBeltSwitch(x, y);
12155 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12156 element == EL_SWITCHGATE_SWITCH_DOWN)
12158 ToggleSwitchgateSwitch(x, y);
12160 else if (element == EL_LIGHT_SWITCH ||
12161 element == EL_LIGHT_SWITCH_ACTIVE)
12163 ToggleLightSwitch(x, y);
12166 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12167 SND_LIGHT_SWITCH_ACTIVATING :
12168 SND_LIGHT_SWITCH_DEACTIVATING);
12171 else if (element == EL_TIMEGATE_SWITCH)
12173 ActivateTimegateSwitch(x, y);
12175 else if (element == EL_BALLOON_SWITCH_LEFT ||
12176 element == EL_BALLOON_SWITCH_RIGHT ||
12177 element == EL_BALLOON_SWITCH_UP ||
12178 element == EL_BALLOON_SWITCH_DOWN ||
12179 element == EL_BALLOON_SWITCH_ANY)
12181 if (element == EL_BALLOON_SWITCH_ANY)
12182 game.balloon_dir = move_direction;
12184 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12185 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12186 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12187 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12190 else if (element == EL_LAMP)
12192 Feld[x][y] = EL_LAMP_ACTIVE;
12193 local_player->lights_still_needed--;
12195 ResetGfxAnimation(x, y);
12196 DrawLevelField(x, y);
12198 else if (element == EL_TIME_ORB_FULL)
12200 Feld[x][y] = EL_TIME_ORB_EMPTY;
12202 DrawGameValue_Time(TimeLeft);
12204 ResetGfxAnimation(x, y);
12205 DrawLevelField(x, y);
12208 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12212 CheckTriggeredElementChangeByPlayer(x, y, element,
12214 player->index_bit, dig_side);
12216 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12217 player->index_bit, dig_side);
12223 if (!PLAYER_SWITCHING(player, x, y))
12225 player->is_switching = TRUE;
12226 player->switch_x = x;
12227 player->switch_y = y;
12230 /* !!! TEST ONLY !!! */
12231 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12232 player->index_bit, dig_side);
12233 CheckTriggeredElementChangeByPlayer(x, y, element,
12235 player->index_bit, dig_side);
12237 CheckTriggeredElementChangeByPlayer(x, y, element,
12239 player->index_bit, dig_side);
12240 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12241 player->index_bit, dig_side);
12246 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12247 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12248 player->index_bit, dig_side);
12249 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12250 player->index_bit, dig_side);
12252 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12253 player->index_bit, dig_side);
12254 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12255 player->index_bit, dig_side);
12259 return MF_NO_ACTION;
12262 #if USE_NEW_PUSH_DELAY
12263 player->push_delay = -1;
12265 player->push_delay = 0;
12268 #if USE_PENGUIN_COLLECT_BUGFIX
12269 if (is_player) /* function can also be called by EL_PENGUIN */
12272 if (Feld[x][y] != element) /* really digged/collected something */
12273 player->is_collecting = !player->is_digging;
12279 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12281 int jx = player->jx, jy = player->jy;
12282 int x = jx + dx, y = jy + dy;
12283 int snap_direction = (dx == -1 ? MV_LEFT :
12284 dx == +1 ? MV_RIGHT :
12286 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12289 if (player->MovPos != 0)
12292 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12296 if (!player->active || !IN_LEV_FIELD(x, y))
12304 if (player->MovPos == 0)
12305 player->is_pushing = FALSE;
12307 player->is_snapping = FALSE;
12309 if (player->MovPos == 0)
12311 player->is_moving = FALSE;
12312 player->is_digging = FALSE;
12313 player->is_collecting = FALSE;
12319 if (player->is_snapping)
12322 player->MovDir = snap_direction;
12325 if (player->MovPos == 0)
12328 player->is_moving = FALSE;
12329 player->is_digging = FALSE;
12330 player->is_collecting = FALSE;
12333 player->is_dropping = FALSE;
12335 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12338 player->is_snapping = TRUE;
12341 if (player->MovPos == 0)
12344 player->is_moving = FALSE;
12345 player->is_digging = FALSE;
12346 player->is_collecting = FALSE;
12350 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12351 DrawLevelField(player->last_jx, player->last_jy);
12354 DrawLevelField(x, y);
12363 boolean DropElement(struct PlayerInfo *player)
12365 int old_element, new_element;
12366 int dropx = player->jx, dropy = player->jy;
12367 int drop_direction = player->MovDir;
12369 int drop_side = drop_direction;
12371 static int trigger_sides[4] =
12373 CH_SIDE_LEFT, /* dropping left */
12374 CH_SIDE_RIGHT, /* dropping right */
12375 CH_SIDE_TOP, /* dropping up */
12376 CH_SIDE_BOTTOM, /* dropping down */
12378 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12380 int drop_element = (player->inventory_size > 0 ?
12381 player->inventory_element[player->inventory_size - 1] :
12382 player->inventory_infinite_element != EL_UNDEFINED ?
12383 player->inventory_infinite_element :
12384 player->dynabombs_left > 0 ?
12385 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12388 #if USE_DROP_BUGFIX
12389 /* do not drop an element on top of another element; when holding drop key
12390 pressed without moving, dropped element must move away before the next
12391 element can be dropped (this is especially important if the next element
12392 is dynamite, which can be placed on background for historical reasons) */
12393 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12397 if (IS_THROWABLE(drop_element))
12399 dropx += GET_DX_FROM_DIR(drop_direction);
12400 dropy += GET_DY_FROM_DIR(drop_direction);
12402 if (!IN_LEV_FIELD(dropx, dropy))
12406 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12407 new_element = drop_element; /* default: no change when dropping */
12409 /* check if player is active, not moving and ready to drop */
12410 if (!player->active || player->MovPos || player->drop_delay > 0)
12413 /* check if player has anything that can be dropped */
12415 if (new_element == EL_UNDEFINED)
12418 if (player->inventory_size == 0 &&
12419 player->inventory_infinite_element == EL_UNDEFINED &&
12420 player->dynabombs_left == 0)
12424 /* check if anything can be dropped at the current position */
12425 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12428 /* collected custom elements can only be dropped on empty fields */
12430 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12433 if (player->inventory_size > 0 &&
12434 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12435 && old_element != EL_EMPTY)
12439 if (old_element != EL_EMPTY)
12440 Back[dropx][dropy] = old_element; /* store old element on this field */
12442 ResetGfxAnimation(dropx, dropy);
12443 ResetRandomAnimationValue(dropx, dropy);
12445 if (player->inventory_size > 0 ||
12446 player->inventory_infinite_element != EL_UNDEFINED)
12448 if (player->inventory_size > 0)
12450 player->inventory_size--;
12453 new_element = player->inventory_element[player->inventory_size];
12456 DrawGameValue_Dynamite(local_player->inventory_size);
12458 if (new_element == EL_DYNAMITE)
12459 new_element = EL_DYNAMITE_ACTIVE;
12460 else if (new_element == EL_SP_DISK_RED)
12461 new_element = EL_SP_DISK_RED_ACTIVE;
12464 Feld[dropx][dropy] = new_element;
12466 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12467 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12468 el2img(Feld[dropx][dropy]), 0);
12470 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12473 /* needed if previous element just changed to "empty" in the last frame */
12474 Changed[dropx][dropy] = FALSE; /* allow another change */
12478 /* !!! TEST ONLY !!! */
12479 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12480 player->index_bit, drop_side);
12481 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12483 player->index_bit, drop_side);
12485 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12487 player->index_bit, drop_side);
12488 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12489 player->index_bit, drop_side);
12492 TestIfElementTouchesCustomElement(dropx, dropy);
12494 else /* player is dropping a dyna bomb */
12496 player->dynabombs_left--;
12499 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12502 Feld[dropx][dropy] = new_element;
12504 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12505 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12506 el2img(Feld[dropx][dropy]), 0);
12508 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12515 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12518 InitField_WithBug1(dropx, dropy, FALSE);
12520 InitField(dropx, dropy, FALSE);
12521 if (CAN_MOVE(Feld[dropx][dropy]))
12522 InitMovDir(dropx, dropy);
12526 new_element = Feld[dropx][dropy]; /* element might have changed */
12528 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12529 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12532 int move_stepsize = element_info[new_element].move_stepsize;
12534 int move_direction, nextx, nexty;
12536 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12537 MovDir[dropx][dropy] = drop_direction;
12539 move_direction = MovDir[dropx][dropy];
12540 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12541 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12544 Changed[dropx][dropy] = FALSE; /* allow another change */
12545 CheckCollision[dropx][dropy] = 2;
12548 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12551 WasJustMoving[dropx][dropy] = 3;
12554 InitMovingField(dropx, dropy, move_direction);
12555 ContinueMoving(dropx, dropy);
12560 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12563 Changed[dropx][dropy] = FALSE; /* allow another change */
12566 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12568 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12569 CE_HITTING_SOMETHING, move_direction);
12577 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12582 player->drop_delay = 8 + 8 + 8;
12586 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12591 player->is_dropping = TRUE;
12593 #if USE_DROP_BUGFIX
12594 player->drop_x = dropx;
12595 player->drop_y = dropy;
12601 /* ------------------------------------------------------------------------- */
12602 /* game sound playing functions */
12603 /* ------------------------------------------------------------------------- */
12605 static int *loop_sound_frame = NULL;
12606 static int *loop_sound_volume = NULL;
12608 void InitPlayLevelSound()
12610 int num_sounds = getSoundListSize();
12612 checked_free(loop_sound_frame);
12613 checked_free(loop_sound_volume);
12615 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12616 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12619 static void PlayLevelSound(int x, int y, int nr)
12621 int sx = SCREENX(x), sy = SCREENY(y);
12622 int volume, stereo_position;
12623 int max_distance = 8;
12624 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12626 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12627 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12630 if (!IN_LEV_FIELD(x, y) ||
12631 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12632 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12635 volume = SOUND_MAX_VOLUME;
12637 if (!IN_SCR_FIELD(sx, sy))
12639 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12640 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12642 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12645 stereo_position = (SOUND_MAX_LEFT +
12646 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12647 (SCR_FIELDX + 2 * max_distance));
12649 if (IS_LOOP_SOUND(nr))
12651 /* This assures that quieter loop sounds do not overwrite louder ones,
12652 while restarting sound volume comparison with each new game frame. */
12654 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12657 loop_sound_volume[nr] = volume;
12658 loop_sound_frame[nr] = FrameCounter;
12661 PlaySoundExt(nr, volume, stereo_position, type);
12664 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12666 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12667 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12668 y < LEVELY(BY1) ? LEVELY(BY1) :
12669 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12673 static void PlayLevelSoundAction(int x, int y, int action)
12675 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12678 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12680 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12682 if (sound_effect != SND_UNDEFINED)
12683 PlayLevelSound(x, y, sound_effect);
12686 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12689 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12691 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12692 PlayLevelSound(x, y, sound_effect);
12695 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12697 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12699 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12700 PlayLevelSound(x, y, sound_effect);
12703 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12705 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12707 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12708 StopSound(sound_effect);
12711 static void PlayLevelMusic()
12713 if (levelset.music[level_nr] != MUS_UNDEFINED)
12714 PlayMusic(levelset.music[level_nr]); /* from config file */
12716 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12719 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12721 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12724 if (sample == SAMPLE_bug)
12725 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12731 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12735 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12739 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12743 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12747 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12751 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12755 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12758 case SAMPLE_android_clone:
12759 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12762 case SAMPLE_android_move:
12763 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12766 case SAMPLE_spring:
12767 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12771 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12775 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12778 case SAMPLE_eater_eat:
12779 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12783 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12786 case SAMPLE_collect:
12787 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12790 case SAMPLE_diamond:
12791 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12794 case SAMPLE_squash:
12795 /* !!! CHECK THIS !!! */
12797 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12799 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12803 case SAMPLE_wonderfall:
12804 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12808 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12812 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12816 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12820 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12824 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12828 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12831 case SAMPLE_wonder:
12832 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12836 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12839 case SAMPLE_exit_open:
12840 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12843 case SAMPLE_exit_leave:
12844 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12847 case SAMPLE_dynamite:
12848 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12852 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12856 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12860 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12864 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12868 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12872 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12876 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12881 void RaiseScore(int value)
12883 local_player->score += value;
12885 DrawGameValue_Score(local_player->score);
12888 void RaiseScoreElement(int element)
12893 case EL_BD_DIAMOND:
12894 case EL_EMERALD_YELLOW:
12895 case EL_EMERALD_RED:
12896 case EL_EMERALD_PURPLE:
12897 case EL_SP_INFOTRON:
12898 RaiseScore(level.score[SC_EMERALD]);
12901 RaiseScore(level.score[SC_DIAMOND]);
12904 RaiseScore(level.score[SC_CRYSTAL]);
12907 RaiseScore(level.score[SC_PEARL]);
12910 case EL_BD_BUTTERFLY:
12911 case EL_SP_ELECTRON:
12912 RaiseScore(level.score[SC_BUG]);
12915 case EL_BD_FIREFLY:
12916 case EL_SP_SNIKSNAK:
12917 RaiseScore(level.score[SC_SPACESHIP]);
12920 case EL_DARK_YAMYAM:
12921 RaiseScore(level.score[SC_YAMYAM]);
12924 RaiseScore(level.score[SC_ROBOT]);
12927 RaiseScore(level.score[SC_PACMAN]);
12930 RaiseScore(level.score[SC_NUT]);
12933 case EL_SP_DISK_RED:
12934 case EL_DYNABOMB_INCREASE_NUMBER:
12935 case EL_DYNABOMB_INCREASE_SIZE:
12936 case EL_DYNABOMB_INCREASE_POWER:
12937 RaiseScore(level.score[SC_DYNAMITE]);
12939 case EL_SHIELD_NORMAL:
12940 case EL_SHIELD_DEADLY:
12941 RaiseScore(level.score[SC_SHIELD]);
12943 case EL_EXTRA_TIME:
12944 RaiseScore(level.score[SC_TIME_BONUS]);
12958 RaiseScore(level.score[SC_KEY]);
12961 RaiseScore(element_info[element].collect_score);
12966 void RequestQuitGame(boolean ask_if_really_quit)
12968 if (AllPlayersGone ||
12969 !ask_if_really_quit ||
12970 level_editor_test_game ||
12971 Request("Do you really want to quit the game ?",
12972 REQ_ASK | REQ_STAY_CLOSED))
12974 #if defined(NETWORK_AVALIABLE)
12975 if (options.network)
12976 SendToServer_StopPlaying();
12980 game_status = GAME_MODE_MAIN;
12988 if (tape.playing && tape.deactivate_display)
12989 TapeDeactivateDisplayOff(TRUE);
12992 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12995 if (tape.playing && tape.deactivate_display)
12996 TapeDeactivateDisplayOn();
13003 /* ---------- new game button stuff ---------------------------------------- */
13005 /* graphic position values for game buttons */
13006 #define GAME_BUTTON_XSIZE 30
13007 #define GAME_BUTTON_YSIZE 30
13008 #define GAME_BUTTON_XPOS 5
13009 #define GAME_BUTTON_YPOS 215
13010 #define SOUND_BUTTON_XPOS 5
13011 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13013 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13014 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13015 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13016 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13017 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13018 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13025 } gamebutton_info[NUM_GAME_BUTTONS] =
13028 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13033 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13034 GAME_CTRL_ID_PAUSE,
13038 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13043 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13044 SOUND_CTRL_ID_MUSIC,
13045 "background music on/off"
13048 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13049 SOUND_CTRL_ID_LOOPS,
13050 "sound loops on/off"
13053 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13054 SOUND_CTRL_ID_SIMPLE,
13055 "normal sounds on/off"
13059 void CreateGameButtons()
13063 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13065 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13066 struct GadgetInfo *gi;
13069 unsigned long event_mask;
13070 int gd_xoffset, gd_yoffset;
13071 int gd_x1, gd_x2, gd_y1, gd_y2;
13074 gd_xoffset = gamebutton_info[i].x;
13075 gd_yoffset = gamebutton_info[i].y;
13076 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13077 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13079 if (id == GAME_CTRL_ID_STOP ||
13080 id == GAME_CTRL_ID_PAUSE ||
13081 id == GAME_CTRL_ID_PLAY)
13083 button_type = GD_TYPE_NORMAL_BUTTON;
13085 event_mask = GD_EVENT_RELEASED;
13086 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13087 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13091 button_type = GD_TYPE_CHECK_BUTTON;
13093 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13094 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13095 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13096 event_mask = GD_EVENT_PRESSED;
13097 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13098 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13101 gi = CreateGadget(GDI_CUSTOM_ID, id,
13102 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13103 GDI_X, DX + gd_xoffset,
13104 GDI_Y, DY + gd_yoffset,
13105 GDI_WIDTH, GAME_BUTTON_XSIZE,
13106 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13107 GDI_TYPE, button_type,
13108 GDI_STATE, GD_BUTTON_UNPRESSED,
13109 GDI_CHECKED, checked,
13110 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13111 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13112 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13113 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13114 GDI_EVENT_MASK, event_mask,
13115 GDI_CALLBACK_ACTION, HandleGameButtons,
13119 Error(ERR_EXIT, "cannot create gadget");
13121 game_gadget[id] = gi;
13125 void FreeGameButtons()
13129 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13130 FreeGadget(game_gadget[i]);
13133 static void MapGameButtons()
13137 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13138 MapGadget(game_gadget[i]);
13141 void UnmapGameButtons()
13145 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13146 UnmapGadget(game_gadget[i]);
13149 static void HandleGameButtons(struct GadgetInfo *gi)
13151 int id = gi->custom_id;
13153 if (game_status != GAME_MODE_PLAYING)
13158 case GAME_CTRL_ID_STOP:
13159 RequestQuitGame(TRUE);
13162 case GAME_CTRL_ID_PAUSE:
13163 if (options.network)
13165 #if defined(NETWORK_AVALIABLE)
13167 SendToServer_ContinuePlaying();
13169 SendToServer_PausePlaying();
13173 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13176 case GAME_CTRL_ID_PLAY:
13179 #if defined(NETWORK_AVALIABLE)
13180 if (options.network)
13181 SendToServer_ContinuePlaying();
13185 tape.pausing = FALSE;
13186 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13191 case SOUND_CTRL_ID_MUSIC:
13192 if (setup.sound_music)
13194 setup.sound_music = FALSE;
13197 else if (audio.music_available)
13199 setup.sound = setup.sound_music = TRUE;
13201 SetAudioMode(setup.sound);
13207 case SOUND_CTRL_ID_LOOPS:
13208 if (setup.sound_loops)
13209 setup.sound_loops = FALSE;
13210 else if (audio.loops_available)
13212 setup.sound = setup.sound_loops = TRUE;
13213 SetAudioMode(setup.sound);
13217 case SOUND_CTRL_ID_SIMPLE:
13218 if (setup.sound_simple)
13219 setup.sound_simple = FALSE;
13220 else if (audio.sound_available)
13222 setup.sound = setup.sound_simple = TRUE;
13223 SetAudioMode(setup.sound);