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_BUG (TRUE * USE_NEW_STUFF * 1)
58 /* for MovePlayer() */
59 #define MF_NO_ACTION 0
63 /* for ScrollPlayer() */
65 #define SCROLL_GO_ON 1
68 #define EX_PHASE_START 0
69 #define EX_TYPE_NONE 0
70 #define EX_TYPE_NORMAL (1 << 0)
71 #define EX_TYPE_CENTER (1 << 1)
72 #define EX_TYPE_BORDER (1 << 2)
73 #define EX_TYPE_CROSS (1 << 3)
74 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
76 /* special positions in the game control window (relative to control window) */
79 #define XX_EMERALDS 29
80 #define YY_EMERALDS 54
81 #define XX_DYNAMITE 29
82 #define YY_DYNAMITE 89
91 /* special positions in the game control window (relative to main window) */
92 #define DX_LEVEL (DX + XX_LEVEL)
93 #define DY_LEVEL (DY + YY_LEVEL)
94 #define DX_EMERALDS (DX + XX_EMERALDS)
95 #define DY_EMERALDS (DY + YY_EMERALDS)
96 #define DX_DYNAMITE (DX + XX_DYNAMITE)
97 #define DY_DYNAMITE (DY + YY_DYNAMITE)
98 #define DX_KEYS (DX + XX_KEYS)
99 #define DY_KEYS (DY + YY_KEYS)
100 #define DX_SCORE (DX + XX_SCORE)
101 #define DY_SCORE (DY + YY_SCORE)
102 #define DX_TIME1 (DX + XX_TIME1)
103 #define DX_TIME2 (DX + XX_TIME2)
104 #define DY_TIME (DY + YY_TIME)
106 /* values for initial player move delay (initial delay counter value) */
107 #define INITIAL_MOVE_DELAY_OFF -1
108 #define INITIAL_MOVE_DELAY_ON 0
110 /* values for player movement speed (which is in fact a delay value) */
111 #define MOVE_DELAY_NORMAL_SPEED 8
112 #define MOVE_DELAY_HIGH_SPEED 4
114 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
115 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
116 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
117 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
119 /* values for other actions */
120 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
122 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
123 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
125 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
127 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
128 RND(element_info[e].push_delay_random))
129 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
130 RND(element_info[e].drop_delay_random))
131 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
132 RND(element_info[e].move_delay_random))
133 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
134 (element_info[e].move_delay_random))
136 #define GET_TARGET_ELEMENT(e, ch) \
137 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
138 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
140 #define GET_VALID_PLAYER_ELEMENT(e) \
141 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
143 #define CAN_GROW_INTO(e) \
144 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
146 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
147 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
150 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
151 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 (CAN_MOVE_INTO_ACID(e) && \
153 Feld[x][y] == EL_ACID) || \
156 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
157 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
158 (CAN_MOVE_INTO_ACID(e) && \
159 Feld[x][y] == EL_ACID) || \
162 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
163 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 (CAN_MOVE_INTO_ACID(e) && \
166 Feld[x][y] == EL_ACID) || \
167 (DONT_COLLIDE_WITH(e) && \
169 !PLAYER_ENEMY_PROTECTED(x, y))))
172 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
175 (DONT_COLLIDE_WITH(e) && \
177 !PLAYER_ENEMY_PROTECTED(x, y))))
180 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
181 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
184 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
187 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
192 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
195 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
200 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
203 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
206 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
209 #define PIG_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
212 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
214 IS_FOOD_PENGUIN(Feld[x][y])))
215 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
218 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
221 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
226 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
227 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
228 (CAN_MOVE_INTO_ACID(e) && \
229 Feld[x][y] == EL_ACID) || \
230 Feld[x][y] == EL_DIAMOND))
232 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
233 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
234 (CAN_MOVE_INTO_ACID(e) && \
235 Feld[x][y] == EL_ACID) || \
236 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
238 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
239 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
240 (CAN_MOVE_INTO_ACID(e) && \
241 Feld[x][y] == EL_ACID) || \
242 IS_AMOEBOID(Feld[x][y])))
244 #define PIG_CAN_ENTER_FIELD(e, x, y) \
245 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
246 (CAN_MOVE_INTO_ACID(e) && \
247 Feld[x][y] == EL_ACID) || \
248 IS_FOOD_PIG(Feld[x][y])))
250 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
251 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
252 (CAN_MOVE_INTO_ACID(e) && \
253 Feld[x][y] == EL_ACID) || \
254 IS_FOOD_PENGUIN(Feld[x][y]) || \
255 Feld[x][y] == EL_EXIT_OPEN))
257 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
258 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
259 (CAN_MOVE_INTO_ACID(e) && \
260 Feld[x][y] == EL_ACID)))
262 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
263 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
264 (CAN_MOVE_INTO_ACID(e) && \
265 Feld[x][y] == EL_ACID) || \
268 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
269 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
270 (CAN_MOVE_INTO_ACID(e) && \
271 Feld[x][y] == EL_ACID)))
275 #define GROUP_NR(e) ((e) - EL_GROUP_START)
276 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
277 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
278 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
280 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
281 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
284 #define CE_ENTER_FIELD_COND(e, x, y) \
285 (!IS_PLAYER(x, y) && \
286 (Feld[x][y] == EL_ACID || \
287 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
289 #define CE_ENTER_FIELD_COND(e, x, y) \
290 (!IS_PLAYER(x, y) && \
291 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
294 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
295 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
297 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
298 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
300 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
301 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
302 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
303 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
305 /* game button identifiers */
306 #define GAME_CTRL_ID_STOP 0
307 #define GAME_CTRL_ID_PAUSE 1
308 #define GAME_CTRL_ID_PLAY 2
309 #define SOUND_CTRL_ID_MUSIC 3
310 #define SOUND_CTRL_ID_LOOPS 4
311 #define SOUND_CTRL_ID_SIMPLE 5
313 #define NUM_GAME_BUTTONS 6
316 /* forward declaration for internal use */
318 static void AdvanceFrameAndPlayerCounters(int);
320 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
321 static boolean MovePlayer(struct PlayerInfo *, int, int);
322 static void ScrollPlayer(struct PlayerInfo *, int);
323 static void ScrollScreen(struct PlayerInfo *, int);
325 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
327 static void InitBeltMovement(void);
328 static void CloseAllOpenTimegates(void);
329 static void CheckGravityMovement(struct PlayerInfo *);
330 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
331 static void KillHeroUnlessEnemyProtected(int, int);
332 static void KillHeroUnlessExplosionProtected(int, int);
334 static void TestIfPlayerTouchesCustomElement(int, int);
335 static void TestIfElementTouchesCustomElement(int, int);
336 static void TestIfElementHitsCustomElement(int, int, int);
338 static void TestIfElementSmashesCustomElement(int, int, int);
341 static void ChangeElement(int, int, int);
343 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
344 #define CheckTriggeredElementChange(x, y, e, ev) \
345 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
347 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
348 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
349 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
350 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
351 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
352 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
355 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
356 #define CheckElementChange(x, y, e, te, ev) \
357 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
358 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
359 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
360 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
361 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
362 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
363 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
365 static void PlayLevelSound(int, int, int);
366 static void PlayLevelSoundNearest(int, int, int);
367 static void PlayLevelSoundAction(int, int, int);
368 static void PlayLevelSoundElementAction(int, int, int, int);
369 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
370 static void PlayLevelSoundActionIfLoop(int, int, int);
371 static void StopLevelSoundActionIfLoop(int, int, int);
372 static void PlayLevelMusic();
374 static void MapGameButtons();
375 static void HandleGameButtons(struct GadgetInfo *);
377 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
380 /* ------------------------------------------------------------------------- */
381 /* definition of elements that automatically change to other elements after */
382 /* a specified time, eventually calling a function when changing */
383 /* ------------------------------------------------------------------------- */
385 /* forward declaration for changer functions */
386 static void InitBuggyBase(int x, int y);
387 static void WarnBuggyBase(int x, int y);
389 static void InitTrap(int x, int y);
390 static void ActivateTrap(int x, int y);
391 static void ChangeActiveTrap(int x, int y);
393 static void InitRobotWheel(int x, int y);
394 static void RunRobotWheel(int x, int y);
395 static void StopRobotWheel(int x, int y);
397 static void InitTimegateWheel(int x, int y);
398 static void RunTimegateWheel(int x, int y);
400 struct ChangingElementInfo
405 void (*pre_change_function)(int x, int y);
406 void (*change_function)(int x, int y);
407 void (*post_change_function)(int x, int y);
410 static struct ChangingElementInfo change_delay_list[] =
461 EL_SWITCHGATE_OPENING,
469 EL_SWITCHGATE_CLOSING,
470 EL_SWITCHGATE_CLOSED,
502 EL_ACID_SPLASH_RIGHT,
511 EL_SP_BUGGY_BASE_ACTIVATING,
518 EL_SP_BUGGY_BASE_ACTIVATING,
519 EL_SP_BUGGY_BASE_ACTIVE,
526 EL_SP_BUGGY_BASE_ACTIVE,
550 EL_ROBOT_WHEEL_ACTIVE,
558 EL_TIMEGATE_SWITCH_ACTIVE,
579 int push_delay_fixed, push_delay_random;
584 { EL_BALLOON, 0, 0 },
586 { EL_SOKOBAN_OBJECT, 2, 0 },
587 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
588 { EL_SATELLITE, 2, 0 },
589 { EL_SP_DISK_YELLOW, 2, 0 },
591 { EL_UNDEFINED, 0, 0 },
599 move_stepsize_list[] =
601 { EL_AMOEBA_DROP, 2 },
602 { EL_AMOEBA_DROPPING, 2 },
603 { EL_QUICKSAND_FILLING, 1 },
604 { EL_QUICKSAND_EMPTYING, 1 },
605 { EL_MAGIC_WALL_FILLING, 2 },
606 { EL_BD_MAGIC_WALL_FILLING, 2 },
607 { EL_MAGIC_WALL_EMPTYING, 2 },
608 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
618 collect_count_list[] =
621 { EL_BD_DIAMOND, 1 },
622 { EL_EMERALD_YELLOW, 1 },
623 { EL_EMERALD_RED, 1 },
624 { EL_EMERALD_PURPLE, 1 },
626 { EL_SP_INFOTRON, 1 },
638 access_direction_list[] =
640 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
641 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
642 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
643 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
644 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
645 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
646 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
647 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
648 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
649 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
650 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
652 { EL_SP_PORT_LEFT, MV_RIGHT },
653 { EL_SP_PORT_RIGHT, MV_LEFT },
654 { EL_SP_PORT_UP, MV_DOWN },
655 { EL_SP_PORT_DOWN, MV_UP },
656 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
657 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
658 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
659 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
660 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
661 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
662 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
663 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
664 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
665 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
666 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
667 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
668 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
669 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
670 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
672 { EL_UNDEFINED, MV_NO_MOVING }
675 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
677 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
678 CH_EVENT_BIT(CE_DELAY))
679 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
680 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
681 IS_JUST_CHANGING(x, y))
683 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
686 void GetPlayerConfig()
688 if (!audio.sound_available)
689 setup.sound_simple = FALSE;
691 if (!audio.loops_available)
692 setup.sound_loops = FALSE;
694 if (!audio.music_available)
695 setup.sound_music = FALSE;
697 if (!video.fullscreen_available)
698 setup.fullscreen = FALSE;
700 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
702 SetAudioMode(setup.sound);
706 static int getBeltNrFromBeltElement(int element)
708 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
709 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
710 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
713 static int getBeltNrFromBeltActiveElement(int element)
715 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
716 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
717 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
720 static int getBeltNrFromBeltSwitchElement(int element)
722 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
723 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
724 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
727 static int getBeltDirNrFromBeltSwitchElement(int element)
729 static int belt_base_element[4] =
731 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
732 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
733 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
734 EL_CONVEYOR_BELT_4_SWITCH_LEFT
737 int belt_nr = getBeltNrFromBeltSwitchElement(element);
738 int belt_dir_nr = element - belt_base_element[belt_nr];
740 return (belt_dir_nr % 3);
743 static int getBeltDirFromBeltSwitchElement(int element)
745 static int belt_move_dir[3] =
752 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
754 return belt_move_dir[belt_dir_nr];
757 static void InitPlayerField(int x, int y, int element, boolean init_game)
759 if (element == EL_SP_MURPHY)
763 if (stored_player[0].present)
765 Feld[x][y] = EL_SP_MURPHY_CLONE;
771 stored_player[0].use_murphy_graphic = TRUE;
774 Feld[x][y] = EL_PLAYER_1;
780 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
781 int jx = player->jx, jy = player->jy;
783 player->present = TRUE;
785 player->block_last_field = (element == EL_SP_MURPHY ?
786 level.sp_block_last_field :
787 level.block_last_field);
789 #if USE_NEW_BLOCK_STYLE
792 /* ---------- initialize player's last field block delay --------------- */
794 /* always start with reliable default value (no adjustment needed) */
795 player->block_delay_adjustment = 0;
797 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
798 if (player->block_last_field && element == EL_SP_MURPHY)
799 player->block_delay_adjustment = 1;
801 /* special case 2: in game engines before 3.1.1, blocking was different */
802 if (game.use_block_last_field_bug)
803 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
806 /* blocking the last field when moving was corrected in version 3.1.1 */
807 if (game.use_block_last_field_bug)
809 /* even "not blocking" was blocking the last field for one frame */
810 level.block_delay = (level.block_last_field ? 7 : 1);
811 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
813 level.block_last_field = TRUE;
814 level.sp_block_last_field = TRUE;
818 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
819 level.block_delay = 8; /* when blocking, block 8 frames */
820 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
824 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
830 player->block_delay = (player->block_last_field ?
831 (element == EL_SP_MURPHY ?
832 level.sp_block_delay :
833 level.block_delay) : 0);
835 player->block_delay = (element == EL_SP_MURPHY ?
836 (player->block_last_field ? 7 : 1) :
837 (player->block_last_field ? 7 : 1));
843 printf("::: block_last_field == %d, block_delay = %d\n",
844 player->block_last_field, player->block_delay);
848 if (!options.network || player->connected)
850 player->active = TRUE;
852 /* remove potentially duplicate players */
853 if (StorePlayer[jx][jy] == Feld[x][y])
854 StorePlayer[jx][jy] = 0;
856 StorePlayer[x][y] = Feld[x][y];
860 printf("Player %d activated.\n", player->element_nr);
861 printf("[Local player is %d and currently %s.]\n",
862 local_player->element_nr,
863 local_player->active ? "active" : "not active");
867 Feld[x][y] = EL_EMPTY;
869 player->jx = player->last_jx = x;
870 player->jy = player->last_jy = y;
874 static void InitField(int x, int y, boolean init_game)
876 int element = Feld[x][y];
885 InitPlayerField(x, y, element, init_game);
888 case EL_SOKOBAN_FIELD_PLAYER:
889 element = Feld[x][y] = EL_PLAYER_1;
890 InitField(x, y, init_game);
892 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
893 InitField(x, y, init_game);
896 case EL_SOKOBAN_FIELD_EMPTY:
897 local_player->sokobanfields_still_needed++;
901 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
902 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
903 else if (x > 0 && Feld[x-1][y] == EL_ACID)
904 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
905 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
906 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
907 else if (y > 0 && Feld[x][y-1] == EL_ACID)
908 Feld[x][y] = EL_ACID_POOL_BOTTOM;
909 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
910 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
918 case EL_SPACESHIP_RIGHT:
919 case EL_SPACESHIP_UP:
920 case EL_SPACESHIP_LEFT:
921 case EL_SPACESHIP_DOWN:
923 case EL_BD_BUTTERFLY_RIGHT:
924 case EL_BD_BUTTERFLY_UP:
925 case EL_BD_BUTTERFLY_LEFT:
926 case EL_BD_BUTTERFLY_DOWN:
927 case EL_BD_BUTTERFLY:
928 case EL_BD_FIREFLY_RIGHT:
929 case EL_BD_FIREFLY_UP:
930 case EL_BD_FIREFLY_LEFT:
931 case EL_BD_FIREFLY_DOWN:
933 case EL_PACMAN_RIGHT:
957 if (y == lev_fieldy - 1)
959 Feld[x][y] = EL_AMOEBA_GROWING;
960 Store[x][y] = EL_AMOEBA_WET;
964 case EL_DYNAMITE_ACTIVE:
965 case EL_SP_DISK_RED_ACTIVE:
966 case EL_DYNABOMB_PLAYER_1_ACTIVE:
967 case EL_DYNABOMB_PLAYER_2_ACTIVE:
968 case EL_DYNABOMB_PLAYER_3_ACTIVE:
969 case EL_DYNABOMB_PLAYER_4_ACTIVE:
974 local_player->lights_still_needed++;
978 local_player->friends_still_needed++;
983 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
988 Feld[x][y] = EL_EMPTY;
993 case EL_EM_KEY_1_FILE:
994 Feld[x][y] = EL_EM_KEY_1;
996 case EL_EM_KEY_2_FILE:
997 Feld[x][y] = EL_EM_KEY_2;
999 case EL_EM_KEY_3_FILE:
1000 Feld[x][y] = EL_EM_KEY_3;
1002 case EL_EM_KEY_4_FILE:
1003 Feld[x][y] = EL_EM_KEY_4;
1007 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1008 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1009 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1010 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1011 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1012 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1013 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1014 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1015 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1016 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1017 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1018 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1021 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1022 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1023 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1025 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1027 game.belt_dir[belt_nr] = belt_dir;
1028 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1030 else /* more than one switch -- set it like the first switch */
1032 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1037 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1039 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1042 case EL_LIGHT_SWITCH_ACTIVE:
1044 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1048 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1050 else if (IS_GROUP_ELEMENT(element))
1052 struct ElementGroupInfo *group = element_info[element].group;
1053 int last_anim_random_frame = gfx.anim_random_frame;
1056 if (group->choice_mode == ANIM_RANDOM)
1057 gfx.anim_random_frame = RND(group->num_elements_resolved);
1059 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1060 group->choice_mode, 0,
1063 if (group->choice_mode == ANIM_RANDOM)
1064 gfx.anim_random_frame = last_anim_random_frame;
1066 group->choice_pos++;
1068 Feld[x][y] = group->element_resolved[element_pos];
1070 InitField(x, y, init_game);
1076 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1078 InitField(x, y, init_game);
1080 /* not needed to call InitMovDir() -- already done by InitField()! */
1081 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1082 CAN_MOVE(Feld[x][y]))
1086 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1088 int old_element = Feld[x][y];
1090 InitField(x, y, init_game);
1092 /* not needed to call InitMovDir() -- already done by InitField()! */
1093 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1094 CAN_MOVE(old_element) &&
1095 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1098 /* this case is in fact a combination of not less than three bugs:
1099 first, it calls InitMovDir() for elements that can move, although this is
1100 already done by InitField(); then, it checks the element that was at this
1101 field _before_ the call to InitField() (which can change it); lastly, it
1102 was not called for "mole with direction" elements, which were treated as
1103 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1107 inline void DrawGameValue_Emeralds(int value)
1109 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1112 inline void DrawGameValue_Dynamite(int value)
1114 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1117 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1121 /* currently only 4 of 8 possible keys are displayed */
1122 for (i = 0; i < STD_NUM_KEYS; i++)
1124 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1125 el2edimg(EL_KEY_1 + i));
1128 inline void DrawGameValue_Score(int value)
1130 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1133 inline void DrawGameValue_Time(int value)
1136 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1138 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1141 inline void DrawGameValue_Level(int value)
1144 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1147 /* misuse area for displaying emeralds to draw bigger level number */
1148 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1149 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1151 /* now copy it to the area for displaying level number */
1152 BlitBitmap(drawto, drawto,
1153 DX_EMERALDS, DY_EMERALDS + 1,
1154 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1155 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1156 DX_LEVEL - 1, DY_LEVEL + 1);
1158 /* restore the area for displaying emeralds */
1159 DrawGameValue_Emeralds(local_player->gems_still_needed);
1161 /* yes, this is all really ugly :-) */
1165 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1168 int key[MAX_NUM_KEYS];
1171 for (i = 0; i < MAX_NUM_KEYS; i++)
1172 key[i] = key_bits & (1 << i);
1174 DrawGameValue_Level(level_nr);
1176 DrawGameValue_Emeralds(emeralds);
1177 DrawGameValue_Dynamite(dynamite);
1178 DrawGameValue_Score(score);
1179 DrawGameValue_Time(time);
1181 DrawGameValue_Keys(key);
1184 void DrawGameDoorValues()
1188 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1190 DrawGameDoorValues_EM();
1195 DrawGameValue_Level(level_nr);
1197 DrawGameValue_Emeralds(local_player->gems_still_needed);
1198 DrawGameValue_Dynamite(local_player->inventory_size);
1199 DrawGameValue_Score(local_player->score);
1200 DrawGameValue_Time(TimeLeft);
1202 for (i = 0; i < MAX_PLAYERS; i++)
1203 DrawGameValue_Keys(stored_player[i].key);
1206 static void resolve_group_element(int group_element, int recursion_depth)
1208 static int group_nr;
1209 static struct ElementGroupInfo *group;
1210 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1213 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1215 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1216 group_element - EL_GROUP_START + 1);
1218 /* replace element which caused too deep recursion by question mark */
1219 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1224 if (recursion_depth == 0) /* initialization */
1226 group = element_info[group_element].group;
1227 group_nr = group_element - EL_GROUP_START;
1229 group->num_elements_resolved = 0;
1230 group->choice_pos = 0;
1233 for (i = 0; i < actual_group->num_elements; i++)
1235 int element = actual_group->element[i];
1237 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1240 if (IS_GROUP_ELEMENT(element))
1241 resolve_group_element(element, recursion_depth + 1);
1244 group->element_resolved[group->num_elements_resolved++] = element;
1245 element_info[element].in_group[group_nr] = TRUE;
1250 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1252 printf("::: group %d: %d resolved elements\n",
1253 group_element - EL_GROUP_START, group->num_elements_resolved);
1254 for (i = 0; i < group->num_elements_resolved; i++)
1255 printf("::: - %d ['%s']\n", group->element_resolved[i],
1256 element_info[group->element_resolved[i]].token_name);
1263 =============================================================================
1265 -----------------------------------------------------------------------------
1266 initialize game engine due to level / tape version number
1267 =============================================================================
1270 static void InitGameEngine()
1274 /* set game engine from tape file when re-playing, else from level file */
1275 game.engine_version = (tape.playing ? tape.engine_version :
1276 level.game_version);
1278 /* ---------------------------------------------------------------------- */
1279 /* set flags for bugs and changes according to active game engine version */
1280 /* ---------------------------------------------------------------------- */
1283 Summary of bugfix/change:
1284 Fixed handling for custom elements that change when pushed by the player.
1286 Fixed/changed in version:
1290 Before 3.1.0, custom elements that "change when pushing" changed directly
1291 after the player started pushing them (until then handled in "DigField()").
1292 Since 3.1.0, these custom elements are not changed until the "pushing"
1293 move of the element is finished (now handled in "ContinueMoving()").
1295 Affected levels/tapes:
1296 The first condition is generally needed for all levels/tapes before version
1297 3.1.0, which might use the old behaviour before it was changed; known tapes
1298 that are affected are some tapes from the level set "Walpurgis Gardens" by
1300 The second condition is an exception from the above case and is needed for
1301 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1302 above (including some development versions of 3.1.0), but before it was
1303 known that this change would break tapes like the above and was fixed in
1304 3.1.1, so that the changed behaviour was active although the engine version
1305 while recording maybe was before 3.1.0. There is at least one tape that is
1306 affected by this exception, which is the tape for the one-level set "Bug
1307 Machine" by Juergen Bonhagen.
1310 game.use_change_when_pushing_bug =
1311 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1313 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1314 tape.game_version < VERSION_IDENT(3,1,1,0)));
1317 Summary of bugfix/change:
1318 Fixed handling for blocking the field the player leaves when moving.
1320 Fixed/changed in version:
1324 Before 3.1.1, when "block last field when moving" was enabled, the field
1325 the player is leaving when moving was blocked for the time of the move,
1326 and was directly unblocked afterwards. This resulted in the last field
1327 being blocked for exactly one less than the number of frames of one player
1328 move. Additionally, even when blocking was disabled, the last field was
1329 blocked for exactly one frame.
1330 Since 3.1.1, due to changes in player movement handling, the last field
1331 is not blocked at all when blocking is disabled. When blocking is enabled,
1332 the last field is blocked for exactly the number of frames of one player
1333 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1334 last field is blocked for exactly one more than the number of frames of
1337 Affected levels/tapes:
1338 (!!! yet to be determined -- probably many !!!)
1341 game.use_block_last_field_bug =
1342 (game.engine_version < VERSION_IDENT(3,1,1,0));
1344 /* ---------------------------------------------------------------------- */
1346 /* dynamically adjust element properties according to game engine version */
1347 InitElementPropertiesEngine(game.engine_version);
1350 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1351 printf(" tape version == %06d [%s] [file: %06d]\n",
1352 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1354 printf(" => game.engine_version == %06d\n", game.engine_version);
1357 /* ---------- recursively resolve group elements ------------------------- */
1359 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1360 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1361 element_info[i].in_group[j] = FALSE;
1363 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1364 resolve_group_element(EL_GROUP_START + i, 0);
1366 /* ---------- initialize player's initial move delay --------------------- */
1368 #if USE_NEW_MOVE_DELAY
1369 /* dynamically adjust player properties according to level information */
1370 game.initial_move_delay_value =
1371 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1373 /* dynamically adjust player properties according to game engine version */
1374 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1375 game.initial_move_delay_value : 0);
1377 /* dynamically adjust player properties according to game engine version */
1378 game.initial_move_delay =
1379 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1380 INITIAL_MOVE_DELAY_OFF);
1382 /* dynamically adjust player properties according to level information */
1383 game.initial_move_delay_value =
1384 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1387 /* ---------- initialize player's initial push delay --------------------- */
1389 /* dynamically adjust player properties according to game engine version */
1390 game.initial_push_delay_value =
1391 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1393 /* ---------- initialize changing elements ------------------------------- */
1395 /* initialize changing elements information */
1396 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1398 struct ElementInfo *ei = &element_info[i];
1400 /* this pointer might have been changed in the level editor */
1401 ei->change = &ei->change_page[0];
1403 if (!IS_CUSTOM_ELEMENT(i))
1405 ei->change->target_element = EL_EMPTY_SPACE;
1406 ei->change->delay_fixed = 0;
1407 ei->change->delay_random = 0;
1408 ei->change->delay_frames = 1;
1411 ei->change_events = CE_BITMASK_DEFAULT;
1412 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1414 ei->event_page_nr[j] = 0;
1415 ei->event_page[j] = &ei->change_page[0];
1419 /* add changing elements from pre-defined list */
1420 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1422 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1423 struct ElementInfo *ei = &element_info[ch_delay->element];
1425 ei->change->target_element = ch_delay->target_element;
1426 ei->change->delay_fixed = ch_delay->change_delay;
1428 ei->change->pre_change_function = ch_delay->pre_change_function;
1429 ei->change->change_function = ch_delay->change_function;
1430 ei->change->post_change_function = ch_delay->post_change_function;
1432 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1435 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1440 /* add change events from custom element configuration */
1441 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1443 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1445 for (j = 0; j < ei->num_change_pages; j++)
1447 if (!ei->change_page[j].can_change)
1450 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1452 /* only add event page for the first page found with this event */
1453 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1454 !(ei->change_events & CH_EVENT_BIT(k)))
1456 ei->change_events |= CH_EVENT_BIT(k);
1457 ei->event_page_nr[k] = j;
1458 ei->event_page[k] = &ei->change_page[j];
1466 /* add change events from custom element configuration */
1467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1469 int element = EL_CUSTOM_START + i;
1471 /* only add custom elements that change after fixed/random frame delay */
1472 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1473 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1477 /* ---------- initialize run-time trigger player and element ------------- */
1479 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1481 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1483 for (j = 0; j < ei->num_change_pages; j++)
1485 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1486 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1490 /* ---------- initialize trigger events ---------------------------------- */
1492 /* initialize trigger events information */
1493 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1494 trigger_events[i] = EP_BITMASK_DEFAULT;
1497 /* add trigger events from element change event properties */
1498 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1500 struct ElementInfo *ei = &element_info[i];
1502 for (j = 0; j < ei->num_change_pages; j++)
1504 if (!ei->change_page[j].can_change)
1507 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1509 int trigger_element = ei->change_page[j].trigger_element;
1511 if (IS_GROUP_ELEMENT(trigger_element))
1513 struct ElementGroupInfo *group = element_info[trigger_element].group;
1515 for (k = 0; k < group->num_elements_resolved; k++)
1516 trigger_events[group->element_resolved[k]]
1517 |= ei->change_page[j].events;
1520 trigger_events[trigger_element] |= ei->change_page[j].events;
1525 /* add trigger events from element change event properties */
1526 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1527 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1528 trigger_events[element_info[i].change->trigger_element] |=
1529 element_info[i].change->events;
1532 /* ---------- initialize push delay -------------------------------------- */
1534 /* initialize push delay values to default */
1535 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1537 if (!IS_CUSTOM_ELEMENT(i))
1539 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1540 element_info[i].push_delay_random = game.default_push_delay_random;
1544 /* set push delay value for certain elements from pre-defined list */
1545 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1547 int e = push_delay_list[i].element;
1549 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1550 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1553 /* set push delay value for Supaplex elements for newer engine versions */
1554 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1556 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1558 if (IS_SP_ELEMENT(i))
1560 #if USE_NEW_MOVE_STYLE
1561 /* set SP push delay to just enough to push under a falling zonk */
1562 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1564 element_info[i].push_delay_fixed = delay;
1565 element_info[i].push_delay_random = 0;
1567 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1568 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1574 /* ---------- initialize move stepsize ----------------------------------- */
1576 /* initialize move stepsize values to default */
1577 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1578 if (!IS_CUSTOM_ELEMENT(i))
1579 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1581 /* set move stepsize value for certain elements from pre-defined list */
1582 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1584 int e = move_stepsize_list[i].element;
1586 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1590 /* ---------- initialize move dig/leave ---------------------------------- */
1592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1594 element_info[i].can_leave_element = FALSE;
1595 element_info[i].can_leave_element_last = FALSE;
1599 /* ---------- initialize gem count --------------------------------------- */
1601 /* initialize gem count values for each element */
1602 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1603 if (!IS_CUSTOM_ELEMENT(i))
1604 element_info[i].collect_count = 0;
1606 /* add gem count values for all elements from pre-defined list */
1607 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1608 element_info[collect_count_list[i].element].collect_count =
1609 collect_count_list[i].count;
1611 /* ---------- initialize access direction -------------------------------- */
1613 /* initialize access direction values to default (access from every side) */
1614 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1615 if (!IS_CUSTOM_ELEMENT(i))
1616 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1618 /* set access direction value for certain elements from pre-defined list */
1619 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1620 element_info[access_direction_list[i].element].access_direction =
1621 access_direction_list[i].direction;
1626 =============================================================================
1628 -----------------------------------------------------------------------------
1629 initialize and start new game
1630 =============================================================================
1635 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1636 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1637 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1644 #if USE_NEW_AMOEBA_CODE
1645 printf("Using new amoeba code.\n");
1647 printf("Using old amoeba code.\n");
1652 /* don't play tapes over network */
1653 network_playing = (options.network && !tape.playing);
1655 for (i = 0; i < MAX_PLAYERS; i++)
1657 struct PlayerInfo *player = &stored_player[i];
1659 player->index_nr = i;
1660 player->index_bit = (1 << i);
1661 player->element_nr = EL_PLAYER_1 + i;
1663 player->present = FALSE;
1664 player->active = FALSE;
1667 player->effective_action = 0;
1668 player->programmed_action = 0;
1671 player->gems_still_needed = level.gems_needed;
1672 player->sokobanfields_still_needed = 0;
1673 player->lights_still_needed = 0;
1674 player->friends_still_needed = 0;
1676 for (j = 0; j < MAX_NUM_KEYS; j++)
1677 player->key[j] = FALSE;
1679 player->dynabomb_count = 0;
1680 player->dynabomb_size = 1;
1681 player->dynabombs_left = 0;
1682 player->dynabomb_xl = FALSE;
1684 player->MovDir = MV_NO_MOVING;
1687 player->GfxDir = MV_NO_MOVING;
1688 player->GfxAction = ACTION_DEFAULT;
1690 player->StepFrame = 0;
1692 player->use_murphy_graphic = FALSE;
1694 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1695 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1697 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1699 player->actual_frame_counter = 0;
1701 player->step_counter = 0;
1703 player->last_move_dir = MV_NO_MOVING;
1705 player->is_waiting = FALSE;
1706 player->is_moving = FALSE;
1707 player->is_auto_moving = FALSE;
1708 player->is_digging = FALSE;
1709 player->is_snapping = FALSE;
1710 player->is_collecting = FALSE;
1711 player->is_pushing = FALSE;
1712 player->is_switching = FALSE;
1713 player->is_dropping = FALSE;
1715 player->is_bored = FALSE;
1716 player->is_sleeping = FALSE;
1718 player->frame_counter_bored = -1;
1719 player->frame_counter_sleeping = -1;
1721 player->anim_delay_counter = 0;
1722 player->post_delay_counter = 0;
1724 player->action_waiting = ACTION_DEFAULT;
1725 player->last_action_waiting = ACTION_DEFAULT;
1726 player->special_action_bored = ACTION_DEFAULT;
1727 player->special_action_sleeping = ACTION_DEFAULT;
1729 player->num_special_action_bored = 0;
1730 player->num_special_action_sleeping = 0;
1732 /* determine number of special actions for bored and sleeping animation */
1733 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1735 boolean found = FALSE;
1737 for (k = 0; k < NUM_DIRECTIONS; k++)
1738 if (el_act_dir2img(player->element_nr, j, k) !=
1739 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1743 player->num_special_action_bored++;
1747 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1749 boolean found = FALSE;
1751 for (k = 0; k < NUM_DIRECTIONS; k++)
1752 if (el_act_dir2img(player->element_nr, j, k) !=
1753 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1757 player->num_special_action_sleeping++;
1762 player->switch_x = -1;
1763 player->switch_y = -1;
1765 player->show_envelope = 0;
1767 player->move_delay = game.initial_move_delay;
1768 player->move_delay_value = game.initial_move_delay_value;
1770 player->move_delay_reset_counter = 0;
1772 #if USE_NEW_PUSH_DELAY
1773 player->push_delay = -1; /* initialized when pushing starts */
1774 player->push_delay_value = game.initial_push_delay_value;
1776 player->push_delay = 0;
1777 player->push_delay_value = game.initial_push_delay_value;
1780 player->drop_delay = 0;
1782 player->last_jx = player->last_jy = 0;
1783 player->jx = player->jy = 0;
1785 player->shield_normal_time_left = 0;
1786 player->shield_deadly_time_left = 0;
1788 player->inventory_infinite_element = EL_UNDEFINED;
1789 player->inventory_size = 0;
1791 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1792 SnapField(player, 0, 0);
1794 player->LevelSolved = FALSE;
1795 player->GameOver = FALSE;
1798 network_player_action_received = FALSE;
1800 #if defined(NETWORK_AVALIABLE)
1801 /* initial null action */
1802 if (network_playing)
1803 SendToServer_MovePlayer(MV_NO_MOVING);
1812 TimeLeft = level.time;
1815 ScreenMovDir = MV_NO_MOVING;
1819 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1821 AllPlayersGone = FALSE;
1823 game.yamyam_content_nr = 0;
1824 game.magic_wall_active = FALSE;
1825 game.magic_wall_time_left = 0;
1826 game.light_time_left = 0;
1827 game.timegate_time_left = 0;
1828 game.switchgate_pos = 0;
1829 game.balloon_dir = MV_NO_MOVING;
1830 game.gravity = level.initial_gravity;
1831 game.explosions_delayed = TRUE;
1833 game.envelope_active = FALSE;
1835 for (i = 0; i < NUM_BELTS; i++)
1837 game.belt_dir[i] = MV_NO_MOVING;
1838 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1841 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1842 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1844 for (x = 0; x < lev_fieldx; x++)
1846 for (y = 0; y < lev_fieldy; y++)
1848 Feld[x][y] = level.field[x][y];
1849 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1850 ChangeDelay[x][y] = 0;
1851 ChangePage[x][y] = -1;
1852 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1854 WasJustMoving[x][y] = 0;
1855 WasJustFalling[x][y] = 0;
1856 CheckCollision[x][y] = 0;
1858 Pushed[x][y] = FALSE;
1860 Changed[x][y] = CE_BITMASK_DEFAULT;
1861 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1863 ExplodePhase[x][y] = 0;
1864 ExplodeDelay[x][y] = 0;
1865 ExplodeField[x][y] = EX_TYPE_NONE;
1867 RunnerVisit[x][y] = 0;
1868 PlayerVisit[x][y] = 0;
1871 GfxRandom[x][y] = INIT_GFX_RANDOM();
1872 GfxElement[x][y] = EL_UNDEFINED;
1873 GfxAction[x][y] = ACTION_DEFAULT;
1874 GfxDir[x][y] = MV_NO_MOVING;
1878 for (y = 0; y < lev_fieldy; y++)
1880 for (x = 0; x < lev_fieldx; x++)
1882 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1884 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1886 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1889 InitField(x, y, TRUE);
1895 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1896 emulate_sb ? EMU_SOKOBAN :
1897 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1899 /* initialize explosion and ignition delay */
1900 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1902 if (!IS_CUSTOM_ELEMENT(i))
1905 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1906 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1907 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1908 int last_phase = (num_phase + 1) * delay;
1909 int half_phase = (num_phase / 2) * delay;
1911 element_info[i].explosion_delay = last_phase - 1;
1912 element_info[i].ignition_delay = half_phase;
1915 if (i == EL_BLACK_ORB)
1916 element_info[i].ignition_delay = 0;
1918 if (i == EL_BLACK_ORB)
1919 element_info[i].ignition_delay = 1;
1924 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1925 element_info[i].explosion_delay = 1;
1927 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1928 element_info[i].ignition_delay = 1;
1932 /* correct non-moving belts to start moving left */
1933 for (i = 0; i < NUM_BELTS; i++)
1934 if (game.belt_dir[i] == MV_NO_MOVING)
1935 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1937 /* check if any connected player was not found in playfield */
1938 for (i = 0; i < MAX_PLAYERS; i++)
1940 struct PlayerInfo *player = &stored_player[i];
1942 if (player->connected && !player->present)
1944 for (j = 0; j < MAX_PLAYERS; j++)
1946 struct PlayerInfo *some_player = &stored_player[j];
1947 int jx = some_player->jx, jy = some_player->jy;
1949 /* assign first free player found that is present in the playfield */
1950 if (some_player->present && !some_player->connected)
1952 player->present = TRUE;
1953 player->active = TRUE;
1955 some_player->present = FALSE;
1956 some_player->active = FALSE;
1959 player->element_nr = some_player->element_nr;
1962 #if USE_NEW_BLOCK_STYLE
1963 player->block_last_field = some_player->block_last_field;
1964 player->block_delay_adjustment = some_player->block_delay_adjustment;
1967 StorePlayer[jx][jy] = player->element_nr;
1968 player->jx = player->last_jx = jx;
1969 player->jy = player->last_jy = jy;
1979 /* when playing a tape, eliminate all players which do not participate */
1981 for (i = 0; i < MAX_PLAYERS; i++)
1983 if (stored_player[i].active && !tape.player_participates[i])
1985 struct PlayerInfo *player = &stored_player[i];
1986 int jx = player->jx, jy = player->jy;
1988 player->active = FALSE;
1989 StorePlayer[jx][jy] = 0;
1990 Feld[jx][jy] = EL_EMPTY;
1994 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1996 /* when in single player mode, eliminate all but the first active player */
1998 for (i = 0; i < MAX_PLAYERS; i++)
2000 if (stored_player[i].active)
2002 for (j = i + 1; j < MAX_PLAYERS; j++)
2004 if (stored_player[j].active)
2006 struct PlayerInfo *player = &stored_player[j];
2007 int jx = player->jx, jy = player->jy;
2009 player->active = FALSE;
2010 player->present = FALSE;
2012 StorePlayer[jx][jy] = 0;
2013 Feld[jx][jy] = EL_EMPTY;
2020 /* when recording the game, store which players take part in the game */
2023 for (i = 0; i < MAX_PLAYERS; i++)
2024 if (stored_player[i].active)
2025 tape.player_participates[i] = TRUE;
2030 for (i = 0; i < MAX_PLAYERS; i++)
2032 struct PlayerInfo *player = &stored_player[i];
2034 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2039 if (local_player == player)
2040 printf("Player %d is local player.\n", i+1);
2044 if (BorderElement == EL_EMPTY)
2047 SBX_Right = lev_fieldx - SCR_FIELDX;
2049 SBY_Lower = lev_fieldy - SCR_FIELDY;
2054 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2056 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2059 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2060 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2062 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2063 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2065 /* if local player not found, look for custom element that might create
2066 the player (make some assumptions about the right custom element) */
2067 if (!local_player->present)
2069 int start_x = 0, start_y = 0;
2070 int found_rating = 0;
2071 int found_element = EL_UNDEFINED;
2073 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2075 int element = Feld[x][y];
2080 if (!IS_CUSTOM_ELEMENT(element))
2083 if (CAN_CHANGE(element))
2085 for (i = 0; i < element_info[element].num_change_pages; i++)
2087 content = element_info[element].change_page[i].target_element;
2088 is_player = ELEM_IS_PLAYER(content);
2090 if (is_player && (found_rating < 3 || element < found_element))
2096 found_element = element;
2101 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2103 content = element_info[element].content[xx][yy];
2104 is_player = ELEM_IS_PLAYER(content);
2106 if (is_player && (found_rating < 2 || element < found_element))
2108 start_x = x + xx - 1;
2109 start_y = y + yy - 1;
2112 found_element = element;
2115 if (!CAN_CHANGE(element))
2118 for (i = 0; i < element_info[element].num_change_pages; i++)
2120 content= element_info[element].change_page[i].target_content[xx][yy];
2121 is_player = ELEM_IS_PLAYER(content);
2123 if (is_player && (found_rating < 1 || element < found_element))
2125 start_x = x + xx - 1;
2126 start_y = y + yy - 1;
2129 found_element = element;
2135 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2136 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2139 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2140 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2146 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2147 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2148 local_player->jx - MIDPOSX);
2150 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2151 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2152 local_player->jy - MIDPOSY);
2154 scroll_x = SBX_Left;
2155 scroll_y = SBY_Upper;
2156 if (local_player->jx >= SBX_Left + MIDPOSX)
2157 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2158 local_player->jx - MIDPOSX :
2160 if (local_player->jy >= SBY_Upper + MIDPOSY)
2161 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2162 local_player->jy - MIDPOSY :
2167 CloseDoor(DOOR_CLOSE_1);
2169 /* !!! FIX THIS (START) !!! */
2170 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2172 InitGameEngine_EM();
2179 /* after drawing the level, correct some elements */
2180 if (game.timegate_time_left == 0)
2181 CloseAllOpenTimegates();
2183 if (setup.soft_scrolling)
2184 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2186 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2189 /* !!! FIX THIS (END) !!! */
2191 /* copy default game door content to main double buffer */
2192 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2193 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2195 DrawGameDoorValues();
2199 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2200 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2201 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2205 /* copy actual game door content to door double buffer for OpenDoor() */
2206 BlitBitmap(drawto, bitmap_db_door,
2207 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2209 OpenDoor(DOOR_OPEN_ALL);
2211 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2213 if (setup.sound_music)
2216 KeyboardAutoRepeatOffUnlessAutoplay();
2220 for (i = 0; i < MAX_PLAYERS; i++)
2221 printf("Player %d %sactive.\n",
2222 i + 1, (stored_player[i].active ? "" : "not "));
2226 printf("::: starting game [%d]\n", FrameCounter);
2230 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2232 /* this is used for non-R'n'D game engines to update certain engine values */
2234 /* needed to determine if sounds are played within the visible screen area */
2235 scroll_x = actual_scroll_x;
2236 scroll_y = actual_scroll_y;
2239 void InitMovDir(int x, int y)
2241 int i, element = Feld[x][y];
2242 static int xy[4][2] =
2249 static int direction[3][4] =
2251 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2252 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2253 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2262 Feld[x][y] = EL_BUG;
2263 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2266 case EL_SPACESHIP_RIGHT:
2267 case EL_SPACESHIP_UP:
2268 case EL_SPACESHIP_LEFT:
2269 case EL_SPACESHIP_DOWN:
2270 Feld[x][y] = EL_SPACESHIP;
2271 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2274 case EL_BD_BUTTERFLY_RIGHT:
2275 case EL_BD_BUTTERFLY_UP:
2276 case EL_BD_BUTTERFLY_LEFT:
2277 case EL_BD_BUTTERFLY_DOWN:
2278 Feld[x][y] = EL_BD_BUTTERFLY;
2279 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2282 case EL_BD_FIREFLY_RIGHT:
2283 case EL_BD_FIREFLY_UP:
2284 case EL_BD_FIREFLY_LEFT:
2285 case EL_BD_FIREFLY_DOWN:
2286 Feld[x][y] = EL_BD_FIREFLY;
2287 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2290 case EL_PACMAN_RIGHT:
2292 case EL_PACMAN_LEFT:
2293 case EL_PACMAN_DOWN:
2294 Feld[x][y] = EL_PACMAN;
2295 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2298 case EL_SP_SNIKSNAK:
2299 MovDir[x][y] = MV_UP;
2302 case EL_SP_ELECTRON:
2303 MovDir[x][y] = MV_LEFT;
2310 Feld[x][y] = EL_MOLE;
2311 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2315 if (IS_CUSTOM_ELEMENT(element))
2317 struct ElementInfo *ei = &element_info[element];
2318 int move_direction_initial = ei->move_direction_initial;
2319 int move_pattern = ei->move_pattern;
2321 if (move_direction_initial == MV_START_PREVIOUS)
2323 if (MovDir[x][y] != MV_NO_MOVING)
2326 move_direction_initial = MV_START_AUTOMATIC;
2329 if (move_direction_initial == MV_START_RANDOM)
2330 MovDir[x][y] = 1 << RND(4);
2331 else if (move_direction_initial & MV_ANY_DIRECTION)
2332 MovDir[x][y] = move_direction_initial;
2333 else if (move_pattern == MV_ALL_DIRECTIONS ||
2334 move_pattern == MV_TURNING_LEFT ||
2335 move_pattern == MV_TURNING_RIGHT ||
2336 move_pattern == MV_TURNING_LEFT_RIGHT ||
2337 move_pattern == MV_TURNING_RIGHT_LEFT ||
2338 move_pattern == MV_TURNING_RANDOM)
2339 MovDir[x][y] = 1 << RND(4);
2340 else if (move_pattern == MV_HORIZONTAL)
2341 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2342 else if (move_pattern == MV_VERTICAL)
2343 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2344 else if (move_pattern & MV_ANY_DIRECTION)
2345 MovDir[x][y] = element_info[element].move_pattern;
2346 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2347 move_pattern == MV_ALONG_RIGHT_SIDE)
2350 /* use random direction as default start direction */
2351 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2352 MovDir[x][y] = 1 << RND(4);
2355 for (i = 0; i < NUM_DIRECTIONS; i++)
2357 int x1 = x + xy[i][0];
2358 int y1 = y + xy[i][1];
2360 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2362 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2363 MovDir[x][y] = direction[0][i];
2365 MovDir[x][y] = direction[1][i];
2374 MovDir[x][y] = 1 << RND(4);
2376 if (element != EL_BUG &&
2377 element != EL_SPACESHIP &&
2378 element != EL_BD_BUTTERFLY &&
2379 element != EL_BD_FIREFLY)
2382 for (i = 0; i < NUM_DIRECTIONS; i++)
2384 int x1 = x + xy[i][0];
2385 int y1 = y + xy[i][1];
2387 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2389 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2391 MovDir[x][y] = direction[0][i];
2394 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2395 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2397 MovDir[x][y] = direction[1][i];
2406 GfxDir[x][y] = MovDir[x][y];
2409 void InitAmoebaNr(int x, int y)
2412 int group_nr = AmoebeNachbarNr(x, y);
2416 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2418 if (AmoebaCnt[i] == 0)
2426 AmoebaNr[x][y] = group_nr;
2427 AmoebaCnt[group_nr]++;
2428 AmoebaCnt2[group_nr]++;
2434 boolean raise_level = FALSE;
2436 if (local_player->MovPos)
2440 if (tape.auto_play) /* tape might already be stopped here */
2441 tape.auto_play_level_solved = TRUE;
2443 if (tape.playing && tape.auto_play)
2444 tape.auto_play_level_solved = TRUE;
2447 local_player->LevelSolved = FALSE;
2449 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2453 if (!tape.playing && setup.sound_loops)
2454 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2455 SND_CTRL_PLAY_LOOP);
2457 while (TimeLeft > 0)
2459 if (!tape.playing && !setup.sound_loops)
2460 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2461 if (TimeLeft > 0 && !(TimeLeft % 10))
2462 RaiseScore(level.score[SC_TIME_BONUS]);
2463 if (TimeLeft > 100 && !(TimeLeft % 10))
2468 DrawGameValue_Time(TimeLeft);
2476 if (!tape.playing && setup.sound_loops)
2477 StopSound(SND_GAME_LEVELTIME_BONUS);
2479 else if (level.time == 0) /* level without time limit */
2481 if (!tape.playing && setup.sound_loops)
2482 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2483 SND_CTRL_PLAY_LOOP);
2485 while (TimePlayed < 999)
2487 if (!tape.playing && !setup.sound_loops)
2488 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2489 if (TimePlayed < 999 && !(TimePlayed % 10))
2490 RaiseScore(level.score[SC_TIME_BONUS]);
2491 if (TimePlayed < 900 && !(TimePlayed % 10))
2496 DrawGameValue_Time(TimePlayed);
2504 if (!tape.playing && setup.sound_loops)
2505 StopSound(SND_GAME_LEVELTIME_BONUS);
2508 /* close exit door after last player */
2509 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2510 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2511 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2513 int element = Feld[ExitX][ExitY];
2515 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2516 EL_SP_EXIT_CLOSING);
2518 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2521 /* Hero disappears */
2522 if (ExitX >= 0 && ExitY >= 0)
2523 DrawLevelField(ExitX, ExitY);
2530 CloseDoor(DOOR_CLOSE_1);
2535 SaveTape(tape.level_nr); /* Ask to save tape */
2538 if (level_nr == leveldir_current->handicap_level)
2540 leveldir_current->handicap_level++;
2541 SaveLevelSetup_SeriesInfo();
2544 if (level_editor_test_game)
2545 local_player->score = -1; /* no highscore when playing from editor */
2546 else if (level_nr < leveldir_current->last_level)
2547 raise_level = TRUE; /* advance to next level */
2549 if ((hi_pos = NewHiScore()) >= 0)
2551 game_status = GAME_MODE_SCORES;
2552 DrawHallOfFame(hi_pos);
2561 game_status = GAME_MODE_MAIN;
2578 LoadScore(level_nr);
2580 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2581 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2584 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2586 if (local_player->score > highscore[k].Score)
2588 /* player has made it to the hall of fame */
2590 if (k < MAX_SCORE_ENTRIES - 1)
2592 int m = MAX_SCORE_ENTRIES - 1;
2595 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2596 if (!strcmp(setup.player_name, highscore[l].Name))
2598 if (m == k) /* player's new highscore overwrites his old one */
2602 for (l = m; l > k; l--)
2604 strcpy(highscore[l].Name, highscore[l - 1].Name);
2605 highscore[l].Score = highscore[l - 1].Score;
2612 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2613 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2614 highscore[k].Score = local_player->score;
2620 else if (!strncmp(setup.player_name, highscore[k].Name,
2621 MAX_PLAYER_NAME_LEN))
2622 break; /* player already there with a higher score */
2628 SaveScore(level_nr);
2633 inline static int getElementMoveStepsize(int x, int y)
2635 int element = Feld[x][y];
2636 int direction = MovDir[x][y];
2637 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2638 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2639 int horiz_move = (dx != 0);
2640 int sign = (horiz_move ? dx : dy);
2641 int step = sign * element_info[element].move_stepsize;
2643 /* special values for move stepsize for spring and things on conveyor belt */
2647 if (element == EL_SPRING)
2648 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2649 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2650 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2651 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2653 if (CAN_FALL(element) &&
2654 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2655 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2656 else if (element == EL_SPRING)
2657 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2664 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2666 if (player->GfxAction != action || player->GfxDir != dir)
2669 printf("Player frame reset! (%d => %d, %d => %d)\n",
2670 player->GfxAction, action, player->GfxDir, dir);
2673 player->GfxAction = action;
2674 player->GfxDir = dir;
2676 player->StepFrame = 0;
2680 static void ResetRandomAnimationValue(int x, int y)
2682 GfxRandom[x][y] = INIT_GFX_RANDOM();
2685 static void ResetGfxAnimation(int x, int y)
2688 GfxAction[x][y] = ACTION_DEFAULT;
2689 GfxDir[x][y] = MovDir[x][y];
2692 void InitMovingField(int x, int y, int direction)
2694 int element = Feld[x][y];
2695 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2696 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2700 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2701 ResetGfxAnimation(x, y);
2703 #if USE_CAN_MOVE_NOT_MOVING
2705 MovDir[x][y] = direction;
2706 GfxDir[x][y] = direction;
2707 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2708 ACTION_FALLING : ACTION_MOVING);
2710 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2712 if (Feld[newx][newy] == EL_EMPTY)
2713 Feld[newx][newy] = EL_BLOCKED;
2715 MovDir[newx][newy] = MovDir[x][y];
2716 GfxFrame[newx][newy] = GfxFrame[x][y];
2717 GfxRandom[newx][newy] = GfxRandom[x][y];
2718 GfxAction[newx][newy] = GfxAction[x][y];
2719 GfxDir[newx][newy] = GfxDir[x][y];
2724 MovDir[newx][newy] = MovDir[x][y] = direction;
2725 GfxDir[x][y] = direction;
2727 if (Feld[newx][newy] == EL_EMPTY)
2728 Feld[newx][newy] = EL_BLOCKED;
2730 if (direction == MV_DOWN && CAN_FALL(element))
2731 GfxAction[x][y] = ACTION_FALLING;
2733 GfxAction[x][y] = ACTION_MOVING;
2735 GfxFrame[newx][newy] = GfxFrame[x][y];
2736 GfxRandom[newx][newy] = GfxRandom[x][y];
2737 GfxAction[newx][newy] = GfxAction[x][y];
2738 GfxDir[newx][newy] = GfxDir[x][y];
2742 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2744 int direction = MovDir[x][y];
2745 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2746 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2752 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2754 int oldx = x, oldy = y;
2755 int direction = MovDir[x][y];
2757 if (direction == MV_LEFT)
2759 else if (direction == MV_RIGHT)
2761 else if (direction == MV_UP)
2763 else if (direction == MV_DOWN)
2766 *comes_from_x = oldx;
2767 *comes_from_y = oldy;
2770 int MovingOrBlocked2Element(int x, int y)
2772 int element = Feld[x][y];
2774 if (element == EL_BLOCKED)
2778 Blocked2Moving(x, y, &oldx, &oldy);
2779 return Feld[oldx][oldy];
2785 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2787 /* like MovingOrBlocked2Element(), but if element is moving
2788 and (x,y) is the field the moving element is just leaving,
2789 return EL_BLOCKED instead of the element value */
2790 int element = Feld[x][y];
2792 if (IS_MOVING(x, y))
2794 if (element == EL_BLOCKED)
2798 Blocked2Moving(x, y, &oldx, &oldy);
2799 return Feld[oldx][oldy];
2808 static void RemoveField(int x, int y)
2810 Feld[x][y] = EL_EMPTY;
2817 ChangeDelay[x][y] = 0;
2818 ChangePage[x][y] = -1;
2819 Pushed[x][y] = FALSE;
2822 ExplodeField[x][y] = EX_TYPE_NONE;
2825 GfxElement[x][y] = EL_UNDEFINED;
2826 GfxAction[x][y] = ACTION_DEFAULT;
2827 GfxDir[x][y] = MV_NO_MOVING;
2830 void RemoveMovingField(int x, int y)
2832 int oldx = x, oldy = y, newx = x, newy = y;
2833 int element = Feld[x][y];
2834 int next_element = EL_UNDEFINED;
2836 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2839 if (IS_MOVING(x, y))
2841 Moving2Blocked(x, y, &newx, &newy);
2843 if (Feld[newx][newy] != EL_BLOCKED)
2846 if (Feld[newx][newy] != EL_BLOCKED)
2848 /* element is moving, but target field is not free (blocked), but
2849 already occupied by something different (example: acid pool);
2850 in this case, only remove the moving field, but not the target */
2852 RemoveField(oldx, oldy);
2854 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2856 DrawLevelField(oldx, oldy);
2862 else if (element == EL_BLOCKED)
2864 Blocked2Moving(x, y, &oldx, &oldy);
2865 if (!IS_MOVING(oldx, oldy))
2869 if (element == EL_BLOCKED &&
2870 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2871 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2872 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2873 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2874 next_element = get_next_element(Feld[oldx][oldy]);
2876 RemoveField(oldx, oldy);
2877 RemoveField(newx, newy);
2879 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2881 if (next_element != EL_UNDEFINED)
2882 Feld[oldx][oldy] = next_element;
2884 DrawLevelField(oldx, oldy);
2885 DrawLevelField(newx, newy);
2888 void DrawDynamite(int x, int y)
2890 int sx = SCREENX(x), sy = SCREENY(y);
2891 int graphic = el2img(Feld[x][y]);
2894 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2897 if (IS_WALKABLE_INSIDE(Back[x][y]))
2901 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2902 else if (Store[x][y])
2903 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2905 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2908 if (Back[x][y] || Store[x][y])
2909 DrawGraphicThruMask(sx, sy, graphic, frame);
2911 DrawGraphic(sx, sy, graphic, frame);
2913 if (game.emulation == EMU_SUPAPLEX)
2914 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2915 else if (Store[x][y])
2916 DrawGraphicThruMask(sx, sy, graphic, frame);
2918 DrawGraphic(sx, sy, graphic, frame);
2922 void CheckDynamite(int x, int y)
2924 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2928 if (MovDelay[x][y] != 0)
2931 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2938 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2940 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2941 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2942 StopSound(SND_DYNAMITE_ACTIVE);
2944 StopSound(SND_DYNABOMB_ACTIVE);
2950 void DrawRelocatePlayer(struct PlayerInfo *player)
2952 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2953 boolean no_delay = (tape.warp_forward);
2954 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2955 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2956 int jx = player->jx;
2957 int jy = player->jy;
2959 if (level.instant_relocation)
2962 int offset = (setup.scroll_delay ? 3 : 0);
2964 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2966 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2967 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2968 local_player->jx - MIDPOSX);
2970 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2971 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2972 local_player->jy - MIDPOSY);
2976 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2977 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2978 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2980 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2981 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2982 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2984 /* don't scroll over playfield boundaries */
2985 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2986 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2988 /* don't scroll over playfield boundaries */
2989 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2990 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2993 scroll_x += (local_player->jx - old_jx);
2994 scroll_y += (local_player->jy - old_jy);
2996 /* don't scroll over playfield boundaries */
2997 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2998 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3000 /* don't scroll over playfield boundaries */
3001 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3002 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3005 RedrawPlayfield(TRUE, 0,0,0,0);
3011 int offset = (setup.scroll_delay ? 3 : 0);
3013 int scroll_xx = -999, scroll_yy = -999;
3015 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3017 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3020 int fx = FX, fy = FY;
3022 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3023 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3024 local_player->jx - MIDPOSX);
3026 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3027 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3028 local_player->jy - MIDPOSY);
3030 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3031 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3034 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3037 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3044 fx += dx * TILEX / 2;
3045 fy += dy * TILEY / 2;
3047 ScrollLevel(dx, dy);
3050 /* scroll in two steps of half tile size to make things smoother */
3051 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3053 Delay(wait_delay_value);
3055 /* scroll second step to align at full tile size */
3057 Delay(wait_delay_value);
3060 int scroll_xx = -999, scroll_yy = -999;
3062 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3064 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3067 int fx = FX, fy = FY;
3069 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3070 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3071 local_player->jx - MIDPOSX);
3073 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3074 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3075 local_player->jy - MIDPOSY);
3077 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3078 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3081 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3084 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3091 fx += dx * TILEX / 2;
3092 fy += dy * TILEY / 2;
3094 ScrollLevel(dx, dy);
3097 /* scroll in two steps of half tile size to make things smoother */
3098 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3100 Delay(wait_delay_value);
3102 /* scroll second step to align at full tile size */
3104 Delay(wait_delay_value);
3110 Delay(wait_delay_value);
3114 void RelocatePlayer(int jx, int jy, int el_player_raw)
3117 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3119 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3121 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3122 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3123 boolean no_delay = (tape.warp_forward);
3124 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3125 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3126 int old_jx = player->jx;
3127 int old_jy = player->jy;
3128 int old_element = Feld[old_jx][old_jy];
3129 int element = Feld[jx][jy];
3130 boolean player_relocated = (old_jx != jx || old_jy != jy);
3132 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3133 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3135 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3136 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3137 int leave_side_horiz = move_dir_horiz;
3138 int leave_side_vert = move_dir_vert;
3140 static int trigger_sides[4][2] =
3142 /* enter side leave side */
3143 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3144 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3145 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3146 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3148 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3149 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3150 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3151 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3153 int enter_side = enter_side_horiz | enter_side_vert;
3154 int leave_side = leave_side_horiz | leave_side_vert;
3156 if (player->GameOver) /* do not reanimate dead player */
3159 if (!player_relocated) /* no need to relocate the player */
3162 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3164 RemoveField(jx, jy); /* temporarily remove newly placed player */
3165 DrawLevelField(jx, jy);
3168 if (player->present)
3170 while (player->MovPos)
3172 ScrollPlayer(player, SCROLL_GO_ON);
3173 ScrollScreen(NULL, SCROLL_GO_ON);
3175 #if USE_NEW_MOVE_DELAY
3176 AdvanceFrameAndPlayerCounters(player->index_nr);
3184 Delay(wait_delay_value);
3187 DrawPlayer(player); /* needed here only to cleanup last field */
3188 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3190 player->is_moving = FALSE;
3194 if (IS_CUSTOM_ELEMENT(old_element))
3195 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3197 player->index_bit, leave_side);
3199 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3201 player->index_bit, leave_side);
3204 Feld[jx][jy] = el_player;
3205 InitPlayerField(jx, jy, el_player, TRUE);
3207 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3209 Feld[jx][jy] = element;
3210 InitField(jx, jy, FALSE);
3214 if (player == local_player) /* only visually relocate local player */
3215 DrawRelocatePlayer(player);
3219 TestIfHeroTouchesBadThing(jx, jy);
3220 TestIfPlayerTouchesCustomElement(jx, jy);
3224 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3229 /* needed to allow change of walkable custom element by entering player */
3230 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3231 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3233 /* needed to allow change of walkable custom element by entering player */
3234 Changed[jx][jy] = 0; /* allow another change */
3239 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3240 enter_side == MV_LEFT ? "left" :
3241 enter_side == MV_RIGHT ? "right" :
3242 enter_side == MV_UP ? "top" :
3243 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3247 if (IS_CUSTOM_ELEMENT(element))
3248 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3249 player->index_bit, enter_side);
3251 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3252 CE_OTHER_GETS_ENTERED,
3253 player->index_bit, enter_side);
3257 void Explode(int ex, int ey, int phase, int mode)
3264 /* !!! eliminate this variable !!! */
3265 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3270 int last_phase = num_phase * delay;
3271 int half_phase = (num_phase / 2) * delay;
3272 int first_phase_after_start = EX_PHASE_START + 1;
3276 if (game.explosions_delayed)
3278 ExplodeField[ex][ey] = mode;
3282 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3284 int center_element = Feld[ex][ey];
3287 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3291 /* --- This is only really needed (and now handled) in "Impact()". --- */
3292 /* do not explode moving elements that left the explode field in time */
3293 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3294 center_element == EL_EMPTY &&
3295 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3300 if (mode == EX_TYPE_NORMAL ||
3301 mode == EX_TYPE_CENTER ||
3302 mode == EX_TYPE_CROSS)
3303 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3305 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3306 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3309 /* remove things displayed in background while burning dynamite */
3310 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3313 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3315 /* put moving element to center field (and let it explode there) */
3316 center_element = MovingOrBlocked2Element(ex, ey);
3317 RemoveMovingField(ex, ey);
3318 Feld[ex][ey] = center_element;
3324 last_phase = element_info[center_element].explosion_delay + 1;
3326 last_phase = element_info[center_element].explosion_delay;
3330 printf("::: %d -> %d\n", center_element, last_phase);
3334 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3336 int xx = x - ex + 1;
3337 int yy = y - ey + 1;
3342 if (!IN_LEV_FIELD(x, y) ||
3343 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3344 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3347 if (!IN_LEV_FIELD(x, y) ||
3348 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3352 if (!IN_LEV_FIELD(x, y) ||
3353 ((mode != EX_TYPE_NORMAL ||
3354 center_element == EL_AMOEBA_TO_DIAMOND) &&
3355 (x != ex || y != ey)))
3359 element = Feld[x][y];
3361 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3363 element = MovingOrBlocked2Element(x, y);
3365 if (!IS_EXPLOSION_PROOF(element))
3366 RemoveMovingField(x, y);
3372 if (IS_EXPLOSION_PROOF(element))
3375 /* indestructible elements can only explode in center (but not flames) */
3377 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3378 mode == EX_TYPE_BORDER)) ||
3379 element == EL_FLAMES)
3382 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3383 element == EL_FLAMES)
3389 if ((IS_INDESTRUCTIBLE(element) &&
3390 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3391 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3392 element == EL_FLAMES)
3397 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3398 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3399 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3401 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3404 if (IS_ACTIVE_BOMB(element))
3406 /* re-activate things under the bomb like gate or penguin */
3408 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3411 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3416 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3417 element_info[Feld[x][y]].token_name,
3418 Store[x][y], Store2[x][y]);
3425 /* save walkable background elements while explosion on same tile */
3427 if (IS_INDESTRUCTIBLE(element))
3428 Back[x][y] = element;
3432 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3433 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3434 Back[x][y] = element;
3436 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3437 (x != ex || y != ey))
3438 Back[x][y] = element;
3441 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3442 Back[x][y] = element;
3446 /* ignite explodable elements reached by other explosion */
3447 if (element == EL_EXPLOSION)
3448 element = Store2[x][y];
3451 if (AmoebaNr[x][y] &&
3452 (element == EL_AMOEBA_FULL ||
3453 element == EL_BD_AMOEBA ||
3454 element == EL_AMOEBA_GROWING))
3456 AmoebaCnt[AmoebaNr[x][y]]--;
3457 AmoebaCnt2[AmoebaNr[x][y]]--;
3463 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3465 switch(StorePlayer[ex][ey])
3468 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3471 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3474 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3478 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3483 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3484 Store[x][y] = EL_EMPTY;
3486 if (game.emulation == EMU_SUPAPLEX)
3487 Store[x][y] = EL_EMPTY;
3490 else if (center_element == EL_MOLE)
3491 Store[x][y] = EL_EMERALD_RED;
3492 else if (center_element == EL_PENGUIN)
3493 Store[x][y] = EL_EMERALD_PURPLE;
3494 else if (center_element == EL_BUG)
3495 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3496 else if (center_element == EL_BD_BUTTERFLY)
3497 Store[x][y] = EL_BD_DIAMOND;
3498 else if (center_element == EL_SP_ELECTRON)
3499 Store[x][y] = EL_SP_INFOTRON;
3500 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3501 Store[x][y] = level.amoeba_content;
3502 else if (center_element == EL_YAMYAM)
3503 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3504 else if (IS_CUSTOM_ELEMENT(center_element) &&
3505 element_info[center_element].content[xx][yy] != EL_EMPTY)
3506 Store[x][y] = element_info[center_element].content[xx][yy];
3507 else if (element == EL_WALL_EMERALD)
3508 Store[x][y] = EL_EMERALD;
3509 else if (element == EL_WALL_DIAMOND)
3510 Store[x][y] = EL_DIAMOND;
3511 else if (element == EL_WALL_BD_DIAMOND)
3512 Store[x][y] = EL_BD_DIAMOND;
3513 else if (element == EL_WALL_EMERALD_YELLOW)
3514 Store[x][y] = EL_EMERALD_YELLOW;
3515 else if (element == EL_WALL_EMERALD_RED)
3516 Store[x][y] = EL_EMERALD_RED;
3517 else if (element == EL_WALL_EMERALD_PURPLE)
3518 Store[x][y] = EL_EMERALD_PURPLE;
3519 else if (element == EL_WALL_PEARL)
3520 Store[x][y] = EL_PEARL;
3521 else if (element == EL_WALL_CRYSTAL)
3522 Store[x][y] = EL_CRYSTAL;
3523 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3524 Store[x][y] = element_info[element].content[1][1];
3526 Store[x][y] = EL_EMPTY;
3528 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3529 center_element == EL_AMOEBA_TO_DIAMOND)
3530 Store2[x][y] = element;
3533 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3534 element_info[Store2[x][y]].token_name);
3538 if (AmoebaNr[x][y] &&
3539 (element == EL_AMOEBA_FULL ||
3540 element == EL_BD_AMOEBA ||
3541 element == EL_AMOEBA_GROWING))
3543 AmoebaCnt[AmoebaNr[x][y]]--;
3544 AmoebaCnt2[AmoebaNr[x][y]]--;
3550 MovDir[x][y] = MovPos[x][y] = 0;
3551 GfxDir[x][y] = MovDir[x][y];
3556 Feld[x][y] = EL_EXPLOSION;
3558 GfxElement[x][y] = center_element;
3560 GfxElement[x][y] = EL_UNDEFINED;
3563 ExplodePhase[x][y] = 1;
3565 ExplodeDelay[x][y] = last_phase;
3570 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3572 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3579 if (center_element == EL_YAMYAM)
3580 game.yamyam_content_nr =
3581 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3584 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3585 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3599 GfxFrame[x][y] = 0; /* restart explosion animation */
3603 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3607 last_phase = ExplodeDelay[x][y];
3610 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3614 /* activate this even in non-DEBUG version until cause for crash in
3615 getGraphicAnimationFrame() (see below) is found and eliminated */
3619 if (GfxElement[x][y] == EL_UNDEFINED)
3622 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3623 printf("Explode(): This should never happen!\n");
3626 GfxElement[x][y] = EL_EMPTY;
3632 border_element = Store2[x][y];
3634 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3635 border_element = StorePlayer[x][y];
3637 if (IS_PLAYER(x, y))
3638 border_element = StorePlayer[x][y];
3642 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3643 element_info[border_element].token_name, Store2[x][y]);
3647 printf("::: phase == %d\n", phase);
3650 if (phase == element_info[border_element].ignition_delay ||
3651 phase == last_phase)
3653 boolean border_explosion = FALSE;
3657 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3658 !PLAYER_EXPLOSION_PROTECTED(x, y))
3660 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3663 if (IS_PLAYER(x, y))
3666 KillHeroUnlessExplosionProtected(x, y);
3667 border_explosion = TRUE;
3670 if (phase == last_phase)
3671 printf("::: IS_PLAYER\n");
3674 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3677 printf("::: %d,%d: %d %s\n", x, y, border_element,
3678 element_info[border_element].token_name);
3681 Feld[x][y] = Store2[x][y];
3684 border_explosion = TRUE;
3687 if (phase == last_phase)
3688 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3691 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3693 AmoebeUmwandeln(x, y);
3695 border_explosion = TRUE;
3698 if (phase == last_phase)
3699 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3700 element_info[border_element].explosion_delay,
3701 element_info[border_element].ignition_delay,
3707 /* if an element just explodes due to another explosion (chain-reaction),
3708 do not immediately end the new explosion when it was the last frame of
3709 the explosion (as it would be done in the following "if"-statement!) */
3710 if (border_explosion && phase == last_phase)
3717 if (phase == first_phase_after_start)
3719 int element = Store2[x][y];
3721 if (element == EL_BLACK_ORB)
3723 Feld[x][y] = Store2[x][y];
3728 else if (phase == half_phase)
3730 int element = Store2[x][y];
3732 if (IS_PLAYER(x, y))
3733 KillHeroUnlessExplosionProtected(x, y);
3734 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3736 Feld[x][y] = Store2[x][y];
3740 else if (element == EL_AMOEBA_TO_DIAMOND)
3741 AmoebeUmwandeln(x, y);
3745 if (phase == last_phase)
3750 printf("::: done: phase == %d\n", phase);
3754 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3757 element = Feld[x][y] = Store[x][y];
3758 Store[x][y] = Store2[x][y] = 0;
3759 GfxElement[x][y] = EL_UNDEFINED;
3761 /* player can escape from explosions and might therefore be still alive */
3762 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3763 element <= EL_PLAYER_IS_EXPLODING_4)
3764 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3766 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3767 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3768 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3771 /* restore probably existing indestructible background element */
3772 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3773 element = Feld[x][y] = Back[x][y];
3776 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3777 GfxDir[x][y] = MV_NO_MOVING;
3778 ChangeDelay[x][y] = 0;
3779 ChangePage[x][y] = -1;
3782 InitField_WithBug2(x, y, FALSE);
3784 InitField(x, y, FALSE);
3786 /* !!! not needed !!! */
3788 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3789 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3792 if (CAN_MOVE(element))
3797 DrawLevelField(x, y);
3799 TestIfElementTouchesCustomElement(x, y);
3801 if (GFX_CRUMBLED(element))
3802 DrawLevelFieldCrumbledSandNeighbours(x, y);
3804 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3805 StorePlayer[x][y] = 0;
3807 if (ELEM_IS_PLAYER(element))
3808 RelocatePlayer(x, y, element);
3811 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3813 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3817 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3819 int stored = Store[x][y];
3820 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3821 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3825 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3827 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3831 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3835 printf("::: %d / %d [%d - %d]\n",
3836 GfxFrame[x][y], phase - delay, phase, delay);
3840 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3841 element_info[GfxElement[x][y]].token_name,
3846 DrawLevelFieldCrumbledSand(x, y);
3848 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3850 DrawLevelElement(x, y, Back[x][y]);
3851 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3853 else if (IS_WALKABLE_UNDER(Back[x][y]))
3855 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3856 DrawLevelElementThruMask(x, y, Back[x][y]);
3858 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3859 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3863 void DynaExplode(int ex, int ey)
3866 int dynabomb_element = Feld[ex][ey];
3867 int dynabomb_size = 1;
3868 boolean dynabomb_xl = FALSE;
3869 struct PlayerInfo *player;
3870 static int xy[4][2] =
3878 if (IS_ACTIVE_BOMB(dynabomb_element))
3880 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3881 dynabomb_size = player->dynabomb_size;
3882 dynabomb_xl = player->dynabomb_xl;
3883 player->dynabombs_left++;
3886 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3888 for (i = 0; i < NUM_DIRECTIONS; i++)
3890 for (j = 1; j <= dynabomb_size; j++)
3892 int x = ex + j * xy[i][0];
3893 int y = ey + j * xy[i][1];
3896 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3899 element = Feld[x][y];
3901 /* do not restart explosions of fields with active bombs */
3902 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3905 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3909 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3910 !IS_DIGGABLE(element) && !dynabomb_xl)
3913 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3914 !CAN_GROW_INTO(element) && !dynabomb_xl)
3918 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3919 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3920 element != EL_SAND && !dynabomb_xl)
3927 void Bang(int x, int y)
3930 int element = MovingOrBlocked2Element(x, y);
3932 int element = Feld[x][y];
3936 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3938 if (IS_PLAYER(x, y))
3941 struct PlayerInfo *player = PLAYERINFO(x, y);
3943 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3944 player->element_nr);
3949 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3951 if (game.emulation == EMU_SUPAPLEX)
3952 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3954 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3959 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3967 case EL_BD_BUTTERFLY:
3970 case EL_DARK_YAMYAM:
3974 RaiseScoreElement(element);
3975 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3977 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3978 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3979 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3980 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3981 case EL_DYNABOMB_INCREASE_NUMBER:
3982 case EL_DYNABOMB_INCREASE_SIZE:
3983 case EL_DYNABOMB_INCREASE_POWER:
3988 case EL_LAMP_ACTIVE:
3990 case EL_AMOEBA_TO_DIAMOND:
3992 if (IS_PLAYER(x, y))
3993 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3995 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3999 if (element_info[element].explosion_type == EXPLODES_CROSS)
4001 if (CAN_EXPLODE_CROSS(element))
4004 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4009 else if (element_info[element].explosion_type == EXPLODES_1X1)
4011 else if (CAN_EXPLODE_1X1(element))
4013 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4015 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4019 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
4022 void SplashAcid(int x, int y)
4025 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4026 (!IN_LEV_FIELD(x - 1, y - 2) ||
4027 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4028 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4030 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4031 (!IN_LEV_FIELD(x + 1, y - 2) ||
4032 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4033 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4035 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4037 /* input: position of element entering acid (obsolete) */
4039 int element = Feld[x][y];
4041 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4044 if (element != EL_ACID_SPLASH_LEFT &&
4045 element != EL_ACID_SPLASH_RIGHT)
4047 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4049 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4050 (!IN_LEV_FIELD(x - 1, y - 1) ||
4051 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4052 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4054 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4055 (!IN_LEV_FIELD(x + 1, y - 1) ||
4056 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4057 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4062 static void InitBeltMovement()
4064 static int belt_base_element[4] =
4066 EL_CONVEYOR_BELT_1_LEFT,
4067 EL_CONVEYOR_BELT_2_LEFT,
4068 EL_CONVEYOR_BELT_3_LEFT,
4069 EL_CONVEYOR_BELT_4_LEFT
4071 static int belt_base_active_element[4] =
4073 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4074 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4075 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4076 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4081 /* set frame order for belt animation graphic according to belt direction */
4082 for (i = 0; i < NUM_BELTS; i++)
4086 for (j = 0; j < NUM_BELT_PARTS; j++)
4088 int element = belt_base_active_element[belt_nr] + j;
4089 int graphic = el2img(element);
4091 if (game.belt_dir[i] == MV_LEFT)
4092 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4094 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4098 for (y = 0; y < lev_fieldy; y++)
4100 for (x = 0; x < lev_fieldx; x++)
4102 int element = Feld[x][y];
4104 for (i = 0; i < NUM_BELTS; i++)
4106 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4108 int e_belt_nr = getBeltNrFromBeltElement(element);
4111 if (e_belt_nr == belt_nr)
4113 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4115 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4123 static void ToggleBeltSwitch(int x, int y)
4125 static int belt_base_element[4] =
4127 EL_CONVEYOR_BELT_1_LEFT,
4128 EL_CONVEYOR_BELT_2_LEFT,
4129 EL_CONVEYOR_BELT_3_LEFT,
4130 EL_CONVEYOR_BELT_4_LEFT
4132 static int belt_base_active_element[4] =
4134 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4135 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4136 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4137 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4139 static int belt_base_switch_element[4] =
4141 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4142 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4143 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4144 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4146 static int belt_move_dir[4] =
4154 int element = Feld[x][y];
4155 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4156 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4157 int belt_dir = belt_move_dir[belt_dir_nr];
4160 if (!IS_BELT_SWITCH(element))
4163 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4164 game.belt_dir[belt_nr] = belt_dir;
4166 if (belt_dir_nr == 3)
4169 /* set frame order for belt animation graphic according to belt direction */
4170 for (i = 0; i < NUM_BELT_PARTS; i++)
4172 int element = belt_base_active_element[belt_nr] + i;
4173 int graphic = el2img(element);
4175 if (belt_dir == MV_LEFT)
4176 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4178 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4181 for (yy = 0; yy < lev_fieldy; yy++)
4183 for (xx = 0; xx < lev_fieldx; xx++)
4185 int element = Feld[xx][yy];
4187 if (IS_BELT_SWITCH(element))
4189 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4191 if (e_belt_nr == belt_nr)
4193 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4194 DrawLevelField(xx, yy);
4197 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4199 int e_belt_nr = getBeltNrFromBeltElement(element);
4201 if (e_belt_nr == belt_nr)
4203 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4205 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4206 DrawLevelField(xx, yy);
4209 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4211 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4213 if (e_belt_nr == belt_nr)
4215 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4217 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4218 DrawLevelField(xx, yy);
4225 static void ToggleSwitchgateSwitch(int x, int y)
4229 game.switchgate_pos = !game.switchgate_pos;
4231 for (yy = 0; yy < lev_fieldy; yy++)
4233 for (xx = 0; xx < lev_fieldx; xx++)
4235 int element = Feld[xx][yy];
4237 if (element == EL_SWITCHGATE_SWITCH_UP ||
4238 element == EL_SWITCHGATE_SWITCH_DOWN)
4240 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4241 DrawLevelField(xx, yy);
4243 else if (element == EL_SWITCHGATE_OPEN ||
4244 element == EL_SWITCHGATE_OPENING)
4246 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4248 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4250 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4253 else if (element == EL_SWITCHGATE_CLOSED ||
4254 element == EL_SWITCHGATE_CLOSING)
4256 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4258 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4260 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4267 static int getInvisibleActiveFromInvisibleElement(int element)
4269 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4270 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4271 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4275 static int getInvisibleFromInvisibleActiveElement(int element)
4277 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4278 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4279 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4283 static void RedrawAllLightSwitchesAndInvisibleElements()
4287 for (y = 0; y < lev_fieldy; y++)
4289 for (x = 0; x < lev_fieldx; x++)
4291 int element = Feld[x][y];
4293 if (element == EL_LIGHT_SWITCH &&
4294 game.light_time_left > 0)
4296 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4297 DrawLevelField(x, y);
4299 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4300 game.light_time_left == 0)
4302 Feld[x][y] = EL_LIGHT_SWITCH;
4303 DrawLevelField(x, y);
4305 else if (element == EL_INVISIBLE_STEELWALL ||
4306 element == EL_INVISIBLE_WALL ||
4307 element == EL_INVISIBLE_SAND)
4309 if (game.light_time_left > 0)
4310 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4312 DrawLevelField(x, y);
4314 /* uncrumble neighbour fields, if needed */
4315 if (element == EL_INVISIBLE_SAND)
4316 DrawLevelFieldCrumbledSandNeighbours(x, y);
4318 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4319 element == EL_INVISIBLE_WALL_ACTIVE ||
4320 element == EL_INVISIBLE_SAND_ACTIVE)
4322 if (game.light_time_left == 0)
4323 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4325 DrawLevelField(x, y);
4327 /* re-crumble neighbour fields, if needed */
4328 if (element == EL_INVISIBLE_SAND)
4329 DrawLevelFieldCrumbledSandNeighbours(x, y);
4335 static void ToggleLightSwitch(int x, int y)
4337 int element = Feld[x][y];
4339 game.light_time_left =
4340 (element == EL_LIGHT_SWITCH ?
4341 level.time_light * FRAMES_PER_SECOND : 0);
4343 RedrawAllLightSwitchesAndInvisibleElements();
4346 static void ActivateTimegateSwitch(int x, int y)
4350 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4352 for (yy = 0; yy < lev_fieldy; yy++)
4354 for (xx = 0; xx < lev_fieldx; xx++)
4356 int element = Feld[xx][yy];
4358 if (element == EL_TIMEGATE_CLOSED ||
4359 element == EL_TIMEGATE_CLOSING)
4361 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4362 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4366 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4368 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4369 DrawLevelField(xx, yy);
4376 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4379 void Impact(int x, int y)
4381 boolean lastline = (y == lev_fieldy-1);
4382 boolean object_hit = FALSE;
4383 boolean impact = (lastline || object_hit);
4384 int element = Feld[x][y];
4385 int smashed = EL_STEELWALL;
4387 if (!lastline) /* check if element below was hit */
4389 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4392 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4393 MovDir[x][y + 1] != MV_DOWN ||
4394 MovPos[x][y + 1] <= TILEY / 2));
4397 object_hit = !IS_FREE(x, y + 1);
4400 /* do not smash moving elements that left the smashed field in time */
4401 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4402 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4406 smashed = MovingOrBlocked2Element(x, y + 1);
4408 impact = (lastline || object_hit);
4411 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4413 SplashAcid(x, y + 1);
4417 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4418 /* only reset graphic animation if graphic really changes after impact */
4420 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4422 ResetGfxAnimation(x, y);
4423 DrawLevelField(x, y);
4426 if (impact && CAN_EXPLODE_IMPACT(element))
4431 else if (impact && element == EL_PEARL)
4433 ResetGfxAnimation(x, y);
4435 Feld[x][y] = EL_PEARL_BREAKING;
4436 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4439 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4441 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4446 if (impact && element == EL_AMOEBA_DROP)
4448 if (object_hit && IS_PLAYER(x, y + 1))
4449 KillHeroUnlessEnemyProtected(x, y + 1);
4450 else if (object_hit && smashed == EL_PENGUIN)
4454 Feld[x][y] = EL_AMOEBA_GROWING;
4455 Store[x][y] = EL_AMOEBA_WET;
4457 ResetRandomAnimationValue(x, y);
4462 if (object_hit) /* check which object was hit */
4464 if (CAN_PASS_MAGIC_WALL(element) &&
4465 (smashed == EL_MAGIC_WALL ||
4466 smashed == EL_BD_MAGIC_WALL))
4469 int activated_magic_wall =
4470 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4471 EL_BD_MAGIC_WALL_ACTIVE);
4473 /* activate magic wall / mill */
4474 for (yy = 0; yy < lev_fieldy; yy++)
4475 for (xx = 0; xx < lev_fieldx; xx++)
4476 if (Feld[xx][yy] == smashed)
4477 Feld[xx][yy] = activated_magic_wall;
4479 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4480 game.magic_wall_active = TRUE;
4482 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4483 SND_MAGIC_WALL_ACTIVATING :
4484 SND_BD_MAGIC_WALL_ACTIVATING));
4487 if (IS_PLAYER(x, y + 1))
4489 if (CAN_SMASH_PLAYER(element))
4491 KillHeroUnlessEnemyProtected(x, y + 1);
4495 else if (smashed == EL_PENGUIN)
4497 if (CAN_SMASH_PLAYER(element))
4503 else if (element == EL_BD_DIAMOND)
4505 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4511 else if (((element == EL_SP_INFOTRON ||
4512 element == EL_SP_ZONK) &&
4513 (smashed == EL_SP_SNIKSNAK ||
4514 smashed == EL_SP_ELECTRON ||
4515 smashed == EL_SP_DISK_ORANGE)) ||
4516 (element == EL_SP_INFOTRON &&
4517 smashed == EL_SP_DISK_YELLOW))
4523 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4529 else if (CAN_SMASH_EVERYTHING(element))
4531 if (IS_CLASSIC_ENEMY(smashed) ||
4532 CAN_EXPLODE_SMASHED(smashed))
4537 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4539 if (smashed == EL_LAMP ||
4540 smashed == EL_LAMP_ACTIVE)
4545 else if (smashed == EL_NUT)
4547 Feld[x][y + 1] = EL_NUT_BREAKING;
4548 PlayLevelSound(x, y, SND_NUT_BREAKING);
4549 RaiseScoreElement(EL_NUT);
4552 else if (smashed == EL_PEARL)
4554 ResetGfxAnimation(x, y);
4556 Feld[x][y + 1] = EL_PEARL_BREAKING;
4557 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4560 else if (smashed == EL_DIAMOND)
4562 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4563 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4566 else if (IS_BELT_SWITCH(smashed))
4568 ToggleBeltSwitch(x, y + 1);
4570 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4571 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4573 ToggleSwitchgateSwitch(x, y + 1);
4575 else if (smashed == EL_LIGHT_SWITCH ||
4576 smashed == EL_LIGHT_SWITCH_ACTIVE)
4578 ToggleLightSwitch(x, y + 1);
4583 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4586 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4589 /* !!! TEST ONLY !!! */
4590 CheckElementChangeBySide(x, y + 1, smashed, element,
4591 CE_SWITCHED, CH_SIDE_TOP);
4592 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4593 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4595 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4596 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4597 CheckElementChangeBySide(x, y + 1, smashed, element,
4598 CE_SWITCHED, CH_SIDE_TOP);
4604 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4609 /* play sound of magic wall / mill */
4611 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4612 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4614 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4615 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4616 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4617 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4622 /* play sound of object that hits the ground */
4623 if (lastline || object_hit)
4624 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4627 inline static void TurnRoundExt(int x, int y)
4639 { 0, 0 }, { 0, 0 }, { 0, 0 },
4644 int left, right, back;
4648 { MV_DOWN, MV_UP, MV_RIGHT },
4649 { MV_UP, MV_DOWN, MV_LEFT },
4651 { MV_LEFT, MV_RIGHT, MV_DOWN },
4655 { MV_RIGHT, MV_LEFT, MV_UP }
4658 int element = Feld[x][y];
4659 int move_pattern = element_info[element].move_pattern;
4661 int old_move_dir = MovDir[x][y];
4662 int left_dir = turn[old_move_dir].left;
4663 int right_dir = turn[old_move_dir].right;
4664 int back_dir = turn[old_move_dir].back;
4666 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4667 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4668 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4669 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4671 int left_x = x + left_dx, left_y = y + left_dy;
4672 int right_x = x + right_dx, right_y = y + right_dy;
4673 int move_x = x + move_dx, move_y = y + move_dy;
4677 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4679 TestIfBadThingTouchesOtherBadThing(x, y);
4681 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4682 MovDir[x][y] = right_dir;
4683 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4684 MovDir[x][y] = left_dir;
4686 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4688 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4692 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4693 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4695 TestIfBadThingTouchesOtherBadThing(x, y);
4697 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4698 MovDir[x][y] = left_dir;
4699 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4700 MovDir[x][y] = right_dir;
4702 if ((element == EL_SPACESHIP ||
4703 element == EL_SP_SNIKSNAK ||
4704 element == EL_SP_ELECTRON)
4705 && MovDir[x][y] != old_move_dir)
4707 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4711 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4713 TestIfBadThingTouchesOtherBadThing(x, y);
4715 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4716 MovDir[x][y] = left_dir;
4717 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4718 MovDir[x][y] = right_dir;
4720 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4722 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4725 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4727 TestIfBadThingTouchesOtherBadThing(x, y);
4729 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4730 MovDir[x][y] = left_dir;
4731 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4732 MovDir[x][y] = right_dir;
4734 if (MovDir[x][y] != old_move_dir)
4738 else if (element == EL_YAMYAM)
4740 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4741 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4743 if (can_turn_left && can_turn_right)
4744 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4745 else if (can_turn_left)
4746 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4747 else if (can_turn_right)
4748 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4750 MovDir[x][y] = back_dir;
4752 MovDelay[x][y] = 16 + 16 * RND(3);
4754 else if (element == EL_DARK_YAMYAM)
4756 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4758 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4761 if (can_turn_left && can_turn_right)
4762 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4763 else if (can_turn_left)
4764 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4765 else if (can_turn_right)
4766 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4768 MovDir[x][y] = back_dir;
4770 MovDelay[x][y] = 16 + 16 * RND(3);
4772 else if (element == EL_PACMAN)
4774 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4775 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4777 if (can_turn_left && can_turn_right)
4778 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4779 else if (can_turn_left)
4780 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4781 else if (can_turn_right)
4782 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4784 MovDir[x][y] = back_dir;
4786 MovDelay[x][y] = 6 + RND(40);
4788 else if (element == EL_PIG)
4790 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4791 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4792 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4793 boolean should_turn_left, should_turn_right, should_move_on;
4795 int rnd = RND(rnd_value);
4797 should_turn_left = (can_turn_left &&
4799 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4800 y + back_dy + left_dy)));
4801 should_turn_right = (can_turn_right &&
4803 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4804 y + back_dy + right_dy)));
4805 should_move_on = (can_move_on &&
4808 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4809 y + move_dy + left_dy) ||
4810 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4811 y + move_dy + right_dy)));
4813 if (should_turn_left || should_turn_right || should_move_on)
4815 if (should_turn_left && should_turn_right && should_move_on)
4816 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4817 rnd < 2 * rnd_value / 3 ? right_dir :
4819 else if (should_turn_left && should_turn_right)
4820 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4821 else if (should_turn_left && should_move_on)
4822 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4823 else if (should_turn_right && should_move_on)
4824 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4825 else if (should_turn_left)
4826 MovDir[x][y] = left_dir;
4827 else if (should_turn_right)
4828 MovDir[x][y] = right_dir;
4829 else if (should_move_on)
4830 MovDir[x][y] = old_move_dir;
4832 else if (can_move_on && rnd > rnd_value / 8)
4833 MovDir[x][y] = old_move_dir;
4834 else if (can_turn_left && can_turn_right)
4835 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4836 else if (can_turn_left && rnd > rnd_value / 8)
4837 MovDir[x][y] = left_dir;
4838 else if (can_turn_right && rnd > rnd_value/8)
4839 MovDir[x][y] = right_dir;
4841 MovDir[x][y] = back_dir;
4843 xx = x + move_xy[MovDir[x][y]].x;
4844 yy = y + move_xy[MovDir[x][y]].y;
4847 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4848 if (!IN_LEV_FIELD(xx, yy) ||
4849 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4850 MovDir[x][y] = old_move_dir;
4852 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4853 MovDir[x][y] = old_move_dir;
4858 else if (element == EL_DRAGON)
4860 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4861 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4862 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4864 int rnd = RND(rnd_value);
4867 if (FrameCounter < 1 && x == 0 && y == 29)
4868 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4871 if (can_move_on && rnd > rnd_value / 8)
4872 MovDir[x][y] = old_move_dir;
4873 else if (can_turn_left && can_turn_right)
4874 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4875 else if (can_turn_left && rnd > rnd_value / 8)
4876 MovDir[x][y] = left_dir;
4877 else if (can_turn_right && rnd > rnd_value / 8)
4878 MovDir[x][y] = right_dir;
4880 MovDir[x][y] = back_dir;
4882 xx = x + move_xy[MovDir[x][y]].x;
4883 yy = y + move_xy[MovDir[x][y]].y;
4886 if (FrameCounter < 1 && x == 0 && y == 29)
4887 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4888 xx, yy, Feld[xx][yy],
4893 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4894 MovDir[x][y] = old_move_dir;
4896 if (!IS_FREE(xx, yy))
4897 MovDir[x][y] = old_move_dir;
4901 if (FrameCounter < 1 && x == 0 && y == 29)
4902 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4907 else if (element == EL_MOLE)
4909 boolean can_move_on =
4910 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4911 IS_AMOEBOID(Feld[move_x][move_y]) ||
4912 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4915 boolean can_turn_left =
4916 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4917 IS_AMOEBOID(Feld[left_x][left_y])));
4919 boolean can_turn_right =
4920 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4921 IS_AMOEBOID(Feld[right_x][right_y])));
4923 if (can_turn_left && can_turn_right)
4924 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4925 else if (can_turn_left)
4926 MovDir[x][y] = left_dir;
4928 MovDir[x][y] = right_dir;
4931 if (MovDir[x][y] != old_move_dir)
4934 else if (element == EL_BALLOON)
4936 MovDir[x][y] = game.balloon_dir;
4939 else if (element == EL_SPRING)
4942 if (MovDir[x][y] & MV_HORIZONTAL &&
4943 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4944 MovDir[x][y] = MV_NO_MOVING;
4946 if (MovDir[x][y] & MV_HORIZONTAL &&
4947 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4948 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4949 MovDir[x][y] = MV_NO_MOVING;
4954 else if (element == EL_ROBOT ||
4955 element == EL_SATELLITE ||
4956 element == EL_PENGUIN)
4958 int attr_x = -1, attr_y = -1;
4969 for (i = 0; i < MAX_PLAYERS; i++)
4971 struct PlayerInfo *player = &stored_player[i];
4972 int jx = player->jx, jy = player->jy;
4974 if (!player->active)
4978 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4987 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4988 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4989 game.engine_version < VERSION_IDENT(3,1,0,0)))
4991 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4998 if (element == EL_PENGUIN)
5001 static int xy[4][2] =
5009 for (i = 0; i < NUM_DIRECTIONS; i++)
5011 int ex = x + xy[i][0];
5012 int ey = y + xy[i][1];
5014 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5023 MovDir[x][y] = MV_NO_MOVING;
5025 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5026 else if (attr_x > x)
5027 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5029 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5030 else if (attr_y > y)
5031 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5033 if (element == EL_ROBOT)
5037 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5038 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5039 Moving2Blocked(x, y, &newx, &newy);
5041 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5042 MovDelay[x][y] = 8 + 8 * !RND(3);
5044 MovDelay[x][y] = 16;
5046 else if (element == EL_PENGUIN)
5052 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5054 boolean first_horiz = RND(2);
5055 int new_move_dir = MovDir[x][y];
5058 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5059 Moving2Blocked(x, y, &newx, &newy);
5061 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5065 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5066 Moving2Blocked(x, y, &newx, &newy);
5068 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5071 MovDir[x][y] = old_move_dir;
5075 else /* (element == EL_SATELLITE) */
5081 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5083 boolean first_horiz = RND(2);
5084 int new_move_dir = MovDir[x][y];
5087 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5088 Moving2Blocked(x, y, &newx, &newy);
5090 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5094 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5095 Moving2Blocked(x, y, &newx, &newy);
5097 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5100 MovDir[x][y] = old_move_dir;
5105 else if (move_pattern == MV_TURNING_LEFT ||
5106 move_pattern == MV_TURNING_RIGHT ||
5107 move_pattern == MV_TURNING_LEFT_RIGHT ||
5108 move_pattern == MV_TURNING_RIGHT_LEFT ||
5109 move_pattern == MV_TURNING_RANDOM ||
5110 move_pattern == MV_ALL_DIRECTIONS)
5112 boolean can_turn_left =
5113 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5114 boolean can_turn_right =
5115 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5117 #if USE_CAN_MOVE_NOT_MOVING
5118 if (element_info[element].move_stepsize == 0) /* not moving */
5122 if (move_pattern == MV_TURNING_LEFT)
5123 MovDir[x][y] = left_dir;
5124 else if (move_pattern == MV_TURNING_RIGHT)
5125 MovDir[x][y] = right_dir;
5126 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5127 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5128 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5129 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5130 else if (move_pattern == MV_TURNING_RANDOM)
5131 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5132 can_turn_right && !can_turn_left ? right_dir :
5133 RND(2) ? left_dir : right_dir);
5134 else if (can_turn_left && can_turn_right)
5135 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5136 else if (can_turn_left)
5137 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5138 else if (can_turn_right)
5139 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5141 MovDir[x][y] = back_dir;
5143 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5145 else if (move_pattern == MV_HORIZONTAL ||
5146 move_pattern == MV_VERTICAL)
5148 if (move_pattern & old_move_dir)
5149 MovDir[x][y] = back_dir;
5150 else if (move_pattern == MV_HORIZONTAL)
5151 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5152 else if (move_pattern == MV_VERTICAL)
5153 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5155 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5157 else if (move_pattern & MV_ANY_DIRECTION)
5159 MovDir[x][y] = move_pattern;
5160 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5162 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5164 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5165 MovDir[x][y] = left_dir;
5166 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5167 MovDir[x][y] = right_dir;
5169 if (MovDir[x][y] != old_move_dir)
5170 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5172 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5174 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5175 MovDir[x][y] = right_dir;
5176 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5177 MovDir[x][y] = left_dir;
5179 if (MovDir[x][y] != old_move_dir)
5180 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5182 else if (move_pattern == MV_TOWARDS_PLAYER ||
5183 move_pattern == MV_AWAY_FROM_PLAYER)
5185 int attr_x = -1, attr_y = -1;
5187 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5198 for (i = 0; i < MAX_PLAYERS; i++)
5200 struct PlayerInfo *player = &stored_player[i];
5201 int jx = player->jx, jy = player->jy;
5203 if (!player->active)
5207 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5215 MovDir[x][y] = MV_NO_MOVING;
5217 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5218 else if (attr_x > x)
5219 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5221 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5222 else if (attr_y > y)
5223 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5225 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5227 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5229 boolean first_horiz = RND(2);
5230 int new_move_dir = MovDir[x][y];
5232 #if USE_CAN_MOVE_NOT_MOVING
5233 if (element_info[element].move_stepsize == 0) /* not moving */
5235 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5236 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5243 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5244 Moving2Blocked(x, y, &newx, &newy);
5246 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5250 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5251 Moving2Blocked(x, y, &newx, &newy);
5253 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5256 MovDir[x][y] = old_move_dir;
5259 else if (move_pattern == MV_WHEN_PUSHED ||
5260 move_pattern == MV_WHEN_DROPPED)
5262 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5263 MovDir[x][y] = MV_NO_MOVING;
5267 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5269 static int test_xy[7][2] =
5279 static int test_dir[7] =
5289 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5290 int move_preference = -1000000; /* start with very low preference */
5291 int new_move_dir = MV_NO_MOVING;
5292 int start_test = RND(4);
5295 for (i = 0; i < NUM_DIRECTIONS; i++)
5297 int move_dir = test_dir[start_test + i];
5298 int move_dir_preference;
5300 xx = x + test_xy[start_test + i][0];
5301 yy = y + test_xy[start_test + i][1];
5303 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5304 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5306 new_move_dir = move_dir;
5311 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5314 move_dir_preference = -1 * RunnerVisit[xx][yy];
5315 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5316 move_dir_preference = PlayerVisit[xx][yy];
5318 if (move_dir_preference > move_preference)
5320 /* prefer field that has not been visited for the longest time */
5321 move_preference = move_dir_preference;
5322 new_move_dir = move_dir;
5324 else if (move_dir_preference == move_preference &&
5325 move_dir == old_move_dir)
5327 /* prefer last direction when all directions are preferred equally */
5328 move_preference = move_dir_preference;
5329 new_move_dir = move_dir;
5333 MovDir[x][y] = new_move_dir;
5334 if (old_move_dir != new_move_dir)
5337 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5345 static void TurnRound(int x, int y)
5347 int direction = MovDir[x][y];
5350 GfxDir[x][y] = MovDir[x][y];
5356 GfxDir[x][y] = MovDir[x][y];
5359 if (direction != MovDir[x][y])
5364 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5367 GfxAction[x][y] = ACTION_WAITING;
5371 static boolean JustBeingPushed(int x, int y)
5375 for (i = 0; i < MAX_PLAYERS; i++)
5377 struct PlayerInfo *player = &stored_player[i];
5379 if (player->active && player->is_pushing && player->MovPos)
5381 int next_jx = player->jx + (player->jx - player->last_jx);
5382 int next_jy = player->jy + (player->jy - player->last_jy);
5384 if (x == next_jx && y == next_jy)
5392 void StartMoving(int x, int y)
5395 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5397 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5398 int element = Feld[x][y];
5404 if (MovDelay[x][y] == 0)
5405 GfxAction[x][y] = ACTION_DEFAULT;
5407 /* !!! this should be handled more generic (not only for mole) !!! */
5408 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5409 GfxAction[x][y] = ACTION_DEFAULT;
5412 if (CAN_FALL(element) && y < lev_fieldy - 1)
5414 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5415 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5416 if (JustBeingPushed(x, y))
5419 if (element == EL_QUICKSAND_FULL)
5421 if (IS_FREE(x, y + 1))
5423 InitMovingField(x, y, MV_DOWN);
5424 started_moving = TRUE;
5426 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5427 Store[x][y] = EL_ROCK;
5429 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5431 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5434 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5436 if (!MovDelay[x][y])
5437 MovDelay[x][y] = TILEY + 1;
5446 Feld[x][y] = EL_QUICKSAND_EMPTY;
5447 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5448 Store[x][y + 1] = Store[x][y];
5451 PlayLevelSoundAction(x, y, ACTION_FILLING);
5453 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5457 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5458 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5460 InitMovingField(x, y, MV_DOWN);
5461 started_moving = TRUE;
5463 Feld[x][y] = EL_QUICKSAND_FILLING;
5464 Store[x][y] = element;
5466 PlayLevelSoundAction(x, y, ACTION_FILLING);
5468 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5471 else if (element == EL_MAGIC_WALL_FULL)
5473 if (IS_FREE(x, y + 1))
5475 InitMovingField(x, y, MV_DOWN);
5476 started_moving = TRUE;
5478 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5479 Store[x][y] = EL_CHANGED(Store[x][y]);
5481 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5483 if (!MovDelay[x][y])
5484 MovDelay[x][y] = TILEY/4 + 1;
5493 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5494 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5495 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5499 else if (element == EL_BD_MAGIC_WALL_FULL)
5501 if (IS_FREE(x, y + 1))
5503 InitMovingField(x, y, MV_DOWN);
5504 started_moving = TRUE;
5506 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5507 Store[x][y] = EL_CHANGED2(Store[x][y]);
5509 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5511 if (!MovDelay[x][y])
5512 MovDelay[x][y] = TILEY/4 + 1;
5521 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5522 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5523 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5527 else if (CAN_PASS_MAGIC_WALL(element) &&
5528 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5529 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5531 InitMovingField(x, y, MV_DOWN);
5532 started_moving = TRUE;
5535 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5536 EL_BD_MAGIC_WALL_FILLING);
5537 Store[x][y] = element;
5540 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5542 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5545 SplashAcid(x, y + 1);
5547 InitMovingField(x, y, MV_DOWN);
5548 started_moving = TRUE;
5550 Store[x][y] = EL_ACID;
5552 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5553 GfxAction[x][y + 1] = ACTION_ACTIVE;
5557 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5558 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5560 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5561 CAN_SMASH(element) && WasJustFalling[x][y] &&
5562 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5564 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5565 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5566 (Feld[x][y + 1] == EL_BLOCKED)))
5570 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5571 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5572 WasJustMoving[x][y] && !Pushed[x][y + 1])
5574 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5575 WasJustMoving[x][y])
5580 /* this is needed for a special case not covered by calling "Impact()"
5581 from "ContinueMoving()": if an element moves to a tile directly below
5582 another element which was just falling on that tile (which was empty
5583 in the previous frame), the falling element above would just stop
5584 instead of smashing the element below (in previous version, the above
5585 element was just checked for "moving" instead of "falling", resulting
5586 in incorrect smashes caused by horizontal movement of the above
5587 element; also, the case of the player being the element to smash was
5588 simply not covered here... :-/ ) */
5591 WasJustMoving[x][y] = 0;
5592 WasJustFalling[x][y] = 0;
5595 CheckCollision[x][y] = 0;
5598 if (IS_PLAYER(x, y + 1))
5599 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5604 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5606 if (MovDir[x][y] == MV_NO_MOVING)
5608 InitMovingField(x, y, MV_DOWN);
5609 started_moving = TRUE;
5612 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5614 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5615 MovDir[x][y] = MV_DOWN;
5617 InitMovingField(x, y, MV_DOWN);
5618 started_moving = TRUE;
5620 else if (element == EL_AMOEBA_DROP)
5622 Feld[x][y] = EL_AMOEBA_GROWING;
5623 Store[x][y] = EL_AMOEBA_WET;
5625 /* Store[x][y + 1] must be zero, because:
5626 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5629 #if OLD_GAME_BEHAVIOUR
5630 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5632 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5633 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5634 element != EL_DX_SUPABOMB)
5637 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5638 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5639 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5640 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5643 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5644 (IS_FREE(x - 1, y + 1) ||
5645 Feld[x - 1][y + 1] == EL_ACID));
5646 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5647 (IS_FREE(x + 1, y + 1) ||
5648 Feld[x + 1][y + 1] == EL_ACID));
5649 boolean can_fall_any = (can_fall_left || can_fall_right);
5650 boolean can_fall_both = (can_fall_left && can_fall_right);
5652 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5654 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5656 if (slippery_type == SLIPPERY_ONLY_LEFT)
5657 can_fall_right = FALSE;
5658 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5659 can_fall_left = FALSE;
5660 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5661 can_fall_right = FALSE;
5662 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5663 can_fall_left = FALSE;
5665 can_fall_any = (can_fall_left || can_fall_right);
5666 can_fall_both = (can_fall_left && can_fall_right);
5669 #if USE_NEW_SP_SLIPPERY
5670 /* !!! better use the same properties as for custom elements here !!! */
5671 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5672 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5674 can_fall_right = FALSE; /* slip down on left side */
5675 can_fall_both = FALSE;
5682 if (game.emulation == EMU_BOULDERDASH ||
5683 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5684 can_fall_right = FALSE; /* slip down on left side */
5686 can_fall_left = !(can_fall_right = RND(2));
5688 can_fall_both = FALSE;
5695 if (can_fall_both &&
5696 (game.emulation != EMU_BOULDERDASH &&
5697 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5698 can_fall_left = !(can_fall_right = RND(2));
5701 /* if not determined otherwise, prefer left side for slipping down */
5702 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5703 started_moving = TRUE;
5707 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5709 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5712 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5713 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5714 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5715 int belt_dir = game.belt_dir[belt_nr];
5717 if ((belt_dir == MV_LEFT && left_is_free) ||
5718 (belt_dir == MV_RIGHT && right_is_free))
5721 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5724 InitMovingField(x, y, belt_dir);
5725 started_moving = TRUE;
5728 Pushed[x][y] = TRUE;
5729 Pushed[nextx][y] = TRUE;
5732 GfxAction[x][y] = ACTION_DEFAULT;
5736 MovDir[x][y] = 0; /* if element was moving, stop it */
5741 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5743 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5745 if (CAN_MOVE(element) && !started_moving)
5748 int move_pattern = element_info[element].move_pattern;
5753 if (MovDir[x][y] == MV_NO_MOVING)
5755 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5756 x, y, element, element_info[element].token_name);
5757 printf("StartMoving(): This should never happen!\n");
5762 Moving2Blocked(x, y, &newx, &newy);
5765 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5768 if ((element == EL_SATELLITE ||
5769 element == EL_BALLOON ||
5770 element == EL_SPRING)
5771 && JustBeingPushed(x, y))
5778 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5779 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5781 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5782 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5783 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5787 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5788 element, element_info[element].token_name,
5789 WasJustMoving[x][y],
5790 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5791 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5792 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5793 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5797 WasJustMoving[x][y] = 0;
5800 CheckCollision[x][y] = 0;
5802 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5805 if (Feld[x][y] != element) /* element has changed */
5807 element = Feld[x][y];
5808 move_pattern = element_info[element].move_pattern;
5810 if (!CAN_MOVE(element))
5814 if (Feld[x][y] != element) /* element has changed */
5822 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5823 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5825 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5827 Moving2Blocked(x, y, &newx, &newy);
5828 if (Feld[newx][newy] == EL_BLOCKED)
5829 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5835 if (FrameCounter < 1 && x == 0 && y == 29)
5836 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5839 if (!MovDelay[x][y]) /* start new movement phase */
5841 /* all objects that can change their move direction after each step
5842 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5844 if (element != EL_YAMYAM &&
5845 element != EL_DARK_YAMYAM &&
5846 element != EL_PACMAN &&
5847 !(move_pattern & MV_ANY_DIRECTION) &&
5848 move_pattern != MV_TURNING_LEFT &&
5849 move_pattern != MV_TURNING_RIGHT &&
5850 move_pattern != MV_TURNING_LEFT_RIGHT &&
5851 move_pattern != MV_TURNING_RIGHT_LEFT &&
5852 move_pattern != MV_TURNING_RANDOM)
5857 if (FrameCounter < 1 && x == 0 && y == 29)
5858 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5861 if (MovDelay[x][y] && (element == EL_BUG ||
5862 element == EL_SPACESHIP ||
5863 element == EL_SP_SNIKSNAK ||
5864 element == EL_SP_ELECTRON ||
5865 element == EL_MOLE))
5866 DrawLevelField(x, y);
5870 if (MovDelay[x][y]) /* wait some time before next movement */
5875 if (element == EL_YAMYAM)
5878 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5879 DrawLevelElementAnimation(x, y, element);
5883 if (MovDelay[x][y]) /* element still has to wait some time */
5886 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5887 ResetGfxAnimation(x, y);
5891 if (GfxAction[x][y] != ACTION_WAITING)
5892 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5894 GfxAction[x][y] = ACTION_WAITING;
5898 if (element == EL_ROBOT ||
5900 element == EL_PACMAN ||
5902 element == EL_YAMYAM ||
5903 element == EL_DARK_YAMYAM)
5906 DrawLevelElementAnimation(x, y, element);
5908 DrawLevelElementAnimationIfNeeded(x, y, element);
5910 PlayLevelSoundAction(x, y, ACTION_WAITING);
5912 else if (element == EL_SP_ELECTRON)
5913 DrawLevelElementAnimationIfNeeded(x, y, element);
5914 else if (element == EL_DRAGON)
5917 int dir = MovDir[x][y];
5918 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5919 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5920 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5921 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5922 dir == MV_UP ? IMG_FLAMES_1_UP :
5923 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5924 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5927 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5930 GfxAction[x][y] = ACTION_ATTACKING;
5932 if (IS_PLAYER(x, y))
5933 DrawPlayerField(x, y);
5935 DrawLevelField(x, y);
5937 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5939 for (i = 1; i <= 3; i++)
5941 int xx = x + i * dx;
5942 int yy = y + i * dy;
5943 int sx = SCREENX(xx);
5944 int sy = SCREENY(yy);
5945 int flame_graphic = graphic + (i - 1);
5947 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5952 int flamed = MovingOrBlocked2Element(xx, yy);
5956 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5958 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5959 RemoveMovingField(xx, yy);
5961 RemoveField(xx, yy);
5963 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5966 RemoveMovingField(xx, yy);
5970 if (ChangeDelay[xx][yy])
5971 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5972 Feld[xx][yy] == EL_BLOCKED));
5976 ChangeDelay[xx][yy] = 0;
5978 Feld[xx][yy] = EL_FLAMES;
5979 if (IN_SCR_FIELD(sx, sy))
5981 DrawLevelFieldCrumbledSand(xx, yy);
5982 DrawGraphic(sx, sy, flame_graphic, frame);
5987 if (Feld[xx][yy] == EL_FLAMES)
5988 Feld[xx][yy] = EL_EMPTY;
5989 DrawLevelField(xx, yy);
5994 if (MovDelay[x][y]) /* element still has to wait some time */
5996 PlayLevelSoundAction(x, y, ACTION_WAITING);
6002 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6003 for all other elements GfxAction will be set by InitMovingField() */
6004 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6005 GfxAction[x][y] = ACTION_MOVING;
6009 /* now make next step */
6011 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6013 if (DONT_COLLIDE_WITH(element) &&
6014 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6015 !PLAYER_ENEMY_PROTECTED(newx, newy))
6018 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6022 /* player killed by element which is deadly when colliding with */
6024 KillHero(PLAYERINFO(newx, newy));
6031 else if (CAN_MOVE_INTO_ACID(element) &&
6032 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6033 (MovDir[x][y] == MV_DOWN ||
6034 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6036 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6037 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6041 else if ((element == EL_PENGUIN ||
6042 element == EL_ROBOT ||
6043 element == EL_SATELLITE ||
6044 element == EL_BALLOON ||
6045 IS_CUSTOM_ELEMENT(element)) &&
6046 IN_LEV_FIELD(newx, newy) &&
6047 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6050 SplashAcid(newx, newy);
6051 Store[x][y] = EL_ACID;
6053 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6055 if (Feld[newx][newy] == EL_EXIT_OPEN)
6059 DrawLevelField(x, y);
6061 Feld[x][y] = EL_EMPTY;
6062 DrawLevelField(x, y);
6065 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6066 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6067 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6069 local_player->friends_still_needed--;
6070 if (!local_player->friends_still_needed &&
6071 !local_player->GameOver && AllPlayersGone)
6072 local_player->LevelSolved = local_player->GameOver = TRUE;
6076 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6078 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6079 DrawLevelField(newx, newy);
6081 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6083 else if (!IS_FREE(newx, newy))
6085 GfxAction[x][y] = ACTION_WAITING;
6087 if (IS_PLAYER(x, y))
6088 DrawPlayerField(x, y);
6090 DrawLevelField(x, y);
6095 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6097 if (IS_FOOD_PIG(Feld[newx][newy]))
6099 if (IS_MOVING(newx, newy))
6100 RemoveMovingField(newx, newy);
6103 Feld[newx][newy] = EL_EMPTY;
6104 DrawLevelField(newx, newy);
6107 PlayLevelSound(x, y, SND_PIG_DIGGING);
6109 else if (!IS_FREE(newx, newy))
6111 if (IS_PLAYER(x, y))
6112 DrawPlayerField(x, y);
6114 DrawLevelField(x, y);
6123 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6126 else if (IS_CUSTOM_ELEMENT(element) &&
6127 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6131 !IS_FREE(newx, newy)
6136 int new_element = Feld[newx][newy];
6139 printf("::: '%s' digs '%s' [%d]\n",
6140 element_info[element].token_name,
6141 element_info[Feld[newx][newy]].token_name,
6142 StorePlayer[newx][newy]);
6145 if (!IS_FREE(newx, newy))
6147 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6148 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6151 /* no element can dig solid indestructible elements */
6152 if (IS_INDESTRUCTIBLE(new_element) &&
6153 !IS_DIGGABLE(new_element) &&
6154 !IS_COLLECTIBLE(new_element))
6157 if (AmoebaNr[newx][newy] &&
6158 (new_element == EL_AMOEBA_FULL ||
6159 new_element == EL_BD_AMOEBA ||
6160 new_element == EL_AMOEBA_GROWING))
6162 AmoebaCnt[AmoebaNr[newx][newy]]--;
6163 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6166 if (IS_MOVING(newx, newy))
6167 RemoveMovingField(newx, newy);
6170 RemoveField(newx, newy);
6171 DrawLevelField(newx, newy);
6174 /* if digged element was about to explode, prevent the explosion */
6175 ExplodeField[newx][newy] = EX_TYPE_NONE;
6177 PlayLevelSoundAction(x, y, action);
6182 Store[newx][newy] = EL_EMPTY;
6183 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6184 Store[newx][newy] = element_info[element].move_leave_element;
6186 Store[newx][newy] = EL_EMPTY;
6187 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6188 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6189 Store[newx][newy] = element_info[element].move_leave_element;
6192 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6193 element_info[element].can_leave_element = TRUE;
6196 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6198 RunnerVisit[x][y] = FrameCounter;
6199 PlayerVisit[x][y] /= 8; /* expire player visit path */
6205 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6207 if (!IS_FREE(newx, newy))
6209 if (IS_PLAYER(x, y))
6210 DrawPlayerField(x, y);
6212 DrawLevelField(x, y);
6218 boolean wanna_flame = !RND(10);
6219 int dx = newx - x, dy = newy - y;
6220 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6221 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6222 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6223 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6224 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6225 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6228 IS_CLASSIC_ENEMY(element1) ||
6229 IS_CLASSIC_ENEMY(element2)) &&
6230 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6231 element1 != EL_FLAMES && element2 != EL_FLAMES)
6234 ResetGfxAnimation(x, y);
6235 GfxAction[x][y] = ACTION_ATTACKING;
6238 if (IS_PLAYER(x, y))
6239 DrawPlayerField(x, y);
6241 DrawLevelField(x, y);
6243 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6245 MovDelay[x][y] = 50;
6249 RemoveField(newx, newy);
6251 Feld[newx][newy] = EL_FLAMES;
6252 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6255 RemoveField(newx1, newy1);
6257 Feld[newx1][newy1] = EL_FLAMES;
6259 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6262 RemoveField(newx2, newy2);
6264 Feld[newx2][newy2] = EL_FLAMES;
6271 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6272 Feld[newx][newy] == EL_DIAMOND)
6274 if (IS_MOVING(newx, newy))
6275 RemoveMovingField(newx, newy);
6278 Feld[newx][newy] = EL_EMPTY;
6279 DrawLevelField(newx, newy);
6282 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6284 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6285 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6287 if (AmoebaNr[newx][newy])
6289 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6290 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6291 Feld[newx][newy] == EL_BD_AMOEBA)
6292 AmoebaCnt[AmoebaNr[newx][newy]]--;
6297 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6299 if (IS_MOVING(newx, newy))
6302 RemoveMovingField(newx, newy);
6306 Feld[newx][newy] = EL_EMPTY;
6307 DrawLevelField(newx, newy);
6310 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6312 else if ((element == EL_PACMAN || element == EL_MOLE)
6313 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6315 if (AmoebaNr[newx][newy])
6317 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6318 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6319 Feld[newx][newy] == EL_BD_AMOEBA)
6320 AmoebaCnt[AmoebaNr[newx][newy]]--;
6323 if (element == EL_MOLE)
6325 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6326 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6328 ResetGfxAnimation(x, y);
6329 GfxAction[x][y] = ACTION_DIGGING;
6330 DrawLevelField(x, y);
6332 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6334 return; /* wait for shrinking amoeba */
6336 else /* element == EL_PACMAN */
6338 Feld[newx][newy] = EL_EMPTY;
6339 DrawLevelField(newx, newy);
6340 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6343 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6344 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6345 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6347 /* wait for shrinking amoeba to completely disappear */
6350 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6352 /* object was running against a wall */
6357 if (move_pattern & MV_ANY_DIRECTION &&
6358 move_pattern == MovDir[x][y])
6360 int blocking_element =
6361 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6364 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6365 element_info[element].token_name,
6366 element_info[blocking_element].token_name,
6370 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6373 element = Feld[x][y]; /* element might have changed */
6378 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6379 DrawLevelElementAnimation(x, y, element);
6381 if (element == EL_BUG ||
6382 element == EL_SPACESHIP ||
6383 element == EL_SP_SNIKSNAK)
6384 DrawLevelField(x, y);
6385 else if (element == EL_MOLE)
6386 DrawLevelField(x, y);
6387 else if (element == EL_BD_BUTTERFLY ||
6388 element == EL_BD_FIREFLY)
6389 DrawLevelElementAnimationIfNeeded(x, y, element);
6390 else if (element == EL_SATELLITE)
6391 DrawLevelElementAnimationIfNeeded(x, y, element);
6392 else if (element == EL_SP_ELECTRON)
6393 DrawLevelElementAnimationIfNeeded(x, y, element);
6396 if (DONT_TOUCH(element))
6397 TestIfBadThingTouchesHero(x, y);
6400 PlayLevelSoundAction(x, y, ACTION_WAITING);
6406 InitMovingField(x, y, MovDir[x][y]);
6408 PlayLevelSoundAction(x, y, ACTION_MOVING);
6412 ContinueMoving(x, y);
6415 void ContinueMoving(int x, int y)
6417 int element = Feld[x][y];
6418 int stored = Store[x][y];
6419 struct ElementInfo *ei = &element_info[element];
6420 int direction = MovDir[x][y];
6421 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6422 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6423 int newx = x + dx, newy = y + dy;
6425 int nextx = newx + dx, nexty = newy + dy;
6428 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6429 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6431 boolean pushed_by_player = Pushed[x][y];
6434 MovPos[x][y] += getElementMoveStepsize(x, y);
6437 if (pushed_by_player && IS_PLAYER(x, y))
6439 /* special case: moving object pushed by player */
6440 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6443 if (pushed_by_player) /* special case: moving object pushed by player */
6444 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6447 if (ABS(MovPos[x][y]) < TILEX)
6449 DrawLevelField(x, y);
6451 return; /* element is still moving */
6454 /* element reached destination field */
6456 Feld[x][y] = EL_EMPTY;
6457 Feld[newx][newy] = element;
6458 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6461 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6463 element = Feld[newx][newy] = EL_ACID;
6466 else if (element == EL_MOLE)
6468 Feld[x][y] = EL_SAND;
6470 DrawLevelFieldCrumbledSandNeighbours(x, y);
6472 else if (element == EL_QUICKSAND_FILLING)
6474 element = Feld[newx][newy] = get_next_element(element);
6475 Store[newx][newy] = Store[x][y];
6477 else if (element == EL_QUICKSAND_EMPTYING)
6479 Feld[x][y] = get_next_element(element);
6480 element = Feld[newx][newy] = Store[x][y];
6482 else if (element == EL_MAGIC_WALL_FILLING)
6484 element = Feld[newx][newy] = get_next_element(element);
6485 if (!game.magic_wall_active)
6486 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6487 Store[newx][newy] = Store[x][y];
6489 else if (element == EL_MAGIC_WALL_EMPTYING)
6491 Feld[x][y] = get_next_element(element);
6492 if (!game.magic_wall_active)
6493 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6494 element = Feld[newx][newy] = Store[x][y];
6496 else if (element == EL_BD_MAGIC_WALL_FILLING)
6498 element = Feld[newx][newy] = get_next_element(element);
6499 if (!game.magic_wall_active)
6500 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6501 Store[newx][newy] = Store[x][y];
6503 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6505 Feld[x][y] = get_next_element(element);
6506 if (!game.magic_wall_active)
6507 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6508 element = Feld[newx][newy] = Store[x][y];
6510 else if (element == EL_AMOEBA_DROPPING)
6512 Feld[x][y] = get_next_element(element);
6513 element = Feld[newx][newy] = Store[x][y];
6515 else if (element == EL_SOKOBAN_OBJECT)
6518 Feld[x][y] = Back[x][y];
6520 if (Back[newx][newy])
6521 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6523 Back[x][y] = Back[newx][newy] = 0;
6526 else if (Store[x][y] == EL_ACID)
6528 element = Feld[newx][newy] = EL_ACID;
6532 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6533 ei->move_leave_element != EL_EMPTY &&
6534 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6535 Store[x][y] != EL_EMPTY))
6537 /* some elements can leave other elements behind after moving */
6539 Feld[x][y] = ei->move_leave_element;
6540 InitField(x, y, FALSE);
6542 if (GFX_CRUMBLED(Feld[x][y]))
6543 DrawLevelFieldCrumbledSandNeighbours(x, y);
6547 Store[x][y] = EL_EMPTY;
6551 MovDelay[newx][newy] = 0;
6553 if (CAN_CHANGE(element))
6555 /* copy element change control values to new field */
6556 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6557 ChangePage[newx][newy] = ChangePage[x][y];
6558 Changed[newx][newy] = Changed[x][y];
6559 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6562 ChangeDelay[x][y] = 0;
6563 ChangePage[x][y] = -1;
6564 Changed[x][y] = CE_BITMASK_DEFAULT;
6565 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6567 /* copy animation control values to new field */
6568 GfxFrame[newx][newy] = GfxFrame[x][y];
6569 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6570 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6571 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6573 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6576 /* do this after checking for left-behind element */
6577 ResetGfxAnimation(x, y); /* reset animation values for old field */
6581 /* some elements can leave other elements behind after moving */
6583 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6584 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6585 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6587 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6588 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6592 int move_leave_element = ei->move_leave_element;
6594 Feld[x][y] = move_leave_element;
6596 #if USE_PREVIOUS_MOVE_DIR
6597 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6598 MovDir[x][y] = direction;
6601 InitField(x, y, FALSE);
6603 if (GFX_CRUMBLED(Feld[x][y]))
6604 DrawLevelFieldCrumbledSandNeighbours(x, y);
6606 if (ELEM_IS_PLAYER(move_leave_element))
6607 RelocatePlayer(x, y, move_leave_element);
6612 /* some elements can leave other elements behind after moving */
6613 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6614 ei->move_leave_element != EL_EMPTY &&
6615 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6616 ei->can_leave_element_last))
6618 Feld[x][y] = ei->move_leave_element;
6619 InitField(x, y, FALSE);
6621 if (GFX_CRUMBLED(Feld[x][y]))
6622 DrawLevelFieldCrumbledSandNeighbours(x, y);
6625 ei->can_leave_element_last = ei->can_leave_element;
6626 ei->can_leave_element = FALSE;
6630 /* do this after checking for left-behind element */
6631 ResetGfxAnimation(x, y); /* reset animation values for old field */
6635 /* 2.1.1 (does not work correctly for spring) */
6636 if (!CAN_MOVE(element))
6637 MovDir[newx][newy] = 0;
6641 /* (does not work for falling objects that slide horizontally) */
6642 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6643 MovDir[newx][newy] = 0;
6646 if (!CAN_MOVE(element) ||
6647 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6648 MovDir[newx][newy] = 0;
6652 if (!CAN_MOVE(element) ||
6653 (CAN_FALL(element) && direction == MV_DOWN))
6654 GfxDir[x][y] = MovDir[newx][newy] = 0;
6656 if (!CAN_MOVE(element) ||
6657 (CAN_FALL(element) && direction == MV_DOWN &&
6658 (element == EL_SPRING ||
6659 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6660 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6661 GfxDir[x][y] = MovDir[newx][newy] = 0;
6667 DrawLevelField(x, y);
6668 DrawLevelField(newx, newy);
6670 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6672 /* prevent pushed element from moving on in pushed direction */
6673 if (pushed_by_player && CAN_MOVE(element) &&
6674 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6675 !(element_info[element].move_pattern & direction))
6676 TurnRound(newx, newy);
6679 /* prevent elements on conveyor belt from moving on in last direction */
6680 if (pushed_by_conveyor && CAN_FALL(element) &&
6681 direction & MV_HORIZONTAL)
6684 if (CAN_MOVE(element))
6685 InitMovDir(newx, newy);
6687 MovDir[newx][newy] = 0;
6689 MovDir[newx][newy] = 0;
6694 if (!pushed_by_player)
6696 int nextx = newx + dx, nexty = newy + dy;
6697 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6699 WasJustMoving[newx][newy] = 3;
6701 if (CAN_FALL(element) && direction == MV_DOWN)
6702 WasJustFalling[newx][newy] = 3;
6704 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6705 CheckCollision[newx][newy] = 2;
6708 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6710 TestIfBadThingTouchesHero(newx, newy);
6711 TestIfBadThingTouchesFriend(newx, newy);
6713 if (!IS_CUSTOM_ELEMENT(element))
6714 TestIfBadThingTouchesOtherBadThing(newx, newy);
6716 else if (element == EL_PENGUIN)
6717 TestIfFriendTouchesBadThing(newx, newy);
6719 #if USE_NEW_MOVE_STYLE
6721 if (CAN_FALL(element) && direction == MV_DOWN &&
6722 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6723 IS_PLAYER(x, newy + 1))
6724 printf("::: we would now kill the player [%d]\n", FrameCounter);
6727 /* give the player one last chance (one more frame) to move away */
6728 if (CAN_FALL(element) && direction == MV_DOWN &&
6729 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6730 (!IS_PLAYER(x, newy + 1) ||
6731 game.engine_version < VERSION_IDENT(3,1,1,0)))
6734 if (CAN_FALL(element) && direction == MV_DOWN &&
6735 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6743 if (pushed_by_player && !game.use_change_when_pushing_bug)
6745 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6748 if (pushed_by_player)
6753 int dig_side = MV_DIR_OPPOSITE(direction);
6755 static int trigger_sides[4] =
6757 CH_SIDE_RIGHT, /* moving left */
6758 CH_SIDE_LEFT, /* moving right */
6759 CH_SIDE_BOTTOM, /* moving up */
6760 CH_SIDE_TOP, /* moving down */
6762 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6764 struct PlayerInfo *player = PLAYERINFO(x, y);
6766 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6767 player->index_bit, dig_side);
6768 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6769 player->index_bit, dig_side);
6774 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6778 if (ChangePage[newx][newy] != -1) /* delayed change */
6779 ChangeElement(newx, newy, ChangePage[newx][newy]);
6784 TestIfElementHitsCustomElement(newx, newy, direction);
6788 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6790 int hitting_element = Feld[newx][newy];
6792 /* !!! fix side (direction) orientation here and elsewhere !!! */
6793 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6797 if (IN_LEV_FIELD(nextx, nexty))
6799 int opposite_direction = MV_DIR_OPPOSITE(direction);
6800 int hitting_side = direction;
6801 int touched_side = opposite_direction;
6802 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6803 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6804 MovDir[nextx][nexty] != direction ||
6805 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6811 CheckElementChangeBySide(nextx, nexty, touched_element,
6812 CE_HIT_BY_SOMETHING, opposite_direction);
6814 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6815 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6817 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6819 struct ElementChangeInfo *change =
6820 &element_info[hitting_element].change_page[i];
6822 if (change->can_change &&
6823 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6824 change->trigger_side & touched_side &&
6825 change->trigger_element == touched_element)
6827 CheckElementChangeByPage(newx, newy, hitting_element,
6828 touched_element, CE_OTHER_IS_HITTING,i);
6834 if (IS_CUSTOM_ELEMENT(touched_element) &&
6835 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6837 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6839 struct ElementChangeInfo *change =
6840 &element_info[touched_element].change_page[i];
6842 if (change->can_change &&
6843 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6844 change->trigger_side & hitting_side &&
6845 change->trigger_element == hitting_element)
6847 CheckElementChangeByPage(nextx, nexty, touched_element,
6848 hitting_element, CE_OTHER_GETS_HIT, i);
6859 TestIfPlayerTouchesCustomElement(newx, newy);
6860 TestIfElementTouchesCustomElement(newx, newy);
6863 int AmoebeNachbarNr(int ax, int ay)
6866 int element = Feld[ax][ay];
6868 static int xy[4][2] =
6876 for (i = 0; i < NUM_DIRECTIONS; i++)
6878 int x = ax + xy[i][0];
6879 int y = ay + xy[i][1];
6881 if (!IN_LEV_FIELD(x, y))
6884 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6885 group_nr = AmoebaNr[x][y];
6891 void AmoebenVereinigen(int ax, int ay)
6893 int i, x, y, xx, yy;
6894 int new_group_nr = AmoebaNr[ax][ay];
6895 static int xy[4][2] =
6903 if (new_group_nr == 0)
6906 for (i = 0; i < NUM_DIRECTIONS; i++)
6911 if (!IN_LEV_FIELD(x, y))
6914 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6915 Feld[x][y] == EL_BD_AMOEBA ||
6916 Feld[x][y] == EL_AMOEBA_DEAD) &&
6917 AmoebaNr[x][y] != new_group_nr)
6919 int old_group_nr = AmoebaNr[x][y];
6921 if (old_group_nr == 0)
6924 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6925 AmoebaCnt[old_group_nr] = 0;
6926 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6927 AmoebaCnt2[old_group_nr] = 0;
6929 for (yy = 0; yy < lev_fieldy; yy++)
6931 for (xx = 0; xx < lev_fieldx; xx++)
6933 if (AmoebaNr[xx][yy] == old_group_nr)
6934 AmoebaNr[xx][yy] = new_group_nr;
6941 void AmoebeUmwandeln(int ax, int ay)
6945 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6947 int group_nr = AmoebaNr[ax][ay];
6952 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6953 printf("AmoebeUmwandeln(): This should never happen!\n");
6958 for (y = 0; y < lev_fieldy; y++)
6960 for (x = 0; x < lev_fieldx; x++)
6962 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6965 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6969 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6970 SND_AMOEBA_TURNING_TO_GEM :
6971 SND_AMOEBA_TURNING_TO_ROCK));
6976 static int xy[4][2] =
6984 for (i = 0; i < NUM_DIRECTIONS; i++)
6989 if (!IN_LEV_FIELD(x, y))
6992 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6994 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6995 SND_AMOEBA_TURNING_TO_GEM :
6996 SND_AMOEBA_TURNING_TO_ROCK));
7003 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7006 int group_nr = AmoebaNr[ax][ay];
7007 boolean done = FALSE;
7012 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7013 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7018 for (y = 0; y < lev_fieldy; y++)
7020 for (x = 0; x < lev_fieldx; x++)
7022 if (AmoebaNr[x][y] == group_nr &&
7023 (Feld[x][y] == EL_AMOEBA_DEAD ||
7024 Feld[x][y] == EL_BD_AMOEBA ||
7025 Feld[x][y] == EL_AMOEBA_GROWING))
7028 Feld[x][y] = new_element;
7029 InitField(x, y, FALSE);
7030 DrawLevelField(x, y);
7037 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7038 SND_BD_AMOEBA_TURNING_TO_ROCK :
7039 SND_BD_AMOEBA_TURNING_TO_GEM));
7042 void AmoebeWaechst(int x, int y)
7044 static unsigned long sound_delay = 0;
7045 static unsigned long sound_delay_value = 0;
7047 if (!MovDelay[x][y]) /* start new growing cycle */
7051 if (DelayReached(&sound_delay, sound_delay_value))
7054 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7056 if (Store[x][y] == EL_BD_AMOEBA)
7057 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7059 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7061 sound_delay_value = 30;
7065 if (MovDelay[x][y]) /* wait some time before growing bigger */
7068 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7070 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7071 6 - MovDelay[x][y]);
7073 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7076 if (!MovDelay[x][y])
7078 Feld[x][y] = Store[x][y];
7080 DrawLevelField(x, y);
7085 void AmoebaDisappearing(int x, int y)
7087 static unsigned long sound_delay = 0;
7088 static unsigned long sound_delay_value = 0;
7090 if (!MovDelay[x][y]) /* start new shrinking cycle */
7094 if (DelayReached(&sound_delay, sound_delay_value))
7095 sound_delay_value = 30;
7098 if (MovDelay[x][y]) /* wait some time before shrinking */
7101 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7103 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7104 6 - MovDelay[x][y]);
7106 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7109 if (!MovDelay[x][y])
7111 Feld[x][y] = EL_EMPTY;
7112 DrawLevelField(x, y);
7114 /* don't let mole enter this field in this cycle;
7115 (give priority to objects falling to this field from above) */
7121 void AmoebeAbleger(int ax, int ay)
7124 int element = Feld[ax][ay];
7125 int graphic = el2img(element);
7126 int newax = ax, neway = ay;
7127 static int xy[4][2] =
7135 if (!level.amoeba_speed)
7137 Feld[ax][ay] = EL_AMOEBA_DEAD;
7138 DrawLevelField(ax, ay);
7142 if (IS_ANIMATED(graphic))
7143 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7145 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7146 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7148 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7151 if (MovDelay[ax][ay])
7155 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7158 int x = ax + xy[start][0];
7159 int y = ay + xy[start][1];
7161 if (!IN_LEV_FIELD(x, y))
7165 if (IS_FREE(x, y) ||
7166 CAN_GROW_INTO(Feld[x][y]) ||
7167 Feld[x][y] == EL_QUICKSAND_EMPTY)
7173 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7174 if (IS_FREE(x, y) ||
7175 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7182 if (newax == ax && neway == ay)
7185 else /* normal or "filled" (BD style) amoeba */
7188 boolean waiting_for_player = FALSE;
7190 for (i = 0; i < NUM_DIRECTIONS; i++)
7192 int j = (start + i) % 4;
7193 int x = ax + xy[j][0];
7194 int y = ay + xy[j][1];
7196 if (!IN_LEV_FIELD(x, y))
7200 if (IS_FREE(x, y) ||
7201 CAN_GROW_INTO(Feld[x][y]) ||
7202 Feld[x][y] == EL_QUICKSAND_EMPTY)
7209 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7210 if (IS_FREE(x, y) ||
7211 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7218 else if (IS_PLAYER(x, y))
7219 waiting_for_player = TRUE;
7222 if (newax == ax && neway == ay) /* amoeba cannot grow */
7225 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7227 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7230 Feld[ax][ay] = EL_AMOEBA_DEAD;
7231 DrawLevelField(ax, ay);
7232 AmoebaCnt[AmoebaNr[ax][ay]]--;
7234 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7236 if (element == EL_AMOEBA_FULL)
7237 AmoebeUmwandeln(ax, ay);
7238 else if (element == EL_BD_AMOEBA)
7239 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7244 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7246 /* amoeba gets larger by growing in some direction */
7248 int new_group_nr = AmoebaNr[ax][ay];
7251 if (new_group_nr == 0)
7253 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7254 printf("AmoebeAbleger(): This should never happen!\n");
7259 AmoebaNr[newax][neway] = new_group_nr;
7260 AmoebaCnt[new_group_nr]++;
7261 AmoebaCnt2[new_group_nr]++;
7263 /* if amoeba touches other amoeba(s) after growing, unify them */
7264 AmoebenVereinigen(newax, neway);
7266 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7268 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7274 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7275 (neway == lev_fieldy - 1 && newax != ax))
7277 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7278 Store[newax][neway] = element;
7280 else if (neway == ay)
7282 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7284 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7286 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7291 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7292 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7293 Store[ax][ay] = EL_AMOEBA_DROP;
7294 ContinueMoving(ax, ay);
7298 DrawLevelField(newax, neway);
7301 void Life(int ax, int ay)
7304 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7306 int element = Feld[ax][ay];
7307 int graphic = el2img(element);
7308 boolean changed = FALSE;
7310 if (IS_ANIMATED(graphic))
7311 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7316 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7317 MovDelay[ax][ay] = life_time;
7319 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7322 if (MovDelay[ax][ay])
7326 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7328 int xx = ax+x1, yy = ay+y1;
7331 if (!IN_LEV_FIELD(xx, yy))
7334 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7336 int x = xx+x2, y = yy+y2;
7338 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7341 if (((Feld[x][y] == element ||
7342 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7344 (IS_FREE(x, y) && Stop[x][y]))
7348 if (xx == ax && yy == ay) /* field in the middle */
7350 if (nachbarn < life[0] || nachbarn > life[1])
7352 Feld[xx][yy] = EL_EMPTY;
7354 DrawLevelField(xx, yy);
7355 Stop[xx][yy] = TRUE;
7360 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7361 { /* free border field */
7362 if (nachbarn >= life[2] && nachbarn <= life[3])
7364 Feld[xx][yy] = element;
7365 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7367 DrawLevelField(xx, yy);
7368 Stop[xx][yy] = TRUE;
7373 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7374 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7375 { /* free border field */
7376 if (nachbarn >= life[2] && nachbarn <= life[3])
7378 Feld[xx][yy] = element;
7379 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7381 DrawLevelField(xx, yy);
7382 Stop[xx][yy] = TRUE;
7390 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7391 SND_GAME_OF_LIFE_GROWING);
7394 static void InitRobotWheel(int x, int y)
7396 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7399 static void RunRobotWheel(int x, int y)
7401 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7404 static void StopRobotWheel(int x, int y)
7406 if (ZX == x && ZY == y)
7410 static void InitTimegateWheel(int x, int y)
7413 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7415 /* another brainless, "type style" bug ... :-( */
7416 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7420 static void RunTimegateWheel(int x, int y)
7422 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7425 void CheckExit(int x, int y)
7427 if (local_player->gems_still_needed > 0 ||
7428 local_player->sokobanfields_still_needed > 0 ||
7429 local_player->lights_still_needed > 0)
7431 int element = Feld[x][y];
7432 int graphic = el2img(element);
7434 if (IS_ANIMATED(graphic))
7435 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7440 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7443 Feld[x][y] = EL_EXIT_OPENING;
7445 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7448 void CheckExitSP(int x, int y)
7450 if (local_player->gems_still_needed > 0)
7452 int element = Feld[x][y];
7453 int graphic = el2img(element);
7455 if (IS_ANIMATED(graphic))
7456 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7461 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7464 Feld[x][y] = EL_SP_EXIT_OPENING;
7466 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7469 static void CloseAllOpenTimegates()
7473 for (y = 0; y < lev_fieldy; y++)
7475 for (x = 0; x < lev_fieldx; x++)
7477 int element = Feld[x][y];
7479 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7481 Feld[x][y] = EL_TIMEGATE_CLOSING;
7483 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7485 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7492 void EdelsteinFunkeln(int x, int y)
7494 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7497 if (Feld[x][y] == EL_BD_DIAMOND)
7500 if (MovDelay[x][y] == 0) /* next animation frame */
7501 MovDelay[x][y] = 11 * !SimpleRND(500);
7503 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7507 if (setup.direct_draw && MovDelay[x][y])
7508 SetDrawtoField(DRAW_BUFFERED);
7510 DrawLevelElementAnimation(x, y, Feld[x][y]);
7512 if (MovDelay[x][y] != 0)
7514 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7515 10 - MovDelay[x][y]);
7517 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7519 if (setup.direct_draw)
7523 dest_x = FX + SCREENX(x) * TILEX;
7524 dest_y = FY + SCREENY(y) * TILEY;
7526 BlitBitmap(drawto_field, window,
7527 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7528 SetDrawtoField(DRAW_DIRECT);
7534 void MauerWaechst(int x, int y)
7538 if (!MovDelay[x][y]) /* next animation frame */
7539 MovDelay[x][y] = 3 * delay;
7541 if (MovDelay[x][y]) /* wait some time before next frame */
7545 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7547 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7548 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7550 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7553 if (!MovDelay[x][y])
7555 if (MovDir[x][y] == MV_LEFT)
7557 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7558 DrawLevelField(x - 1, y);
7560 else if (MovDir[x][y] == MV_RIGHT)
7562 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7563 DrawLevelField(x + 1, y);
7565 else if (MovDir[x][y] == MV_UP)
7567 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7568 DrawLevelField(x, y - 1);
7572 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7573 DrawLevelField(x, y + 1);
7576 Feld[x][y] = Store[x][y];
7578 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7579 DrawLevelField(x, y);
7584 void MauerAbleger(int ax, int ay)
7586 int element = Feld[ax][ay];
7587 int graphic = el2img(element);
7588 boolean oben_frei = FALSE, unten_frei = FALSE;
7589 boolean links_frei = FALSE, rechts_frei = FALSE;
7590 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7591 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7592 boolean new_wall = FALSE;
7594 if (IS_ANIMATED(graphic))
7595 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7597 if (!MovDelay[ax][ay]) /* start building new wall */
7598 MovDelay[ax][ay] = 6;
7600 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7603 if (MovDelay[ax][ay])
7607 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7609 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7611 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7613 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7616 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7617 element == EL_EXPANDABLE_WALL_ANY)
7621 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7622 Store[ax][ay-1] = element;
7623 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7624 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7625 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7626 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7631 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7632 Store[ax][ay+1] = element;
7633 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7634 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7635 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7636 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7641 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7642 element == EL_EXPANDABLE_WALL_ANY ||
7643 element == EL_EXPANDABLE_WALL)
7647 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7648 Store[ax-1][ay] = element;
7649 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7650 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7651 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7652 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7658 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7659 Store[ax+1][ay] = element;
7660 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7661 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7662 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7663 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7668 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7669 DrawLevelField(ax, ay);
7671 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7673 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7674 unten_massiv = TRUE;
7675 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7676 links_massiv = TRUE;
7677 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7678 rechts_massiv = TRUE;
7680 if (((oben_massiv && unten_massiv) ||
7681 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7682 element == EL_EXPANDABLE_WALL) &&
7683 ((links_massiv && rechts_massiv) ||
7684 element == EL_EXPANDABLE_WALL_VERTICAL))
7685 Feld[ax][ay] = EL_WALL;
7689 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7691 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7695 void CheckForDragon(int x, int y)
7698 boolean dragon_found = FALSE;
7699 static int xy[4][2] =
7707 for (i = 0; i < NUM_DIRECTIONS; i++)
7709 for (j = 0; j < 4; j++)
7711 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7713 if (IN_LEV_FIELD(xx, yy) &&
7714 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7716 if (Feld[xx][yy] == EL_DRAGON)
7717 dragon_found = TRUE;
7726 for (i = 0; i < NUM_DIRECTIONS; i++)
7728 for (j = 0; j < 3; j++)
7730 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7732 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7734 Feld[xx][yy] = EL_EMPTY;
7735 DrawLevelField(xx, yy);
7744 static void InitBuggyBase(int x, int y)
7746 int element = Feld[x][y];
7747 int activating_delay = FRAMES_PER_SECOND / 4;
7750 (element == EL_SP_BUGGY_BASE ?
7751 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7752 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7754 element == EL_SP_BUGGY_BASE_ACTIVE ?
7755 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7758 static void WarnBuggyBase(int x, int y)
7761 static int xy[4][2] =
7769 for (i = 0; i < NUM_DIRECTIONS; i++)
7771 int xx = x + xy[i][0], yy = y + xy[i][1];
7773 if (IS_PLAYER(xx, yy))
7775 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7782 static void InitTrap(int x, int y)
7784 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7787 static void ActivateTrap(int x, int y)
7789 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7792 static void ChangeActiveTrap(int x, int y)
7794 int graphic = IMG_TRAP_ACTIVE;
7796 /* if new animation frame was drawn, correct crumbled sand border */
7797 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7798 DrawLevelFieldCrumbledSand(x, y);
7801 static void ChangeElementNowExt(int x, int y, int target_element)
7803 int previous_move_direction = MovDir[x][y];
7805 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7806 IS_WALKABLE(Feld[x][y]));
7808 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7809 IS_WALKABLE(Feld[x][y]) &&
7813 /* check if element under player changes from accessible to unaccessible
7814 (needed for special case of dropping element which then changes) */
7815 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7816 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7819 printf("::: BOOOM! [%d, '%s']\n", target_element,
7820 element_info[target_element].token_name);
7832 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7833 RemoveMovingField(x, y);
7837 Feld[x][y] = target_element;
7840 Feld[x][y] = target_element;
7843 ResetGfxAnimation(x, y);
7844 ResetRandomAnimationValue(x, y);
7846 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7847 MovDir[x][y] = previous_move_direction;
7850 InitField_WithBug1(x, y, FALSE);
7852 InitField(x, y, FALSE);
7853 if (CAN_MOVE(Feld[x][y]))
7857 DrawLevelField(x, y);
7859 if (GFX_CRUMBLED(Feld[x][y]))
7860 DrawLevelFieldCrumbledSandNeighbours(x, y);
7864 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7868 TestIfBadThingTouchesHero(x, y);
7869 TestIfPlayerTouchesCustomElement(x, y);
7870 TestIfElementTouchesCustomElement(x, y);
7873 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7874 if (ELEM_IS_PLAYER(target_element))
7875 RelocatePlayer(x, y, target_element);
7878 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7882 TestIfBadThingTouchesHero(x, y);
7883 TestIfPlayerTouchesCustomElement(x, y);
7884 TestIfElementTouchesCustomElement(x, y);
7888 static boolean ChangeElementNow(int x, int y, int element, int page)
7890 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7892 int old_element = Feld[x][y];
7894 /* always use default change event to prevent running into a loop */
7895 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7896 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7898 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7900 /* reset actual trigger element and player */
7901 change->actual_trigger_element = EL_EMPTY;
7902 change->actual_trigger_player = EL_PLAYER_1;
7905 /* do not change already changed elements with same change event */
7907 if (Changed[x][y] & ChangeEvent[x][y])
7914 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7917 /* !!! indirect change before direct change !!! */
7918 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7921 if (change->explode)
7928 if (change->use_target_content)
7930 boolean complete_replace = TRUE;
7931 boolean can_replace[3][3];
7934 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7937 boolean is_walkable;
7938 boolean is_diggable;
7939 boolean is_collectible;
7940 boolean is_removable;
7941 boolean is_destructible;
7942 int ex = x + xx - 1;
7943 int ey = y + yy - 1;
7944 int content_element = change->target_content[xx][yy];
7947 can_replace[xx][yy] = TRUE;
7949 if (ex == x && ey == y) /* do not check changing element itself */
7952 if (content_element == EL_EMPTY_SPACE)
7954 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7959 if (!IN_LEV_FIELD(ex, ey))
7961 can_replace[xx][yy] = FALSE;
7962 complete_replace = FALSE;
7968 if (Changed[ex][ey]) /* do not change already changed elements */
7970 can_replace[xx][yy] = FALSE;
7971 complete_replace = FALSE;
7979 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7980 e = MovingOrBlocked2Element(ex, ey);
7985 is_empty = (IS_FREE(ex, ey) ||
7986 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7987 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7988 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7992 is_empty = (IS_FREE(ex, ey) ||
7993 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7995 is_empty = (IS_FREE(ex, ey) ||
7996 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8001 is_walkable = (is_empty || IS_WALKABLE(e));
8002 is_diggable = (is_empty || IS_DIGGABLE(e));
8003 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8004 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8005 is_removable = (is_diggable || is_collectible);
8007 can_replace[xx][yy] =
8008 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8009 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8010 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8011 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8012 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8013 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8014 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8016 if (!can_replace[xx][yy])
8017 complete_replace = FALSE;
8019 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8020 IS_WALKABLE(content_element)));
8022 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8024 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8027 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8028 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8029 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8031 can_replace[xx][yy] = FALSE;
8032 complete_replace = FALSE;
8037 if (!change->only_if_complete || complete_replace)
8039 boolean something_has_changed = FALSE;
8041 if (change->only_if_complete && change->use_random_replace &&
8042 RND(100) < change->random_percentage)
8045 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8047 int ex = x + xx - 1;
8048 int ey = y + yy - 1;
8049 int content_element;
8051 if (can_replace[xx][yy] && (!change->use_random_replace ||
8052 RND(100) < change->random_percentage))
8054 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8055 RemoveMovingField(ex, ey);
8057 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8059 content_element = change->target_content[xx][yy];
8060 target_element = GET_TARGET_ELEMENT(content_element, change);
8062 ChangeElementNowExt(ex, ey, target_element);
8064 something_has_changed = TRUE;
8066 /* for symmetry reasons, freeze newly created border elements */
8067 if (ex != x || ey != y)
8068 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8072 if (something_has_changed)
8073 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8078 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8080 ChangeElementNowExt(x, y, target_element);
8082 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8086 /* this uses direct change before indirect change */
8087 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
8093 static void ChangeElement(int x, int y, int page)
8095 int element = MovingOrBlocked2Element(x, y);
8096 struct ElementInfo *ei = &element_info[element];
8097 struct ElementChangeInfo *change = &ei->change_page[page];
8100 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8103 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8104 x, y, element, element_info[element].token_name);
8105 printf("ChangeElement(): This should never happen!\n");
8110 /* this can happen with classic bombs on walkable, changing elements */
8111 if (!CAN_CHANGE(element))
8114 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8115 ChangeDelay[x][y] = 0;
8121 if (ChangeDelay[x][y] == 0) /* initialize element change */
8123 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8124 RND(change->delay_random * change->delay_frames)) + 1;
8126 ResetGfxAnimation(x, y);
8127 ResetRandomAnimationValue(x, y);
8129 if (change->pre_change_function)
8130 change->pre_change_function(x, y);
8133 ChangeDelay[x][y]--;
8135 if (ChangeDelay[x][y] != 0) /* continue element change */
8137 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8139 if (IS_ANIMATED(graphic))
8140 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8142 if (change->change_function)
8143 change->change_function(x, y);
8145 else /* finish element change */
8147 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8149 page = ChangePage[x][y];
8150 ChangePage[x][y] = -1;
8152 change = &ei->change_page[page];
8156 if (IS_MOVING(x, y) && !change->explode)
8158 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8161 ChangeDelay[x][y] = 1; /* try change after next move step */
8162 ChangePage[x][y] = page; /* remember page to use for change */
8167 if (ChangeElementNow(x, y, element, page))
8169 if (change->post_change_function)
8170 change->post_change_function(x, y);
8175 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8176 int trigger_element,
8183 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8185 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8188 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8190 int element = EL_CUSTOM_START + i;
8192 boolean change_element = FALSE;
8195 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8198 for (j = 0; j < element_info[element].num_change_pages; j++)
8200 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8202 if (change->can_change &&
8203 change->events & CH_EVENT_BIT(trigger_event) &&
8204 change->trigger_side & trigger_side &&
8205 change->trigger_player & trigger_player &&
8206 change->trigger_page & trigger_page_bits &&
8207 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8210 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8211 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8212 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8215 change_element = TRUE;
8218 change->actual_trigger_element = trigger_element;
8219 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8225 if (!change_element)
8228 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8231 if (x == lx && y == ly) /* do not change trigger element itself */
8235 if (Feld[x][y] == element)
8237 ChangeDelay[x][y] = 1;
8238 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8239 ChangeElement(x, y, page);
8247 static boolean CheckElementChangeExt(int x, int y,
8249 int trigger_element,
8255 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8258 if (Feld[x][y] == EL_BLOCKED)
8260 Blocked2Moving(x, y, &x, &y);
8261 element = Feld[x][y];
8265 if (Feld[x][y] != element) /* check if element has already changed */
8268 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8269 Feld[x][y], element_info[Feld[x][y]].token_name,
8270 element, element_info[element].token_name,
8279 if (trigger_page < 0)
8281 boolean change_element = FALSE;
8284 for (i = 0; i < element_info[element].num_change_pages; i++)
8286 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8288 if (change->can_change &&
8289 change->events & CH_EVENT_BIT(trigger_event) &&
8290 change->trigger_side & trigger_side &&
8291 change->trigger_player & trigger_player)
8293 change_element = TRUE;
8296 change->actual_trigger_element = trigger_element;
8297 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8303 if (!change_element)
8308 struct ElementInfo *ei = &element_info[element];
8309 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8311 change->actual_trigger_element = trigger_element;
8312 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8317 /* !!! this check misses pages with same event, but different side !!! */
8319 if (trigger_page < 0)
8320 trigger_page = element_info[element].event_page_nr[trigger_event];
8322 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8326 ChangeDelay[x][y] = 1;
8327 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8328 ChangeElement(x, y, trigger_page);
8333 static void PlayPlayerSound(struct PlayerInfo *player)
8335 int jx = player->jx, jy = player->jy;
8336 int element = player->element_nr;
8337 int last_action = player->last_action_waiting;
8338 int action = player->action_waiting;
8340 if (player->is_waiting)
8342 if (action != last_action)
8343 PlayLevelSoundElementAction(jx, jy, element, action);
8345 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8349 if (action != last_action)
8350 StopSound(element_info[element].sound[last_action]);
8352 if (last_action == ACTION_SLEEPING)
8353 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8357 static void PlayAllPlayersSound()
8361 for (i = 0; i < MAX_PLAYERS; i++)
8362 if (stored_player[i].active)
8363 PlayPlayerSound(&stored_player[i]);
8366 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8368 boolean last_waiting = player->is_waiting;
8369 int move_dir = player->MovDir;
8371 player->last_action_waiting = player->action_waiting;
8375 if (!last_waiting) /* not waiting -> waiting */
8377 player->is_waiting = TRUE;
8379 player->frame_counter_bored =
8381 game.player_boring_delay_fixed +
8382 SimpleRND(game.player_boring_delay_random);
8383 player->frame_counter_sleeping =
8385 game.player_sleeping_delay_fixed +
8386 SimpleRND(game.player_sleeping_delay_random);
8388 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8391 if (game.player_sleeping_delay_fixed +
8392 game.player_sleeping_delay_random > 0 &&
8393 player->anim_delay_counter == 0 &&
8394 player->post_delay_counter == 0 &&
8395 FrameCounter >= player->frame_counter_sleeping)
8396 player->is_sleeping = TRUE;
8397 else if (game.player_boring_delay_fixed +
8398 game.player_boring_delay_random > 0 &&
8399 FrameCounter >= player->frame_counter_bored)
8400 player->is_bored = TRUE;
8402 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8403 player->is_bored ? ACTION_BORING :
8406 if (player->is_sleeping)
8408 if (player->num_special_action_sleeping > 0)
8410 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8412 int last_special_action = player->special_action_sleeping;
8413 int num_special_action = player->num_special_action_sleeping;
8414 int special_action =
8415 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8416 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8417 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8418 last_special_action + 1 : ACTION_SLEEPING);
8419 int special_graphic =
8420 el_act_dir2img(player->element_nr, special_action, move_dir);
8422 player->anim_delay_counter =
8423 graphic_info[special_graphic].anim_delay_fixed +
8424 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8425 player->post_delay_counter =
8426 graphic_info[special_graphic].post_delay_fixed +
8427 SimpleRND(graphic_info[special_graphic].post_delay_random);
8429 player->special_action_sleeping = special_action;
8432 if (player->anim_delay_counter > 0)
8434 player->action_waiting = player->special_action_sleeping;
8435 player->anim_delay_counter--;
8437 else if (player->post_delay_counter > 0)
8439 player->post_delay_counter--;
8443 else if (player->is_bored)
8445 if (player->num_special_action_bored > 0)
8447 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8449 int special_action =
8450 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8451 int special_graphic =
8452 el_act_dir2img(player->element_nr, special_action, move_dir);
8454 player->anim_delay_counter =
8455 graphic_info[special_graphic].anim_delay_fixed +
8456 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8457 player->post_delay_counter =
8458 graphic_info[special_graphic].post_delay_fixed +
8459 SimpleRND(graphic_info[special_graphic].post_delay_random);
8461 player->special_action_bored = special_action;
8464 if (player->anim_delay_counter > 0)
8466 player->action_waiting = player->special_action_bored;
8467 player->anim_delay_counter--;
8469 else if (player->post_delay_counter > 0)
8471 player->post_delay_counter--;
8476 else if (last_waiting) /* waiting -> not waiting */
8478 player->is_waiting = FALSE;
8479 player->is_bored = FALSE;
8480 player->is_sleeping = FALSE;
8482 player->frame_counter_bored = -1;
8483 player->frame_counter_sleeping = -1;
8485 player->anim_delay_counter = 0;
8486 player->post_delay_counter = 0;
8488 player->action_waiting = ACTION_DEFAULT;
8490 player->special_action_bored = ACTION_DEFAULT;
8491 player->special_action_sleeping = ACTION_DEFAULT;
8496 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8499 static byte stored_player_action[MAX_PLAYERS];
8500 static int num_stored_actions = 0;
8502 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8503 int left = player_action & JOY_LEFT;
8504 int right = player_action & JOY_RIGHT;
8505 int up = player_action & JOY_UP;
8506 int down = player_action & JOY_DOWN;
8507 int button1 = player_action & JOY_BUTTON_1;
8508 int button2 = player_action & JOY_BUTTON_2;
8509 int dx = (left ? -1 : right ? 1 : 0);
8510 int dy = (up ? -1 : down ? 1 : 0);
8513 stored_player_action[player->index_nr] = 0;
8514 num_stored_actions++;
8518 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8521 if (!player->active || tape.pausing)
8525 printf("::: [%d %d %d %d] [%d %d]\n",
8526 left, right, up, down, button1, button2);
8532 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8537 if (player->MovPos == 0)
8538 CheckGravityMovement(player);
8541 snapped = SnapField(player, dx, dy);
8545 dropped = DropElement(player);
8547 moved = MovePlayer(player, dx, dy);
8550 if (tape.single_step && tape.recording && !tape.pausing)
8552 if (button1 || (dropped && !moved))
8554 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8555 SnapField(player, 0, 0); /* stop snapping */
8559 SetPlayerWaiting(player, FALSE);
8562 return player_action;
8564 stored_player_action[player->index_nr] = player_action;
8570 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8573 /* no actions for this player (no input at player's configured device) */
8575 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8576 SnapField(player, 0, 0);
8577 CheckGravityMovementWhenNotMoving(player);
8579 if (player->MovPos == 0)
8580 SetPlayerWaiting(player, TRUE);
8582 if (player->MovPos == 0) /* needed for tape.playing */
8583 player->is_moving = FALSE;
8585 player->is_dropping = FALSE;
8591 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8593 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8595 TapeRecordAction(stored_player_action);
8596 num_stored_actions = 0;
8603 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8605 static byte stored_player_action[MAX_PLAYERS];
8606 static int num_stored_actions = 0;
8607 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8608 int left = player_action & JOY_LEFT;
8609 int right = player_action & JOY_RIGHT;
8610 int up = player_action & JOY_UP;
8611 int down = player_action & JOY_DOWN;
8612 int button1 = player_action & JOY_BUTTON_1;
8613 int button2 = player_action & JOY_BUTTON_2;
8614 int dx = (left ? -1 : right ? 1 : 0);
8615 int dy = (up ? -1 : down ? 1 : 0);
8617 stored_player_action[player->index_nr] = 0;
8618 num_stored_actions++;
8620 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8622 if (!player->active || tape.pausing)
8627 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8630 snapped = SnapField(player, dx, dy);
8634 dropped = DropElement(player);
8636 moved = MovePlayer(player, dx, dy);
8639 if (tape.single_step && tape.recording && !tape.pausing)
8641 if (button1 || (dropped && !moved))
8643 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8644 SnapField(player, 0, 0); /* stop snapping */
8648 stored_player_action[player->index_nr] = player_action;
8652 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8654 /* no actions for this player (no input at player's configured device) */
8656 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8657 SnapField(player, 0, 0);
8658 CheckGravityMovementWhenNotMoving(player);
8660 if (player->MovPos == 0)
8661 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8663 if (player->MovPos == 0) /* needed for tape.playing */
8664 player->is_moving = FALSE;
8667 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8669 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8671 TapeRecordAction(stored_player_action);
8672 num_stored_actions = 0;
8677 void AdvanceFrameAndPlayerCounters(int player_nr)
8681 /* advance frame counters (global frame counter and time frame counter) */
8685 /* advance player counters (counters for move delay, move animation etc.) */
8686 for (i = 0; i < MAX_PLAYERS; i++)
8688 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8690 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8692 if (!advance_player_counters) /* not all players may be affected */
8695 stored_player[i].Frame += move_frames;
8697 if (stored_player[i].MovPos != 0)
8698 stored_player[i].StepFrame += move_frames;
8700 #if USE_NEW_MOVE_DELAY
8701 if (stored_player[i].move_delay > 0)
8702 stored_player[i].move_delay--;
8705 #if USE_NEW_PUSH_DELAY
8706 /* due to bugs in previous versions, counter must count up, not down */
8707 if (stored_player[i].push_delay != -1)
8708 stored_player[i].push_delay++;
8711 if (stored_player[i].drop_delay > 0)
8712 stored_player[i].drop_delay--;
8718 static unsigned long game_frame_delay = 0;
8719 unsigned long game_frame_delay_value;
8720 int magic_wall_x = 0, magic_wall_y = 0;
8721 int i, x, y, element, graphic;
8722 byte *recorded_player_action;
8723 byte summarized_player_action = 0;
8725 byte tape_action[MAX_PLAYERS];
8728 if (game_status != GAME_MODE_PLAYING)
8731 game_frame_delay_value =
8732 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8734 if (tape.playing && tape.warp_forward && !tape.pausing)
8735 game_frame_delay_value = 0;
8737 /* ---------- main game synchronization point ---------- */
8739 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8741 if (network_playing && !network_player_action_received)
8745 printf("DEBUG: try to get network player actions in time\n");
8749 #if defined(NETWORK_AVALIABLE)
8750 /* last chance to get network player actions without main loop delay */
8754 if (game_status != GAME_MODE_PLAYING)
8757 if (!network_player_action_received)
8761 printf("DEBUG: failed to get network player actions in time\n");
8772 printf("::: getting new tape action [%d]\n", FrameCounter);
8775 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8778 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8779 if (recorded_player_action == NULL && tape.pausing)
8784 printf("::: %d\n", stored_player[0].action);
8788 if (recorded_player_action != NULL)
8789 for (i = 0; i < MAX_PLAYERS; i++)
8790 stored_player[i].action = recorded_player_action[i];
8793 for (i = 0; i < MAX_PLAYERS; i++)
8795 summarized_player_action |= stored_player[i].action;
8797 if (!network_playing)
8798 stored_player[i].effective_action = stored_player[i].action;
8801 #if defined(NETWORK_AVALIABLE)
8802 if (network_playing)
8803 SendToServer_MovePlayer(summarized_player_action);
8806 if (!options.network && !setup.team_mode)
8807 local_player->effective_action = summarized_player_action;
8810 if (recorded_player_action != NULL)
8811 for (i = 0; i < MAX_PLAYERS; i++)
8812 stored_player[i].effective_action = recorded_player_action[i];
8816 for (i = 0; i < MAX_PLAYERS; i++)
8818 tape_action[i] = stored_player[i].effective_action;
8820 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8821 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8824 /* only save actions from input devices, but not programmed actions */
8826 TapeRecordAction(tape_action);
8829 for (i = 0; i < MAX_PLAYERS; i++)
8831 int actual_player_action = stored_player[i].effective_action;
8834 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8835 - rnd_equinox_tetrachloride 048
8836 - rnd_equinox_tetrachloride_ii 096
8837 - rnd_emanuel_schmieg 002
8838 - doctor_sloan_ww 001, 020
8840 if (stored_player[i].MovPos == 0)
8841 CheckGravityMovement(&stored_player[i]);
8845 /* overwrite programmed action with tape action */
8846 if (stored_player[i].programmed_action)
8847 actual_player_action = stored_player[i].programmed_action;
8851 if (stored_player[i].programmed_action)
8852 printf("::: %d\n", stored_player[i].programmed_action);
8855 if (recorded_player_action)
8858 if (stored_player[i].programmed_action &&
8859 stored_player[i].programmed_action != recorded_player_action[i])
8860 printf("::: %d: %d <-> %d\n", i,
8861 stored_player[i].programmed_action, recorded_player_action[i]);
8865 actual_player_action = recorded_player_action[i];
8870 /* overwrite tape action with programmed action */
8871 if (stored_player[i].programmed_action)
8872 actual_player_action = stored_player[i].programmed_action;
8877 printf("::: action: %d: %x [%d]\n",
8878 stored_player[i].MovPos, actual_player_action, FrameCounter);
8882 PlayerActions(&stored_player[i], actual_player_action);
8884 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8886 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8887 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8890 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8895 TapeRecordAction(tape_action);
8898 network_player_action_received = FALSE;
8900 ScrollScreen(NULL, SCROLL_GO_ON);
8906 for (i = 0; i < MAX_PLAYERS; i++)
8907 stored_player[i].Frame++;
8911 /* for backwards compatibility, the following code emulates a fixed bug that
8912 occured when pushing elements (causing elements that just made their last
8913 pushing step to already (if possible) make their first falling step in the
8914 same game frame, which is bad); this code is also needed to use the famous
8915 "spring push bug" which is used in older levels and might be wanted to be
8916 used also in newer levels, but in this case the buggy pushing code is only
8917 affecting the "spring" element and no other elements */
8920 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8922 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8925 for (i = 0; i < MAX_PLAYERS; i++)
8927 struct PlayerInfo *player = &stored_player[i];
8932 if (player->active && player->is_pushing && player->is_moving &&
8934 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8935 Feld[x][y] == EL_SPRING))
8937 if (player->active && player->is_pushing && player->is_moving &&
8941 ContinueMoving(x, y);
8943 /* continue moving after pushing (this is actually a bug) */
8944 if (!IS_MOVING(x, y))
8953 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8955 Changed[x][y] = CE_BITMASK_DEFAULT;
8956 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8958 #if USE_NEW_BLOCK_STYLE
8959 /* this must be handled before main playfield loop */
8960 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8963 if (MovDelay[x][y] <= 0)
8969 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8971 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8972 printf("GameActions(): This should never happen!\n");
8974 ChangePage[x][y] = -1;
8979 if (WasJustMoving[x][y] > 0)
8980 WasJustMoving[x][y]--;
8981 if (WasJustFalling[x][y] > 0)
8982 WasJustFalling[x][y]--;
8983 if (CheckCollision[x][y] > 0)
8984 CheckCollision[x][y]--;
8989 /* reset finished pushing action (not done in ContinueMoving() to allow
8990 continous pushing animation for elements with zero push delay) */
8991 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8993 ResetGfxAnimation(x, y);
8994 DrawLevelField(x, y);
8999 if (IS_BLOCKED(x, y))
9003 Blocked2Moving(x, y, &oldx, &oldy);
9004 if (!IS_MOVING(oldx, oldy))
9006 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9007 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9008 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9009 printf("GameActions(): This should never happen!\n");
9015 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9017 element = Feld[x][y];
9019 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9021 graphic = el2img(element);
9027 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9029 element = graphic = 0;
9033 if (graphic_info[graphic].anim_global_sync)
9034 GfxFrame[x][y] = FrameCounter;
9036 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9037 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9038 ResetRandomAnimationValue(x, y);
9040 SetRandomAnimationValue(x, y);
9043 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9046 if (IS_INACTIVE(element))
9048 if (IS_ANIMATED(graphic))
9049 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9055 /* this may take place after moving, so 'element' may have changed */
9057 if (IS_CHANGING(x, y))
9059 if (IS_CHANGING(x, y) &&
9060 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9064 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9065 element_info[element].event_page_nr[CE_DELAY]);
9067 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9070 element = Feld[x][y];
9071 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9075 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9080 element = Feld[x][y];
9081 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9083 if (element == EL_MOLE)
9084 printf("::: %d, %d, %d [%d]\n",
9085 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9089 if (element == EL_YAMYAM)
9090 printf("::: %d, %d, %d\n",
9091 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9095 if (IS_ANIMATED(graphic) &&
9099 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9102 if (element == EL_BUG)
9103 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9107 if (element == EL_MOLE)
9108 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9112 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9113 EdelsteinFunkeln(x, y);
9115 else if ((element == EL_ACID ||
9116 element == EL_EXIT_OPEN ||
9117 element == EL_SP_EXIT_OPEN ||
9118 element == EL_SP_TERMINAL ||
9119 element == EL_SP_TERMINAL_ACTIVE ||
9120 element == EL_EXTRA_TIME ||
9121 element == EL_SHIELD_NORMAL ||
9122 element == EL_SHIELD_DEADLY) &&
9123 IS_ANIMATED(graphic))
9124 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9125 else if (IS_MOVING(x, y))
9126 ContinueMoving(x, y);
9127 else if (IS_ACTIVE_BOMB(element))
9128 CheckDynamite(x, y);
9130 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9131 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9133 else if (element == EL_AMOEBA_GROWING)
9134 AmoebeWaechst(x, y);
9135 else if (element == EL_AMOEBA_SHRINKING)
9136 AmoebaDisappearing(x, y);
9138 #if !USE_NEW_AMOEBA_CODE
9139 else if (IS_AMOEBALIVE(element))
9140 AmoebeAbleger(x, y);
9143 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9145 else if (element == EL_EXIT_CLOSED)
9147 else if (element == EL_SP_EXIT_CLOSED)
9149 else if (element == EL_EXPANDABLE_WALL_GROWING)
9151 else if (element == EL_EXPANDABLE_WALL ||
9152 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9153 element == EL_EXPANDABLE_WALL_VERTICAL ||
9154 element == EL_EXPANDABLE_WALL_ANY)
9156 else if (element == EL_FLAMES)
9157 CheckForDragon(x, y);
9159 else if (IS_AUTO_CHANGING(element))
9160 ChangeElement(x, y);
9162 else if (element == EL_EXPLOSION)
9163 ; /* drawing of correct explosion animation is handled separately */
9164 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9165 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9168 /* this may take place after moving, so 'element' may have changed */
9169 if (IS_AUTO_CHANGING(Feld[x][y]))
9170 ChangeElement(x, y);
9173 if (IS_BELT_ACTIVE(element))
9174 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9176 if (game.magic_wall_active)
9178 int jx = local_player->jx, jy = local_player->jy;
9180 /* play the element sound at the position nearest to the player */
9181 if ((element == EL_MAGIC_WALL_FULL ||
9182 element == EL_MAGIC_WALL_ACTIVE ||
9183 element == EL_MAGIC_WALL_EMPTYING ||
9184 element == EL_BD_MAGIC_WALL_FULL ||
9185 element == EL_BD_MAGIC_WALL_ACTIVE ||
9186 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9187 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9195 #if USE_NEW_AMOEBA_CODE
9196 /* new experimental amoeba growth stuff */
9198 if (!(FrameCounter % 8))
9201 static unsigned long random = 1684108901;
9203 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9206 x = (random >> 10) % lev_fieldx;
9207 y = (random >> 20) % lev_fieldy;
9209 x = RND(lev_fieldx);
9210 y = RND(lev_fieldy);
9212 element = Feld[x][y];
9215 if (!IS_PLAYER(x,y) &&
9216 (element == EL_EMPTY ||
9217 CAN_GROW_INTO(element) ||
9218 element == EL_QUICKSAND_EMPTY ||
9219 element == EL_ACID_SPLASH_LEFT ||
9220 element == EL_ACID_SPLASH_RIGHT))
9222 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9223 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9224 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9225 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9226 Feld[x][y] = EL_AMOEBA_DROP;
9229 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9230 if (!IS_PLAYER(x,y) &&
9231 (element == EL_EMPTY ||
9232 element == EL_SAND ||
9233 element == EL_QUICKSAND_EMPTY ||
9234 element == EL_ACID_SPLASH_LEFT ||
9235 element == EL_ACID_SPLASH_RIGHT))
9237 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9238 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9239 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9240 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9241 Feld[x][y] = EL_AMOEBA_DROP;
9245 random = random * 129 + 1;
9251 if (game.explosions_delayed)
9254 game.explosions_delayed = FALSE;
9256 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9258 element = Feld[x][y];
9260 if (ExplodeField[x][y])
9261 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9262 else if (element == EL_EXPLOSION)
9263 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9265 ExplodeField[x][y] = EX_TYPE_NONE;
9268 game.explosions_delayed = TRUE;
9271 if (game.magic_wall_active)
9273 if (!(game.magic_wall_time_left % 4))
9275 int element = Feld[magic_wall_x][magic_wall_y];
9277 if (element == EL_BD_MAGIC_WALL_FULL ||
9278 element == EL_BD_MAGIC_WALL_ACTIVE ||
9279 element == EL_BD_MAGIC_WALL_EMPTYING)
9280 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9282 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9285 if (game.magic_wall_time_left > 0)
9287 game.magic_wall_time_left--;
9288 if (!game.magic_wall_time_left)
9290 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9292 element = Feld[x][y];
9294 if (element == EL_MAGIC_WALL_ACTIVE ||
9295 element == EL_MAGIC_WALL_FULL)
9297 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9298 DrawLevelField(x, y);
9300 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9301 element == EL_BD_MAGIC_WALL_FULL)
9303 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9304 DrawLevelField(x, y);
9308 game.magic_wall_active = FALSE;
9313 if (game.light_time_left > 0)
9315 game.light_time_left--;
9317 if (game.light_time_left == 0)
9318 RedrawAllLightSwitchesAndInvisibleElements();
9321 if (game.timegate_time_left > 0)
9323 game.timegate_time_left--;
9325 if (game.timegate_time_left == 0)
9326 CloseAllOpenTimegates();
9329 for (i = 0; i < MAX_PLAYERS; i++)
9331 struct PlayerInfo *player = &stored_player[i];
9333 if (SHIELD_ON(player))
9335 if (player->shield_deadly_time_left)
9336 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9337 else if (player->shield_normal_time_left)
9338 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9342 if (TimeFrames >= FRAMES_PER_SECOND)
9347 for (i = 0; i < MAX_PLAYERS; i++)
9349 struct PlayerInfo *player = &stored_player[i];
9351 if (SHIELD_ON(player))
9353 player->shield_normal_time_left--;
9355 if (player->shield_deadly_time_left > 0)
9356 player->shield_deadly_time_left--;
9360 if (!level.use_step_counter)
9368 if (TimeLeft <= 10 && setup.time_limit)
9369 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9371 DrawGameValue_Time(TimeLeft);
9373 if (!TimeLeft && setup.time_limit)
9374 for (i = 0; i < MAX_PLAYERS; i++)
9375 KillHero(&stored_player[i]);
9377 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9378 DrawGameValue_Time(TimePlayed);
9381 if (tape.recording || tape.playing)
9382 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9386 PlayAllPlayersSound();
9388 if (options.debug) /* calculate frames per second */
9390 static unsigned long fps_counter = 0;
9391 static int fps_frames = 0;
9392 unsigned long fps_delay_ms = Counter() - fps_counter;
9396 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9398 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9401 fps_counter = Counter();
9404 redraw_mask |= REDRAW_FPS;
9408 if (stored_player[0].jx != stored_player[0].last_jx ||
9409 stored_player[0].jy != stored_player[0].last_jy)
9410 printf("::: %d, %d, %d, %d, %d\n",
9411 stored_player[0].MovDir,
9412 stored_player[0].MovPos,
9413 stored_player[0].GfxPos,
9414 stored_player[0].Frame,
9415 stored_player[0].StepFrame);
9418 #if USE_NEW_MOVE_DELAY
9419 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9424 for (i = 0; i < MAX_PLAYERS; i++)
9427 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9429 stored_player[i].Frame += move_frames;
9431 if (stored_player[i].MovPos != 0)
9432 stored_player[i].StepFrame += move_frames;
9434 #if USE_NEW_MOVE_DELAY
9435 if (stored_player[i].move_delay > 0)
9436 stored_player[i].move_delay--;
9439 if (stored_player[i].drop_delay > 0)
9440 stored_player[i].drop_delay--;
9445 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9447 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9449 local_player->show_envelope = 0;
9453 #if USE_NEW_RANDOMIZE
9454 /* use random number generator in every frame to make it less predictable */
9455 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9460 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9462 int min_x = x, min_y = y, max_x = x, max_y = y;
9465 for (i = 0; i < MAX_PLAYERS; i++)
9467 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9469 if (!stored_player[i].active || &stored_player[i] == player)
9472 min_x = MIN(min_x, jx);
9473 min_y = MIN(min_y, jy);
9474 max_x = MAX(max_x, jx);
9475 max_y = MAX(max_y, jy);
9478 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9481 static boolean AllPlayersInVisibleScreen()
9485 for (i = 0; i < MAX_PLAYERS; i++)
9487 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9489 if (!stored_player[i].active)
9492 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9499 void ScrollLevel(int dx, int dy)
9501 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9504 BlitBitmap(drawto_field, drawto_field,
9505 FX + TILEX * (dx == -1) - softscroll_offset,
9506 FY + TILEY * (dy == -1) - softscroll_offset,
9507 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9508 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9509 FX + TILEX * (dx == 1) - softscroll_offset,
9510 FY + TILEY * (dy == 1) - softscroll_offset);
9514 x = (dx == 1 ? BX1 : BX2);
9515 for (y = BY1; y <= BY2; y++)
9516 DrawScreenField(x, y);
9521 y = (dy == 1 ? BY1 : BY2);
9522 for (x = BX1; x <= BX2; x++)
9523 DrawScreenField(x, y);
9526 redraw_mask |= REDRAW_FIELD;
9530 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9532 int nextx = x + dx, nexty = y + dy;
9533 int element = Feld[x][y];
9536 element != EL_SP_PORT_LEFT &&
9537 element != EL_SP_GRAVITY_PORT_LEFT &&
9538 element != EL_SP_PORT_HORIZONTAL &&
9539 element != EL_SP_PORT_ANY) ||
9541 element != EL_SP_PORT_RIGHT &&
9542 element != EL_SP_GRAVITY_PORT_RIGHT &&
9543 element != EL_SP_PORT_HORIZONTAL &&
9544 element != EL_SP_PORT_ANY) ||
9546 element != EL_SP_PORT_UP &&
9547 element != EL_SP_GRAVITY_PORT_UP &&
9548 element != EL_SP_PORT_VERTICAL &&
9549 element != EL_SP_PORT_ANY) ||
9551 element != EL_SP_PORT_DOWN &&
9552 element != EL_SP_GRAVITY_PORT_DOWN &&
9553 element != EL_SP_PORT_VERTICAL &&
9554 element != EL_SP_PORT_ANY) ||
9555 !IN_LEV_FIELD(nextx, nexty) ||
9556 !IS_FREE(nextx, nexty))
9563 static boolean canFallDown(struct PlayerInfo *player)
9565 int jx = player->jx, jy = player->jy;
9567 return (IN_LEV_FIELD(jx, jy + 1) &&
9568 (IS_FREE(jx, jy + 1) ||
9569 #if USE_NEW_BLOCK_STYLE
9570 #if USE_GRAVITY_BUGFIX_OLD
9571 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9574 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9575 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9576 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9579 static boolean canPassField(int x, int y, int move_dir)
9581 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9582 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9583 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9586 int element = Feld[x][y];
9588 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9589 !CAN_MOVE(element) &&
9590 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9591 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9592 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9595 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9597 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9598 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9599 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9603 int nextx = newx + dx;
9604 int nexty = newy + dy;
9608 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9609 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9611 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9613 (IS_DIGGABLE(Feld[newx][newy]) ||
9614 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9615 canPassField(newx, newy, move_dir)));
9618 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9619 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9620 (IS_DIGGABLE(Feld[newx][newy]) ||
9621 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9622 canPassField(newx, newy, move_dir)));
9625 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9626 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9627 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9628 canPassField(newx, newy, move_dir)));
9630 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9631 (IS_DIGGABLE(Feld[newx][newy]) ||
9632 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9633 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9634 !CAN_MOVE(Feld[newx][newy]) &&
9635 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9636 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9637 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9643 static void CheckGravityMovement(struct PlayerInfo *player)
9645 if (game.gravity && !player->programmed_action)
9648 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9649 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9651 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9652 int move_dir_vertical = player->action & MV_VERTICAL;
9656 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9658 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9661 int jx = player->jx, jy = player->jy;
9663 boolean player_is_moving_to_valid_field =
9664 (!player_is_snapping &&
9665 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9666 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9670 (player->last_move_dir & MV_HORIZONTAL ?
9671 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9672 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9676 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9677 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9678 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9679 int new_jx = jx + dx, new_jy = jy + dy;
9680 int nextx = new_jx + dx, nexty = new_jy + dy;
9686 boolean player_can_fall_down = canFallDown(player);
9688 boolean player_can_fall_down =
9689 (IN_LEV_FIELD(jx, jy + 1) &&
9690 (IS_FREE(jx, jy + 1) ||
9691 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9695 boolean player_can_fall_down =
9696 (IN_LEV_FIELD(jx, jy + 1) &&
9697 (IS_FREE(jx, jy + 1)));
9701 boolean player_is_moving_to_valid_field =
9704 !player_is_snapping &&
9708 IN_LEV_FIELD(new_jx, new_jy) &&
9709 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9710 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9711 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9712 IN_LEV_FIELD(nextx, nexty) &&
9713 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9715 IN_LEV_FIELD(new_jx, new_jy) &&
9716 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9717 Feld[new_jx][new_jy] == EL_SAND ||
9718 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9719 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9720 /* !!! extend EL_SAND to anything diggable !!! */
9726 boolean player_is_standing_on_valid_field =
9727 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9728 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9732 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9733 player_can_fall_down,
9734 player_is_standing_on_valid_field,
9735 player_is_moving_to_valid_field,
9736 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9737 player->effective_action,
9738 player->can_fall_into_acid);
9741 if (player_can_fall_down &&
9743 !player_is_standing_on_valid_field &&
9745 !player_is_moving_to_valid_field)
9748 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9749 jx, jy, FrameCounter);
9752 player->programmed_action = MV_DOWN;
9757 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9760 return CheckGravityMovement(player);
9763 if (game.gravity && !player->programmed_action)
9765 int jx = player->jx, jy = player->jy;
9766 boolean field_under_player_is_free =
9767 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9768 boolean player_is_standing_on_valid_field =
9769 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9770 (IS_WALKABLE(Feld[jx][jy]) &&
9771 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9773 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9774 player->programmed_action = MV_DOWN;
9780 -----------------------------------------------------------------------------
9781 dx, dy: direction (non-diagonal) to try to move the player to
9782 real_dx, real_dy: direction as read from input device (can be diagonal)
9785 boolean MovePlayerOneStep(struct PlayerInfo *player,
9786 int dx, int dy, int real_dx, int real_dy)
9789 static int trigger_sides[4][2] =
9791 /* enter side leave side */
9792 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9793 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9794 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9795 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9797 int move_direction = (dx == -1 ? MV_LEFT :
9798 dx == +1 ? MV_RIGHT :
9800 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9801 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9802 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9804 int jx = player->jx, jy = player->jy;
9805 int new_jx = jx + dx, new_jy = jy + dy;
9809 if (!player->active || (!dx && !dy))
9810 return MF_NO_ACTION;
9812 player->MovDir = (dx < 0 ? MV_LEFT :
9815 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9817 if (!IN_LEV_FIELD(new_jx, new_jy))
9818 return MF_NO_ACTION;
9820 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9821 return MF_NO_ACTION;
9824 element = MovingOrBlocked2Element(new_jx, new_jy);
9826 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9829 if (DONT_RUN_INTO(element))
9831 if (element == EL_ACID && dx == 0 && dy == 1)
9833 SplashAcid(new_jx, new_jy);
9834 Feld[jx][jy] = EL_PLAYER_1;
9835 InitMovingField(jx, jy, MV_DOWN);
9836 Store[jx][jy] = EL_ACID;
9837 ContinueMoving(jx, jy);
9841 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9846 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9847 if (can_move != MF_MOVING)
9850 /* check if DigField() has caused relocation of the player */
9851 if (player->jx != jx || player->jy != jy)
9852 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9854 StorePlayer[jx][jy] = 0;
9855 player->last_jx = jx;
9856 player->last_jy = jy;
9857 player->jx = new_jx;
9858 player->jy = new_jy;
9859 StorePlayer[new_jx][new_jy] = player->element_nr;
9862 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9864 player->step_counter++;
9867 player->drop_delay = 0;
9870 PlayerVisit[jx][jy] = FrameCounter;
9872 ScrollPlayer(player, SCROLL_INIT);
9875 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9877 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9879 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9882 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9884 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9885 CE_OTHER_GETS_ENTERED, enter_side);
9886 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9887 CE_ENTERED_BY_PLAYER, enter_side);
9894 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9896 int jx = player->jx, jy = player->jy;
9897 int old_jx = jx, old_jy = jy;
9898 int moved = MF_NO_ACTION;
9901 if (!player->active)
9906 if (player->MovPos == 0)
9908 player->is_moving = FALSE;
9909 player->is_digging = FALSE;
9910 player->is_collecting = FALSE;
9911 player->is_snapping = FALSE;
9912 player->is_pushing = FALSE;
9918 if (!player->active || (!dx && !dy))
9923 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9931 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9932 player->move_delay + player->move_delay_value);
9935 #if USE_NEW_MOVE_DELAY
9936 if (player->move_delay > 0)
9938 if (!FrameReached(&player->move_delay, player->move_delay_value))
9942 printf("::: can NOT move\n");
9948 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9949 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9956 printf("::: COULD move now\n");
9959 #if USE_NEW_MOVE_DELAY
9960 player->move_delay = -1; /* set to "uninitialized" value */
9963 /* store if player is automatically moved to next field */
9964 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9966 /* remove the last programmed player action */
9967 player->programmed_action = 0;
9971 /* should only happen if pre-1.2 tape recordings are played */
9972 /* this is only for backward compatibility */
9974 int original_move_delay_value = player->move_delay_value;
9977 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9981 /* scroll remaining steps with finest movement resolution */
9982 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9984 while (player->MovPos)
9986 ScrollPlayer(player, SCROLL_GO_ON);
9987 ScrollScreen(NULL, SCROLL_GO_ON);
9989 #if USE_NEW_MOVE_DELAY
9990 AdvanceFrameAndPlayerCounters(player->index_nr);
9999 player->move_delay_value = original_move_delay_value;
10002 if (player->last_move_dir & MV_HORIZONTAL)
10004 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10005 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10009 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10010 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10016 if (moved & MF_MOVING && !ScreenMovPos &&
10017 (player == local_player || !options.network))
10019 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10020 int offset = (setup.scroll_delay ? 3 : 0);
10022 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10024 /* actual player has left the screen -- scroll in that direction */
10025 if (jx != old_jx) /* player has moved horizontally */
10026 scroll_x += (jx - old_jx);
10027 else /* player has moved vertically */
10028 scroll_y += (jy - old_jy);
10032 if (jx != old_jx) /* player has moved horizontally */
10034 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10035 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10036 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10038 /* don't scroll over playfield boundaries */
10039 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10040 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10042 /* don't scroll more than one field at a time */
10043 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10045 /* don't scroll against the player's moving direction */
10046 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10047 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10048 scroll_x = old_scroll_x;
10050 else /* player has moved vertically */
10052 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10053 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10054 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10056 /* don't scroll over playfield boundaries */
10057 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10058 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10060 /* don't scroll more than one field at a time */
10061 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10063 /* don't scroll against the player's moving direction */
10064 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10065 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10066 scroll_y = old_scroll_y;
10070 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10072 if (!options.network && !AllPlayersInVisibleScreen())
10074 scroll_x = old_scroll_x;
10075 scroll_y = old_scroll_y;
10079 ScrollScreen(player, SCROLL_INIT);
10080 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10087 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10089 if (!(moved & MF_MOVING) && !player->is_pushing)
10094 player->StepFrame = 0;
10096 if (moved & MF_MOVING)
10099 printf("::: REALLY moves now\n");
10102 if (old_jx != jx && old_jy == jy)
10103 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10104 else if (old_jx == jx && old_jy != jy)
10105 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10107 DrawLevelField(jx, jy); /* for "crumbled sand" */
10109 player->last_move_dir = player->MovDir;
10110 player->is_moving = TRUE;
10112 player->is_snapping = FALSE;
10116 player->is_switching = FALSE;
10119 player->is_dropping = FALSE;
10123 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10126 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10129 int move_direction = player->MovDir;
10131 int enter_side = MV_DIR_OPPOSITE(move_direction);
10132 int leave_side = move_direction;
10134 static int trigger_sides[4][2] =
10136 /* enter side leave side */
10137 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10138 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10139 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10140 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10142 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10143 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10145 int old_element = Feld[old_jx][old_jy];
10146 int new_element = Feld[jx][jy];
10149 /* !!! TEST ONLY !!! */
10150 if (IS_CUSTOM_ELEMENT(old_element))
10151 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10153 player->index_bit, leave_side);
10155 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10156 CE_OTHER_GETS_LEFT,
10157 player->index_bit, leave_side);
10159 if (IS_CUSTOM_ELEMENT(new_element))
10160 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10161 player->index_bit, enter_side);
10163 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10164 CE_OTHER_GETS_ENTERED,
10165 player->index_bit, enter_side);
10175 CheckGravityMovementWhenNotMoving(player);
10178 player->last_move_dir = MV_NO_MOVING;
10180 player->is_moving = FALSE;
10182 #if USE_NEW_MOVE_STYLE
10183 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10184 /* ensure that the player is also allowed to move in the next frame */
10185 /* (currently, the player is forced to wait eight frames before he can try
10188 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10189 player->move_delay = 0; /* allow direct movement in the next frame */
10193 #if USE_NEW_MOVE_DELAY
10194 if (player->move_delay == -1) /* not yet initialized by DigField() */
10195 player->move_delay = player->move_delay_value;
10198 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10200 TestIfHeroTouchesBadThing(jx, jy);
10201 TestIfPlayerTouchesCustomElement(jx, jy);
10204 if (!player->active)
10205 RemoveHero(player);
10210 void ScrollPlayer(struct PlayerInfo *player, int mode)
10212 int jx = player->jx, jy = player->jy;
10213 int last_jx = player->last_jx, last_jy = player->last_jy;
10214 int move_stepsize = TILEX / player->move_delay_value;
10216 if (!player->active || !player->MovPos)
10219 if (mode == SCROLL_INIT)
10221 player->actual_frame_counter = FrameCounter;
10222 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10225 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10227 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10228 player->block_delay);
10231 #if USE_NEW_BLOCK_STYLE
10234 if (player->block_delay <= 0)
10235 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10238 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10239 Feld[last_jx][last_jy] == EL_EMPTY)
10241 int last_field_block_delay = 0; /* start with no blocking at all */
10242 int block_delay_adjustment = player->block_delay_adjustment;
10244 /* if player blocks last field, add delay for exactly one move */
10245 if (player->block_last_field)
10247 last_field_block_delay += player->move_delay_value;
10249 #if USE_GRAVITY_BUGFIX_NEW
10250 /* when blocking enabled, prevent moving up despite gravity */
10251 if (game.gravity && player->MovDir == MV_UP)
10252 block_delay_adjustment = -1;
10256 /* add block delay adjustment (also possible when not blocking) */
10257 last_field_block_delay += block_delay_adjustment;
10260 #if USE_BLOCK_DELAY_BUGFIX
10261 /* when blocking enabled, correct block delay for fast movement */
10262 if (player->block_last_field &&
10263 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10264 last_field_block_delay =
10265 player->move_delay_value + player->block_delay_adjustment;
10270 #if USE_GRAVITY_BUGFIX_NEW
10271 /* when blocking enabled, correct block delay for gravity movement */
10272 if (player->block_last_field &&
10273 game.gravity && player->MovDir == MV_UP)
10274 last_field_block_delay = player->move_delay_value - 1;
10278 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10279 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10282 #if USE_NEW_MOVE_STYLE
10283 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10284 player->block_last_field) &&
10285 Feld[last_jx][last_jy] == EL_EMPTY)
10286 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10288 if (Feld[last_jx][last_jy] == EL_EMPTY)
10289 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10294 DrawPlayer(player);
10299 else if (!FrameReached(&player->actual_frame_counter, 1))
10302 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10303 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10305 #if USE_NEW_BLOCK_STYLE
10307 if (!player->block_last_field &&
10308 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10310 RemoveField(last_jx, last_jy);
10312 Feld[last_jx][last_jy] = EL_EMPTY;
10316 /* before DrawPlayer() to draw correct player graphic for this case */
10317 if (player->MovPos == 0)
10318 CheckGravityMovement(player);
10321 DrawPlayer(player); /* needed here only to cleanup last field */
10324 if (player->MovPos == 0) /* player reached destination field */
10327 if (player->move_delay_reset_counter > 0)
10329 player->move_delay_reset_counter--;
10331 if (player->move_delay_reset_counter == 0)
10333 /* continue with normal speed after quickly moving through gate */
10334 HALVE_PLAYER_SPEED(player);
10336 /* be able to make the next move without delay */
10337 player->move_delay = 0;
10341 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10343 /* continue with normal speed after quickly moving through gate */
10344 HALVE_PLAYER_SPEED(player);
10346 /* be able to make the next move without delay */
10347 player->move_delay = 0;
10351 #if USE_NEW_BLOCK_STYLE
10353 if (player->block_last_field &&
10354 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10356 RemoveField(last_jx, last_jy);
10358 Feld[last_jx][last_jy] = EL_EMPTY;
10362 player->last_jx = jx;
10363 player->last_jy = jy;
10365 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10366 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10367 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10369 DrawPlayer(player); /* needed here only to cleanup last field */
10370 RemoveHero(player);
10372 if (local_player->friends_still_needed == 0 ||
10373 IS_SP_ELEMENT(Feld[jx][jy]))
10374 player->LevelSolved = player->GameOver = TRUE;
10378 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10379 /* this breaks one level: "machine", level 000 */
10381 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10384 int move_direction = player->MovDir;
10386 int enter_side = MV_DIR_OPPOSITE(move_direction);
10387 int leave_side = move_direction;
10389 static int trigger_sides[4][2] =
10391 /* enter side leave side */
10392 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10393 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10394 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10395 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10397 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10398 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10400 int old_jx = last_jx;
10401 int old_jy = last_jy;
10402 int old_element = Feld[old_jx][old_jy];
10403 int new_element = Feld[jx][jy];
10406 /* !!! TEST ONLY !!! */
10407 if (IS_CUSTOM_ELEMENT(old_element))
10408 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10410 player->index_bit, leave_side);
10412 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10413 CE_OTHER_GETS_LEFT,
10414 player->index_bit, leave_side);
10416 if (IS_CUSTOM_ELEMENT(new_element))
10417 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10418 player->index_bit, enter_side);
10420 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10421 CE_OTHER_GETS_ENTERED,
10422 player->index_bit, enter_side);
10428 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10430 TestIfHeroTouchesBadThing(jx, jy);
10431 TestIfPlayerTouchesCustomElement(jx, jy);
10434 /* needed because pushed element has not yet reached its destination,
10435 so it would trigger a change event at its previous field location */
10436 if (!player->is_pushing)
10438 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10441 if (!player->active)
10442 RemoveHero(player);
10445 if (level.use_step_counter)
10455 if (TimeLeft <= 10 && setup.time_limit)
10456 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10458 DrawGameValue_Time(TimeLeft);
10460 if (!TimeLeft && setup.time_limit)
10461 for (i = 0; i < MAX_PLAYERS; i++)
10462 KillHero(&stored_player[i]);
10464 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10465 DrawGameValue_Time(TimePlayed);
10468 if (tape.single_step && tape.recording && !tape.pausing &&
10469 !player->programmed_action)
10470 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10474 void ScrollScreen(struct PlayerInfo *player, int mode)
10476 static unsigned long screen_frame_counter = 0;
10478 if (mode == SCROLL_INIT)
10480 /* set scrolling step size according to actual player's moving speed */
10481 ScrollStepSize = TILEX / player->move_delay_value;
10483 screen_frame_counter = FrameCounter;
10484 ScreenMovDir = player->MovDir;
10485 ScreenMovPos = player->MovPos;
10486 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10489 else if (!FrameReached(&screen_frame_counter, 1))
10494 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10495 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10496 redraw_mask |= REDRAW_FIELD;
10499 ScreenMovDir = MV_NO_MOVING;
10502 void TestIfPlayerTouchesCustomElement(int x, int y)
10504 static int xy[4][2] =
10511 static int trigger_sides[4][2] =
10513 /* center side border side */
10514 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10515 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10516 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10517 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10519 static int touch_dir[4] =
10521 MV_LEFT | MV_RIGHT,
10526 int center_element = Feld[x][y]; /* should always be non-moving! */
10529 for (i = 0; i < NUM_DIRECTIONS; i++)
10531 int xx = x + xy[i][0];
10532 int yy = y + xy[i][1];
10533 int center_side = trigger_sides[i][0];
10534 int border_side = trigger_sides[i][1];
10535 int border_element;
10537 if (!IN_LEV_FIELD(xx, yy))
10540 if (IS_PLAYER(x, y))
10542 struct PlayerInfo *player = PLAYERINFO(x, y);
10544 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10545 border_element = Feld[xx][yy]; /* may be moving! */
10546 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10547 border_element = Feld[xx][yy];
10548 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10549 border_element = MovingOrBlocked2Element(xx, yy);
10551 continue; /* center and border element do not touch */
10554 /* !!! TEST ONLY !!! */
10555 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10556 player->index_bit, border_side);
10557 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10558 CE_OTHER_GETS_TOUCHED,
10559 player->index_bit, border_side);
10561 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10562 CE_OTHER_GETS_TOUCHED,
10563 player->index_bit, border_side);
10564 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10565 player->index_bit, border_side);
10568 else if (IS_PLAYER(xx, yy))
10570 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10572 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10574 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10575 continue; /* center and border element do not touch */
10579 /* !!! TEST ONLY !!! */
10580 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10581 player->index_bit, center_side);
10582 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10583 CE_OTHER_GETS_TOUCHED,
10584 player->index_bit, center_side);
10586 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10587 CE_OTHER_GETS_TOUCHED,
10588 player->index_bit, center_side);
10589 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10590 player->index_bit, center_side);
10598 void TestIfElementTouchesCustomElement(int x, int y)
10600 static int xy[4][2] =
10607 static int trigger_sides[4][2] =
10609 /* center side border side */
10610 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10611 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10612 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10613 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10615 static int touch_dir[4] =
10617 MV_LEFT | MV_RIGHT,
10622 boolean change_center_element = FALSE;
10623 int center_element_change_page = 0;
10624 int center_element = Feld[x][y]; /* should always be non-moving! */
10625 int border_trigger_element = EL_UNDEFINED;
10628 for (i = 0; i < NUM_DIRECTIONS; i++)
10630 int xx = x + xy[i][0];
10631 int yy = y + xy[i][1];
10632 int center_side = trigger_sides[i][0];
10633 int border_side = trigger_sides[i][1];
10634 int border_element;
10636 if (!IN_LEV_FIELD(xx, yy))
10639 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10640 border_element = Feld[xx][yy]; /* may be moving! */
10641 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10642 border_element = Feld[xx][yy];
10643 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10644 border_element = MovingOrBlocked2Element(xx, yy);
10646 continue; /* center and border element do not touch */
10648 /* check for change of center element (but change it only once) */
10649 if (IS_CUSTOM_ELEMENT(center_element) &&
10650 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10651 !change_center_element)
10653 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10655 struct ElementChangeInfo *change =
10656 &element_info[center_element].change_page[j];
10658 if (change->can_change &&
10659 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10660 change->trigger_side & border_side &&
10662 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10664 change->trigger_element == border_element
10668 change_center_element = TRUE;
10669 center_element_change_page = j;
10670 border_trigger_element = border_element;
10677 /* check for change of border element */
10678 if (IS_CUSTOM_ELEMENT(border_element) &&
10679 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10681 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10683 struct ElementChangeInfo *change =
10684 &element_info[border_element].change_page[j];
10686 if (change->can_change &&
10687 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10688 change->trigger_side & center_side &&
10690 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10692 change->trigger_element == center_element
10697 printf("::: border_element %d, %d\n", x, y);
10700 CheckElementChangeByPage(xx, yy, border_element, center_element,
10701 CE_OTHER_IS_TOUCHING, j);
10708 if (change_center_element)
10711 printf("::: center_element %d, %d\n", x, y);
10714 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10715 CE_OTHER_IS_TOUCHING, center_element_change_page);
10719 void TestIfElementHitsCustomElement(int x, int y, int direction)
10721 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10722 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10723 int hitx = x + dx, hity = y + dy;
10724 int hitting_element = Feld[x][y];
10725 int touched_element;
10727 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10728 !IS_FREE(hitx, hity) &&
10729 (!IS_MOVING(hitx, hity) ||
10730 MovDir[hitx][hity] != direction ||
10731 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10734 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10738 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10742 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10743 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10745 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10746 CE_HITTING_SOMETHING, direction);
10748 if (IN_LEV_FIELD(hitx, hity))
10750 int opposite_direction = MV_DIR_OPPOSITE(direction);
10751 int hitting_side = direction;
10752 int touched_side = opposite_direction;
10754 int touched_element = MovingOrBlocked2Element(hitx, hity);
10757 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10758 MovDir[hitx][hity] != direction ||
10759 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10768 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10769 CE_HIT_BY_SOMETHING, opposite_direction);
10771 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10772 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10774 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10776 struct ElementChangeInfo *change =
10777 &element_info[hitting_element].change_page[i];
10779 if (change->can_change &&
10780 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10781 change->trigger_side & touched_side &&
10784 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10786 change->trigger_element == touched_element
10790 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10791 CE_OTHER_IS_HITTING, i);
10797 if (IS_CUSTOM_ELEMENT(touched_element) &&
10798 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10800 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10802 struct ElementChangeInfo *change =
10803 &element_info[touched_element].change_page[i];
10805 if (change->can_change &&
10806 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10807 change->trigger_side & hitting_side &&
10809 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10811 change->trigger_element == hitting_element
10815 CheckElementChangeByPage(hitx, hity, touched_element,
10816 hitting_element, CE_OTHER_GETS_HIT, i);
10826 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10828 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10829 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10830 int hitx = x + dx, hity = y + dy;
10831 int hitting_element = Feld[x][y];
10832 int touched_element;
10834 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10835 !IS_FREE(hitx, hity) &&
10836 (!IS_MOVING(hitx, hity) ||
10837 MovDir[hitx][hity] != direction ||
10838 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10841 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10845 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10849 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10850 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10852 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10853 EP_CAN_SMASH_EVERYTHING, direction);
10855 if (IN_LEV_FIELD(hitx, hity))
10857 int opposite_direction = MV_DIR_OPPOSITE(direction);
10858 int hitting_side = direction;
10859 int touched_side = opposite_direction;
10861 int touched_element = MovingOrBlocked2Element(hitx, hity);
10864 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10865 MovDir[hitx][hity] != direction ||
10866 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10875 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10876 CE_SMASHED_BY_SOMETHING, opposite_direction);
10878 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10879 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10881 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10883 struct ElementChangeInfo *change =
10884 &element_info[hitting_element].change_page[i];
10886 if (change->can_change &&
10887 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10888 change->trigger_side & touched_side &&
10891 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10893 change->trigger_element == touched_element
10897 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10898 CE_OTHER_IS_SMASHING, i);
10904 if (IS_CUSTOM_ELEMENT(touched_element) &&
10905 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10907 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10909 struct ElementChangeInfo *change =
10910 &element_info[touched_element].change_page[i];
10912 if (change->can_change &&
10913 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10914 change->trigger_side & hitting_side &&
10916 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10918 change->trigger_element == hitting_element
10922 CheckElementChangeByPage(hitx, hity, touched_element,
10923 hitting_element, CE_OTHER_GETS_SMASHED,i);
10933 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10935 int i, kill_x = -1, kill_y = -1;
10936 int bad_element = -1;
10937 static int test_xy[4][2] =
10944 static int test_dir[4] =
10952 for (i = 0; i < NUM_DIRECTIONS; i++)
10954 int test_x, test_y, test_move_dir, test_element;
10956 test_x = good_x + test_xy[i][0];
10957 test_y = good_y + test_xy[i][1];
10959 if (!IN_LEV_FIELD(test_x, test_y))
10963 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10966 test_element = Feld[test_x][test_y];
10968 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10971 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10972 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10974 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10975 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10979 bad_element = test_element;
10985 if (kill_x != -1 || kill_y != -1)
10987 if (IS_PLAYER(good_x, good_y))
10989 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10992 if (player->shield_deadly_time_left > 0 &&
10993 !IS_INDESTRUCTIBLE(bad_element))
10994 Bang(kill_x, kill_y);
10995 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10998 if (player->shield_deadly_time_left > 0)
10999 Bang(kill_x, kill_y);
11000 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11005 Bang(good_x, good_y);
11009 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11011 int i, kill_x = -1, kill_y = -1;
11012 int bad_element = Feld[bad_x][bad_y];
11013 static int test_xy[4][2] =
11020 static int touch_dir[4] =
11022 MV_LEFT | MV_RIGHT,
11027 static int test_dir[4] =
11035 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11038 for (i = 0; i < NUM_DIRECTIONS; i++)
11040 int test_x, test_y, test_move_dir, test_element;
11042 test_x = bad_x + test_xy[i][0];
11043 test_y = bad_y + test_xy[i][1];
11044 if (!IN_LEV_FIELD(test_x, test_y))
11048 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11050 test_element = Feld[test_x][test_y];
11052 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11053 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11055 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11056 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11058 /* good thing is player or penguin that does not move away */
11059 if (IS_PLAYER(test_x, test_y))
11061 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11063 if (bad_element == EL_ROBOT && player->is_moving)
11064 continue; /* robot does not kill player if he is moving */
11066 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11068 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11069 continue; /* center and border element do not touch */
11076 else if (test_element == EL_PENGUIN)
11085 if (kill_x != -1 || kill_y != -1)
11087 if (IS_PLAYER(kill_x, kill_y))
11089 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11092 if (player->shield_deadly_time_left > 0 &&
11093 !IS_INDESTRUCTIBLE(bad_element))
11094 Bang(bad_x, bad_y);
11095 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11098 if (player->shield_deadly_time_left > 0)
11099 Bang(bad_x, bad_y);
11100 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11105 Bang(kill_x, kill_y);
11109 void TestIfHeroTouchesBadThing(int x, int y)
11111 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11114 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11116 TestIfGoodThingHitsBadThing(x, y, move_dir);
11119 void TestIfBadThingTouchesHero(int x, int y)
11121 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11124 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11126 TestIfBadThingHitsGoodThing(x, y, move_dir);
11129 void TestIfFriendTouchesBadThing(int x, int y)
11131 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11134 void TestIfBadThingTouchesFriend(int x, int y)
11136 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11139 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11141 int i, kill_x = bad_x, kill_y = bad_y;
11142 static int xy[4][2] =
11150 for (i = 0; i < NUM_DIRECTIONS; i++)
11154 x = bad_x + xy[i][0];
11155 y = bad_y + xy[i][1];
11156 if (!IN_LEV_FIELD(x, y))
11159 element = Feld[x][y];
11160 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11161 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11169 if (kill_x != bad_x || kill_y != bad_y)
11170 Bang(bad_x, bad_y);
11173 void KillHero(struct PlayerInfo *player)
11175 int jx = player->jx, jy = player->jy;
11177 if (!player->active)
11180 /* remove accessible field at the player's position */
11181 Feld[jx][jy] = EL_EMPTY;
11183 /* deactivate shield (else Bang()/Explode() would not work right) */
11184 player->shield_normal_time_left = 0;
11185 player->shield_deadly_time_left = 0;
11191 static void KillHeroUnlessEnemyProtected(int x, int y)
11193 if (!PLAYER_ENEMY_PROTECTED(x, y))
11194 KillHero(PLAYERINFO(x, y));
11197 static void KillHeroUnlessExplosionProtected(int x, int y)
11199 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11200 KillHero(PLAYERINFO(x, y));
11203 void BuryHero(struct PlayerInfo *player)
11205 int jx = player->jx, jy = player->jy;
11207 if (!player->active)
11211 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11213 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11215 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11217 player->GameOver = TRUE;
11218 RemoveHero(player);
11221 void RemoveHero(struct PlayerInfo *player)
11223 int jx = player->jx, jy = player->jy;
11224 int i, found = FALSE;
11226 player->present = FALSE;
11227 player->active = FALSE;
11229 if (!ExplodeField[jx][jy])
11230 StorePlayer[jx][jy] = 0;
11232 for (i = 0; i < MAX_PLAYERS; i++)
11233 if (stored_player[i].active)
11237 AllPlayersGone = TRUE;
11244 =============================================================================
11245 checkDiagonalPushing()
11246 -----------------------------------------------------------------------------
11247 check if diagonal input device direction results in pushing of object
11248 (by checking if the alternative direction is walkable, diggable, ...)
11249 =============================================================================
11252 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11253 int x, int y, int real_dx, int real_dy)
11255 int jx, jy, dx, dy, xx, yy;
11257 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11260 /* diagonal direction: check alternative direction */
11265 xx = jx + (dx == 0 ? real_dx : 0);
11266 yy = jy + (dy == 0 ? real_dy : 0);
11268 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11272 =============================================================================
11274 -----------------------------------------------------------------------------
11275 x, y: field next to player (non-diagonal) to try to dig to
11276 real_dx, real_dy: direction as read from input device (can be diagonal)
11277 =============================================================================
11280 int DigField(struct PlayerInfo *player,
11281 int oldx, int oldy, int x, int y,
11282 int real_dx, int real_dy, int mode)
11285 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11287 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11288 boolean player_was_pushing = player->is_pushing;
11289 int jx = oldx, jy = oldy;
11290 int dx = x - jx, dy = y - jy;
11291 int nextx = x + dx, nexty = y + dy;
11292 int move_direction = (dx == -1 ? MV_LEFT :
11293 dx == +1 ? MV_RIGHT :
11295 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11296 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11298 int dig_side = MV_DIR_OPPOSITE(move_direction);
11300 static int trigger_sides[4] =
11302 CH_SIDE_RIGHT, /* moving left */
11303 CH_SIDE_LEFT, /* moving right */
11304 CH_SIDE_BOTTOM, /* moving up */
11305 CH_SIDE_TOP, /* moving down */
11307 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11309 int old_element = Feld[jx][jy];
11312 if (is_player) /* function can also be called by EL_PENGUIN */
11314 if (player->MovPos == 0)
11316 player->is_digging = FALSE;
11317 player->is_collecting = FALSE;
11320 if (player->MovPos == 0) /* last pushing move finished */
11321 player->is_pushing = FALSE;
11323 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11325 player->is_switching = FALSE;
11326 #if USE_NEW_PUSH_DELAY
11327 player->push_delay = -1;
11329 player->push_delay = 0;
11332 return MF_NO_ACTION;
11336 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11337 return MF_NO_ACTION;
11342 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11344 if (IS_TUBE(Feld[jx][jy]) ||
11345 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11349 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11350 int tube_leave_directions[][2] =
11352 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11353 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11354 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11355 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11356 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11357 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11358 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11359 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11360 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11361 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11362 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11363 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11366 while (tube_leave_directions[i][0] != tube_element)
11369 if (tube_leave_directions[i][0] == -1) /* should not happen */
11373 if (!(tube_leave_directions[i][1] & move_direction))
11374 return MF_NO_ACTION; /* tube has no opening in this direction */
11379 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11380 old_element = Back[jx][jy];
11384 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11385 return MF_NO_ACTION; /* field has no opening in this direction */
11387 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11388 return MF_NO_ACTION; /* field has no opening in this direction */
11390 element = Feld[x][y];
11392 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11393 return MF_NO_ACTION;
11395 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11396 game.engine_version >= VERSION_IDENT(2,2,0,0))
11397 return MF_NO_ACTION;
11400 if (game.gravity && is_player && !player->is_auto_moving &&
11401 canFallDown(player) && move_direction != MV_DOWN &&
11402 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11403 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11407 if (element == EL_EMPTY_SPACE &&
11408 game.gravity && !player->is_auto_moving &&
11409 canFallDown(player) && move_direction != MV_DOWN)
11410 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11416 case EL_SP_PORT_LEFT:
11417 case EL_SP_PORT_RIGHT:
11418 case EL_SP_PORT_UP:
11419 case EL_SP_PORT_DOWN:
11420 case EL_SP_PORT_HORIZONTAL:
11421 case EL_SP_PORT_VERTICAL:
11422 case EL_SP_PORT_ANY:
11423 case EL_SP_GRAVITY_PORT_LEFT:
11424 case EL_SP_GRAVITY_PORT_RIGHT:
11425 case EL_SP_GRAVITY_PORT_UP:
11426 case EL_SP_GRAVITY_PORT_DOWN:
11428 if (!canEnterSupaplexPort(x, y, dx, dy))
11429 return MF_NO_ACTION;
11432 element != EL_SP_PORT_LEFT &&
11433 element != EL_SP_GRAVITY_PORT_LEFT &&
11434 element != EL_SP_PORT_HORIZONTAL &&
11435 element != EL_SP_PORT_ANY) ||
11437 element != EL_SP_PORT_RIGHT &&
11438 element != EL_SP_GRAVITY_PORT_RIGHT &&
11439 element != EL_SP_PORT_HORIZONTAL &&
11440 element != EL_SP_PORT_ANY) ||
11442 element != EL_SP_PORT_UP &&
11443 element != EL_SP_GRAVITY_PORT_UP &&
11444 element != EL_SP_PORT_VERTICAL &&
11445 element != EL_SP_PORT_ANY) ||
11447 element != EL_SP_PORT_DOWN &&
11448 element != EL_SP_GRAVITY_PORT_DOWN &&
11449 element != EL_SP_PORT_VERTICAL &&
11450 element != EL_SP_PORT_ANY) ||
11451 !IN_LEV_FIELD(nextx, nexty) ||
11452 !IS_FREE(nextx, nexty))
11453 return MF_NO_ACTION;
11456 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11457 element == EL_SP_GRAVITY_PORT_RIGHT ||
11458 element == EL_SP_GRAVITY_PORT_UP ||
11459 element == EL_SP_GRAVITY_PORT_DOWN)
11460 game.gravity = !game.gravity;
11462 /* automatically move to the next field with double speed */
11463 player->programmed_action = move_direction;
11465 if (player->move_delay_reset_counter == 0)
11467 player->move_delay_reset_counter = 2; /* two double speed steps */
11469 DOUBLE_PLAYER_SPEED(player);
11472 player->move_delay_reset_counter = 2;
11474 DOUBLE_PLAYER_SPEED(player);
11478 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11481 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11487 case EL_TUBE_VERTICAL:
11488 case EL_TUBE_HORIZONTAL:
11489 case EL_TUBE_VERTICAL_LEFT:
11490 case EL_TUBE_VERTICAL_RIGHT:
11491 case EL_TUBE_HORIZONTAL_UP:
11492 case EL_TUBE_HORIZONTAL_DOWN:
11493 case EL_TUBE_LEFT_UP:
11494 case EL_TUBE_LEFT_DOWN:
11495 case EL_TUBE_RIGHT_UP:
11496 case EL_TUBE_RIGHT_DOWN:
11499 int tube_enter_directions[][2] =
11501 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11502 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11503 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11504 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11505 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11506 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11507 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11508 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11509 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11510 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11511 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11512 { -1, MV_NO_MOVING }
11515 while (tube_enter_directions[i][0] != element)
11518 if (tube_enter_directions[i][0] == -1) /* should not happen */
11522 if (!(tube_enter_directions[i][1] & move_direction))
11523 return MF_NO_ACTION; /* tube has no opening in this direction */
11525 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11533 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11535 if (IS_WALKABLE(element))
11538 int sound_element = SND_ELEMENT(element);
11539 int sound_action = ACTION_WALKING;
11542 if (!ACCESS_FROM(element, opposite_direction))
11543 return MF_NO_ACTION; /* field not accessible from this direction */
11547 if (element == EL_EMPTY_SPACE &&
11548 game.gravity && !player->is_auto_moving &&
11549 canFallDown(player) && move_direction != MV_DOWN)
11550 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11553 if (IS_RND_GATE(element))
11555 if (!player->key[RND_GATE_NR(element)])
11556 return MF_NO_ACTION;
11558 else if (IS_RND_GATE_GRAY(element))
11560 if (!player->key[RND_GATE_GRAY_NR(element)])
11561 return MF_NO_ACTION;
11563 else if (element == EL_EXIT_OPEN ||
11564 element == EL_SP_EXIT_OPEN ||
11565 element == EL_SP_EXIT_OPENING)
11567 sound_action = ACTION_PASSING; /* player is passing exit */
11569 else if (element == EL_EMPTY)
11571 sound_action = ACTION_MOVING; /* nothing to walk on */
11574 /* play sound from background or player, whatever is available */
11575 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11576 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11578 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11583 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11585 else if (IS_PASSABLE(element))
11589 if (!canPassField(x, y, move_direction))
11590 return MF_NO_ACTION;
11595 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11596 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11597 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11598 return MF_NO_ACTION;
11600 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11601 return MF_NO_ACTION;
11606 if (!ACCESS_FROM(element, opposite_direction))
11607 return MF_NO_ACTION; /* field not accessible from this direction */
11609 if (IS_CUSTOM_ELEMENT(element) &&
11610 !ACCESS_FROM(element, opposite_direction))
11611 return MF_NO_ACTION; /* field not accessible from this direction */
11615 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11616 return MF_NO_ACTION;
11621 if (IS_EM_GATE(element))
11623 if (!player->key[EM_GATE_NR(element)])
11624 return MF_NO_ACTION;
11626 else if (IS_EM_GATE_GRAY(element))
11628 if (!player->key[EM_GATE_GRAY_NR(element)])
11629 return MF_NO_ACTION;
11631 else if (IS_SP_PORT(element))
11633 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11634 element == EL_SP_GRAVITY_PORT_RIGHT ||
11635 element == EL_SP_GRAVITY_PORT_UP ||
11636 element == EL_SP_GRAVITY_PORT_DOWN)
11637 game.gravity = !game.gravity;
11638 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11639 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11640 element == EL_SP_GRAVITY_ON_PORT_UP ||
11641 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11642 game.gravity = TRUE;
11643 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11644 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11645 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11646 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11647 game.gravity = FALSE;
11650 /* automatically move to the next field with double speed */
11651 player->programmed_action = move_direction;
11653 if (player->move_delay_reset_counter == 0)
11655 player->move_delay_reset_counter = 2; /* two double speed steps */
11657 DOUBLE_PLAYER_SPEED(player);
11660 player->move_delay_reset_counter = 2;
11662 DOUBLE_PLAYER_SPEED(player);
11665 PlayLevelSoundAction(x, y, ACTION_PASSING);
11669 else if (IS_DIGGABLE(element))
11673 if (mode != DF_SNAP)
11676 GfxElement[x][y] = GFX_ELEMENT(element);
11679 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11681 player->is_digging = TRUE;
11684 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11686 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11687 player->index_bit, dig_side);
11690 if (mode == DF_SNAP)
11691 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11696 else if (IS_COLLECTIBLE(element))
11700 if (is_player && mode != DF_SNAP)
11702 GfxElement[x][y] = element;
11703 player->is_collecting = TRUE;
11706 if (element == EL_SPEED_PILL)
11707 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11708 else if (element == EL_EXTRA_TIME && level.time > 0)
11711 DrawGameValue_Time(TimeLeft);
11713 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11715 player->shield_normal_time_left += 10;
11716 if (element == EL_SHIELD_DEADLY)
11717 player->shield_deadly_time_left += 10;
11719 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11721 if (player->inventory_size < MAX_INVENTORY_SIZE)
11722 player->inventory_element[player->inventory_size++] = element;
11724 DrawGameValue_Dynamite(local_player->inventory_size);
11726 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11728 player->dynabomb_count++;
11729 player->dynabombs_left++;
11731 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11733 player->dynabomb_size++;
11735 else if (element == EL_DYNABOMB_INCREASE_POWER)
11737 player->dynabomb_xl = TRUE;
11739 else if (IS_KEY(element))
11741 player->key[KEY_NR(element)] = TRUE;
11743 DrawGameValue_Keys(player->key);
11745 redraw_mask |= REDRAW_DOOR_1;
11747 else if (IS_ENVELOPE(element))
11750 player->show_envelope = element;
11752 ShowEnvelope(element - EL_ENVELOPE_1);
11755 else if (IS_DROPPABLE(element) ||
11756 IS_THROWABLE(element)) /* can be collected and dropped */
11760 if (element_info[element].collect_count == 0)
11761 player->inventory_infinite_element = element;
11763 for (i = 0; i < element_info[element].collect_count; i++)
11764 if (player->inventory_size < MAX_INVENTORY_SIZE)
11765 player->inventory_element[player->inventory_size++] = element;
11767 DrawGameValue_Dynamite(local_player->inventory_size);
11769 else if (element_info[element].collect_count > 0)
11771 local_player->gems_still_needed -=
11772 element_info[element].collect_count;
11773 if (local_player->gems_still_needed < 0)
11774 local_player->gems_still_needed = 0;
11776 DrawGameValue_Emeralds(local_player->gems_still_needed);
11779 RaiseScoreElement(element);
11780 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11783 CheckTriggeredElementChangeByPlayer(x, y, element,
11784 CE_OTHER_GETS_COLLECTED,
11785 player->index_bit, dig_side);
11788 if (mode == DF_SNAP)
11789 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11794 else if (IS_PUSHABLE(element))
11796 if (mode == DF_SNAP && element != EL_BD_ROCK)
11797 return MF_NO_ACTION;
11799 if (CAN_FALL(element) && dy)
11800 return MF_NO_ACTION;
11802 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11803 !(element == EL_SPRING && level.use_spring_bug))
11804 return MF_NO_ACTION;
11807 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11808 ((move_direction & MV_VERTICAL &&
11809 ((element_info[element].move_pattern & MV_LEFT &&
11810 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11811 (element_info[element].move_pattern & MV_RIGHT &&
11812 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11813 (move_direction & MV_HORIZONTAL &&
11814 ((element_info[element].move_pattern & MV_UP &&
11815 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11816 (element_info[element].move_pattern & MV_DOWN &&
11817 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11818 return MF_NO_ACTION;
11822 /* do not push elements already moving away faster than player */
11823 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11824 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11825 return MF_NO_ACTION;
11827 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11828 return MF_NO_ACTION;
11834 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11836 if (player->push_delay_value == -1 || !player_was_pushing)
11837 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11839 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11841 if (player->push_delay_value == -1)
11842 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11845 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11847 if (player->push_delay_value == -1 || !player_was_pushing)
11848 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11851 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11853 if (!player->is_pushing)
11854 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11858 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11859 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11860 !player_is_pushing))
11861 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11864 if (!player->is_pushing &&
11865 game.engine_version >= VERSION_IDENT(2,2,0,7))
11866 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11870 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11871 player->push_delay, player->push_delay_value,
11872 FrameCounter, game.engine_version,
11873 player_was_pushing, player->is_pushing,
11874 element, element_info[element].token_name,
11875 GET_NEW_PUSH_DELAY(element));
11878 player->is_pushing = TRUE;
11880 if (!(IN_LEV_FIELD(nextx, nexty) &&
11881 (IS_FREE(nextx, nexty) ||
11882 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11883 IS_SB_ELEMENT(element)))))
11884 return MF_NO_ACTION;
11886 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11887 return MF_NO_ACTION;
11889 #if USE_NEW_PUSH_DELAY
11892 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11893 printf("::: ALERT: %d, %d [%d / %d]\n",
11894 player->push_delay, player->push_delay2,
11895 FrameCounter, FrameCounter / 50);
11898 if (player->push_delay == -1) /* new pushing; restart delay */
11899 player->push_delay = 0;
11901 if (player->push_delay == 0) /* new pushing; restart delay */
11902 player->push_delay = FrameCounter;
11905 #if USE_NEW_PUSH_DELAY
11907 if ( (player->push_delay > 0) != (!xxx_fr) )
11908 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11909 player->push_delay,
11910 xxx_pdv2, player->push_delay2, player->push_delay_value,
11911 FrameCounter, FrameCounter / 50);
11915 if (player->push_delay > 0 &&
11916 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11917 element != EL_SPRING && element != EL_BALLOON)
11920 if (player->push_delay < player->push_delay_value &&
11921 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11922 element != EL_SPRING && element != EL_BALLOON)
11926 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11927 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11928 element != EL_SPRING && element != EL_BALLOON)
11931 /* make sure that there is no move delay before next try to push */
11932 #if USE_NEW_MOVE_DELAY
11933 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11934 player->move_delay = 0;
11936 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11937 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11940 return MF_NO_ACTION;
11944 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11947 if (IS_SB_ELEMENT(element))
11949 if (element == EL_SOKOBAN_FIELD_FULL)
11951 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11952 local_player->sokobanfields_still_needed++;
11955 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11957 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11958 local_player->sokobanfields_still_needed--;
11961 Feld[x][y] = EL_SOKOBAN_OBJECT;
11963 if (Back[x][y] == Back[nextx][nexty])
11964 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11965 else if (Back[x][y] != 0)
11966 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11969 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11972 if (local_player->sokobanfields_still_needed == 0 &&
11973 game.emulation == EMU_SOKOBAN)
11975 player->LevelSolved = player->GameOver = TRUE;
11976 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11980 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11982 InitMovingField(x, y, move_direction);
11983 GfxAction[x][y] = ACTION_PUSHING;
11985 if (mode == DF_SNAP)
11986 ContinueMoving(x, y);
11988 MovPos[x][y] = (dx != 0 ? dx : dy);
11990 Pushed[x][y] = TRUE;
11991 Pushed[nextx][nexty] = TRUE;
11993 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11994 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11996 player->push_delay_value = -1; /* get new value later */
11998 #if USE_PUSH_BUGFIX
11999 /* now: check for element change _after_ element has been pushed! */
12001 if (game.use_change_when_pushing_bug)
12003 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12006 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12007 player->index_bit, dig_side);
12008 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
12009 player->index_bit, dig_side);
12015 /* check for element change _after_ element has been pushed! */
12019 /* !!! TEST ONLY !!! */
12020 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12021 player->index_bit, dig_side);
12022 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12023 player->index_bit, dig_side);
12025 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12026 player->index_bit, dig_side);
12027 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12028 player->index_bit, dig_side);
12036 else if (IS_SWITCHABLE(element))
12038 if (PLAYER_SWITCHING(player, x, y))
12040 CheckTriggeredElementChangeByPlayer(x,y, element,
12041 CE_OTHER_GETS_PRESSED,
12042 player->index_bit, dig_side);
12047 player->is_switching = TRUE;
12048 player->switch_x = x;
12049 player->switch_y = y;
12051 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12053 if (element == EL_ROBOT_WHEEL)
12055 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12059 DrawLevelField(x, y);
12061 else if (element == EL_SP_TERMINAL)
12065 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12067 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12069 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12070 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12073 else if (IS_BELT_SWITCH(element))
12075 ToggleBeltSwitch(x, y);
12077 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12078 element == EL_SWITCHGATE_SWITCH_DOWN)
12080 ToggleSwitchgateSwitch(x, y);
12082 else if (element == EL_LIGHT_SWITCH ||
12083 element == EL_LIGHT_SWITCH_ACTIVE)
12085 ToggleLightSwitch(x, y);
12088 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12089 SND_LIGHT_SWITCH_ACTIVATING :
12090 SND_LIGHT_SWITCH_DEACTIVATING);
12093 else if (element == EL_TIMEGATE_SWITCH)
12095 ActivateTimegateSwitch(x, y);
12097 else if (element == EL_BALLOON_SWITCH_LEFT ||
12098 element == EL_BALLOON_SWITCH_RIGHT ||
12099 element == EL_BALLOON_SWITCH_UP ||
12100 element == EL_BALLOON_SWITCH_DOWN ||
12101 element == EL_BALLOON_SWITCH_ANY)
12103 if (element == EL_BALLOON_SWITCH_ANY)
12104 game.balloon_dir = move_direction;
12106 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12107 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12108 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12109 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12112 else if (element == EL_LAMP)
12114 Feld[x][y] = EL_LAMP_ACTIVE;
12115 local_player->lights_still_needed--;
12117 ResetGfxAnimation(x, y);
12118 DrawLevelField(x, y);
12120 else if (element == EL_TIME_ORB_FULL)
12122 Feld[x][y] = EL_TIME_ORB_EMPTY;
12124 DrawGameValue_Time(TimeLeft);
12126 ResetGfxAnimation(x, y);
12127 DrawLevelField(x, y);
12130 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12134 CheckTriggeredElementChangeByPlayer(x, y, element,
12135 CE_OTHER_IS_SWITCHING,
12136 player->index_bit, dig_side);
12138 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12139 player->index_bit, dig_side);
12145 if (!PLAYER_SWITCHING(player, x, y))
12147 player->is_switching = TRUE;
12148 player->switch_x = x;
12149 player->switch_y = y;
12152 /* !!! TEST ONLY !!! */
12153 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12154 player->index_bit, dig_side);
12155 CheckTriggeredElementChangeByPlayer(x, y, element,
12156 CE_OTHER_IS_SWITCHING,
12157 player->index_bit, dig_side);
12159 CheckTriggeredElementChangeByPlayer(x, y, element,
12160 CE_OTHER_IS_SWITCHING,
12161 player->index_bit, dig_side);
12162 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12163 player->index_bit, dig_side);
12168 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12169 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12170 player->index_bit, dig_side);
12171 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12172 player->index_bit, dig_side);
12174 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12175 player->index_bit, dig_side);
12176 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12177 player->index_bit, dig_side);
12181 return MF_NO_ACTION;
12184 #if USE_NEW_PUSH_DELAY
12185 player->push_delay = -1;
12187 player->push_delay = 0;
12190 #if USE_PENGUIN_COLLECT_BUG
12191 if (is_player) /* function can also be called by EL_PENGUIN */
12194 if (Feld[x][y] != element) /* really digged/collected something */
12195 player->is_collecting = !player->is_digging;
12201 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12203 int jx = player->jx, jy = player->jy;
12204 int x = jx + dx, y = jy + dy;
12205 int snap_direction = (dx == -1 ? MV_LEFT :
12206 dx == +1 ? MV_RIGHT :
12208 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12211 if (player->MovPos != 0)
12214 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12218 if (!player->active || !IN_LEV_FIELD(x, y))
12226 if (player->MovPos == 0)
12227 player->is_pushing = FALSE;
12229 player->is_snapping = FALSE;
12231 if (player->MovPos == 0)
12233 player->is_moving = FALSE;
12234 player->is_digging = FALSE;
12235 player->is_collecting = FALSE;
12241 if (player->is_snapping)
12244 player->MovDir = snap_direction;
12247 if (player->MovPos == 0)
12250 player->is_moving = FALSE;
12251 player->is_digging = FALSE;
12252 player->is_collecting = FALSE;
12255 player->is_dropping = FALSE;
12257 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12260 player->is_snapping = TRUE;
12263 if (player->MovPos == 0)
12266 player->is_moving = FALSE;
12267 player->is_digging = FALSE;
12268 player->is_collecting = FALSE;
12272 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12273 DrawLevelField(player->last_jx, player->last_jy);
12276 DrawLevelField(x, y);
12285 boolean DropElement(struct PlayerInfo *player)
12287 int old_element, new_element;
12288 int dropx = player->jx, dropy = player->jy;
12289 int drop_direction = player->MovDir;
12291 int drop_side = drop_direction;
12293 static int trigger_sides[4] =
12295 CH_SIDE_LEFT, /* dropping left */
12296 CH_SIDE_RIGHT, /* dropping right */
12297 CH_SIDE_TOP, /* dropping up */
12298 CH_SIDE_BOTTOM, /* dropping down */
12300 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12302 int drop_element = (player->inventory_size > 0 ?
12303 player->inventory_element[player->inventory_size - 1] :
12304 player->inventory_infinite_element != EL_UNDEFINED ?
12305 player->inventory_infinite_element :
12306 player->dynabombs_left > 0 ?
12307 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12310 if (IS_THROWABLE(drop_element))
12312 dropx += GET_DX_FROM_DIR(drop_direction);
12313 dropy += GET_DY_FROM_DIR(drop_direction);
12315 if (!IN_LEV_FIELD(dropx, dropy))
12319 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12320 new_element = drop_element; /* default: no change when dropping */
12322 /* check if player is active, not moving and ready to drop */
12323 if (!player->active || player->MovPos || player->drop_delay > 0)
12326 /* check if player has anything that can be dropped */
12328 if (new_element == EL_UNDEFINED)
12331 if (player->inventory_size == 0 &&
12332 player->inventory_infinite_element == EL_UNDEFINED &&
12333 player->dynabombs_left == 0)
12337 /* check if anything can be dropped at the current position */
12338 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12341 /* collected custom elements can only be dropped on empty fields */
12343 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12346 if (player->inventory_size > 0 &&
12347 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12348 && old_element != EL_EMPTY)
12352 if (old_element != EL_EMPTY)
12353 Back[dropx][dropy] = old_element; /* store old element on this field */
12355 ResetGfxAnimation(dropx, dropy);
12356 ResetRandomAnimationValue(dropx, dropy);
12358 if (player->inventory_size > 0 ||
12359 player->inventory_infinite_element != EL_UNDEFINED)
12361 if (player->inventory_size > 0)
12363 player->inventory_size--;
12366 new_element = player->inventory_element[player->inventory_size];
12369 DrawGameValue_Dynamite(local_player->inventory_size);
12371 if (new_element == EL_DYNAMITE)
12372 new_element = EL_DYNAMITE_ACTIVE;
12373 else if (new_element == EL_SP_DISK_RED)
12374 new_element = EL_SP_DISK_RED_ACTIVE;
12377 Feld[dropx][dropy] = new_element;
12379 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12380 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12381 el2img(Feld[dropx][dropy]), 0);
12383 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12386 /* needed if previous element just changed to "empty" in the last frame */
12387 Changed[dropx][dropy] = 0; /* allow another change */
12391 /* !!! TEST ONLY !!! */
12392 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12393 player->index_bit, drop_side);
12394 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12395 CE_OTHER_GETS_DROPPED,
12396 player->index_bit, drop_side);
12398 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12399 CE_OTHER_GETS_DROPPED,
12400 player->index_bit, drop_side);
12401 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12402 player->index_bit, drop_side);
12405 TestIfElementTouchesCustomElement(dropx, dropy);
12407 else /* player is dropping a dyna bomb */
12409 player->dynabombs_left--;
12412 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12415 Feld[dropx][dropy] = new_element;
12417 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12418 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12419 el2img(Feld[dropx][dropy]), 0);
12421 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12428 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12431 InitField_WithBug1(dropx, dropy, FALSE);
12433 InitField(dropx, dropy, FALSE);
12434 if (CAN_MOVE(Feld[dropx][dropy]))
12435 InitMovDir(dropx, dropy);
12439 new_element = Feld[dropx][dropy]; /* element might have changed */
12441 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12442 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12445 int move_stepsize = element_info[new_element].move_stepsize;
12447 int move_direction, nextx, nexty;
12449 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12450 MovDir[dropx][dropy] = drop_direction;
12452 move_direction = MovDir[dropx][dropy];
12453 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12454 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12457 Changed[dropx][dropy] = 0; /* allow another change */
12458 CheckCollision[dropx][dropy] = 2;
12461 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12464 WasJustMoving[dropx][dropy] = 3;
12467 InitMovingField(dropx, dropy, move_direction);
12468 ContinueMoving(dropx, dropy);
12473 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12476 Changed[dropx][dropy] = 0; /* allow another change */
12479 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12481 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12482 CE_HITTING_SOMETHING, move_direction);
12490 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12495 player->drop_delay = 8 + 8 + 8;
12499 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12504 player->is_dropping = TRUE;
12510 /* ------------------------------------------------------------------------- */
12511 /* game sound playing functions */
12512 /* ------------------------------------------------------------------------- */
12514 static int *loop_sound_frame = NULL;
12515 static int *loop_sound_volume = NULL;
12517 void InitPlayLevelSound()
12519 int num_sounds = getSoundListSize();
12521 checked_free(loop_sound_frame);
12522 checked_free(loop_sound_volume);
12524 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12525 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12528 static void PlayLevelSound(int x, int y, int nr)
12530 int sx = SCREENX(x), sy = SCREENY(y);
12531 int volume, stereo_position;
12532 int max_distance = 8;
12533 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12535 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12536 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12539 if (!IN_LEV_FIELD(x, y) ||
12540 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12541 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12544 volume = SOUND_MAX_VOLUME;
12546 if (!IN_SCR_FIELD(sx, sy))
12548 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12549 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12551 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12554 stereo_position = (SOUND_MAX_LEFT +
12555 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12556 (SCR_FIELDX + 2 * max_distance));
12558 if (IS_LOOP_SOUND(nr))
12560 /* This assures that quieter loop sounds do not overwrite louder ones,
12561 while restarting sound volume comparison with each new game frame. */
12563 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12566 loop_sound_volume[nr] = volume;
12567 loop_sound_frame[nr] = FrameCounter;
12570 PlaySoundExt(nr, volume, stereo_position, type);
12573 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12575 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12576 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12577 y < LEVELY(BY1) ? LEVELY(BY1) :
12578 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12582 static void PlayLevelSoundAction(int x, int y, int action)
12584 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12587 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12589 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12591 if (sound_effect != SND_UNDEFINED)
12592 PlayLevelSound(x, y, sound_effect);
12595 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12598 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12600 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12601 PlayLevelSound(x, y, sound_effect);
12604 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12606 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12608 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12609 PlayLevelSound(x, y, sound_effect);
12612 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12614 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12616 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12617 StopSound(sound_effect);
12620 static void PlayLevelMusic()
12622 if (levelset.music[level_nr] != MUS_UNDEFINED)
12623 PlayMusic(levelset.music[level_nr]); /* from config file */
12625 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12628 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12630 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12633 if (sample == SAMPLE_bug)
12634 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12640 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12644 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12648 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12652 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12656 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12660 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12664 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12667 case SAMPLE_android_clone:
12668 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12671 case SAMPLE_android_move:
12672 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12675 case SAMPLE_spring:
12676 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12680 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12684 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12687 case SAMPLE_eater_eat:
12688 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12692 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12695 case SAMPLE_collect:
12696 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12699 case SAMPLE_diamond:
12700 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12703 case SAMPLE_squash:
12704 /* !!! CHECK THIS !!! */
12706 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12708 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12712 case SAMPLE_wonderfall:
12713 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12717 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12721 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12725 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12729 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12733 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12737 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12740 case SAMPLE_wonder:
12741 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12745 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12748 case SAMPLE_exit_open:
12749 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12752 case SAMPLE_exit_leave:
12753 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12756 case SAMPLE_dynamite:
12757 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12761 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12765 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12769 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12773 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12777 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12781 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12785 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12790 void RaiseScore(int value)
12792 local_player->score += value;
12794 DrawGameValue_Score(local_player->score);
12797 void RaiseScoreElement(int element)
12802 case EL_BD_DIAMOND:
12803 case EL_EMERALD_YELLOW:
12804 case EL_EMERALD_RED:
12805 case EL_EMERALD_PURPLE:
12806 case EL_SP_INFOTRON:
12807 RaiseScore(level.score[SC_EMERALD]);
12810 RaiseScore(level.score[SC_DIAMOND]);
12813 RaiseScore(level.score[SC_CRYSTAL]);
12816 RaiseScore(level.score[SC_PEARL]);
12819 case EL_BD_BUTTERFLY:
12820 case EL_SP_ELECTRON:
12821 RaiseScore(level.score[SC_BUG]);
12824 case EL_BD_FIREFLY:
12825 case EL_SP_SNIKSNAK:
12826 RaiseScore(level.score[SC_SPACESHIP]);
12829 case EL_DARK_YAMYAM:
12830 RaiseScore(level.score[SC_YAMYAM]);
12833 RaiseScore(level.score[SC_ROBOT]);
12836 RaiseScore(level.score[SC_PACMAN]);
12839 RaiseScore(level.score[SC_NUT]);
12842 case EL_SP_DISK_RED:
12843 case EL_DYNABOMB_INCREASE_NUMBER:
12844 case EL_DYNABOMB_INCREASE_SIZE:
12845 case EL_DYNABOMB_INCREASE_POWER:
12846 RaiseScore(level.score[SC_DYNAMITE]);
12848 case EL_SHIELD_NORMAL:
12849 case EL_SHIELD_DEADLY:
12850 RaiseScore(level.score[SC_SHIELD]);
12852 case EL_EXTRA_TIME:
12853 RaiseScore(level.score[SC_TIME_BONUS]);
12867 RaiseScore(level.score[SC_KEY]);
12870 RaiseScore(element_info[element].collect_score);
12875 void RequestQuitGame(boolean ask_if_really_quit)
12877 if (AllPlayersGone ||
12878 !ask_if_really_quit ||
12879 level_editor_test_game ||
12880 Request("Do you really want to quit the game ?",
12881 REQ_ASK | REQ_STAY_CLOSED))
12883 #if defined(NETWORK_AVALIABLE)
12884 if (options.network)
12885 SendToServer_StopPlaying();
12889 game_status = GAME_MODE_MAIN;
12897 if (tape.playing && tape.deactivate_display)
12898 TapeDeactivateDisplayOff(TRUE);
12901 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12904 if (tape.playing && tape.deactivate_display)
12905 TapeDeactivateDisplayOn();
12912 /* ---------- new game button stuff ---------------------------------------- */
12914 /* graphic position values for game buttons */
12915 #define GAME_BUTTON_XSIZE 30
12916 #define GAME_BUTTON_YSIZE 30
12917 #define GAME_BUTTON_XPOS 5
12918 #define GAME_BUTTON_YPOS 215
12919 #define SOUND_BUTTON_XPOS 5
12920 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12922 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12923 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12924 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12925 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12926 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12927 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12934 } gamebutton_info[NUM_GAME_BUTTONS] =
12937 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12942 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12943 GAME_CTRL_ID_PAUSE,
12947 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12952 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12953 SOUND_CTRL_ID_MUSIC,
12954 "background music on/off"
12957 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12958 SOUND_CTRL_ID_LOOPS,
12959 "sound loops on/off"
12962 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12963 SOUND_CTRL_ID_SIMPLE,
12964 "normal sounds on/off"
12968 void CreateGameButtons()
12972 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12974 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12975 struct GadgetInfo *gi;
12978 unsigned long event_mask;
12979 int gd_xoffset, gd_yoffset;
12980 int gd_x1, gd_x2, gd_y1, gd_y2;
12983 gd_xoffset = gamebutton_info[i].x;
12984 gd_yoffset = gamebutton_info[i].y;
12985 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12986 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12988 if (id == GAME_CTRL_ID_STOP ||
12989 id == GAME_CTRL_ID_PAUSE ||
12990 id == GAME_CTRL_ID_PLAY)
12992 button_type = GD_TYPE_NORMAL_BUTTON;
12994 event_mask = GD_EVENT_RELEASED;
12995 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12996 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13000 button_type = GD_TYPE_CHECK_BUTTON;
13002 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13003 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13004 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13005 event_mask = GD_EVENT_PRESSED;
13006 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13007 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13010 gi = CreateGadget(GDI_CUSTOM_ID, id,
13011 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13012 GDI_X, DX + gd_xoffset,
13013 GDI_Y, DY + gd_yoffset,
13014 GDI_WIDTH, GAME_BUTTON_XSIZE,
13015 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13016 GDI_TYPE, button_type,
13017 GDI_STATE, GD_BUTTON_UNPRESSED,
13018 GDI_CHECKED, checked,
13019 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13020 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13021 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13022 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13023 GDI_EVENT_MASK, event_mask,
13024 GDI_CALLBACK_ACTION, HandleGameButtons,
13028 Error(ERR_EXIT, "cannot create gadget");
13030 game_gadget[id] = gi;
13034 void FreeGameButtons()
13038 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13039 FreeGadget(game_gadget[i]);
13042 static void MapGameButtons()
13046 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13047 MapGadget(game_gadget[i]);
13050 void UnmapGameButtons()
13054 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13055 UnmapGadget(game_gadget[i]);
13058 static void HandleGameButtons(struct GadgetInfo *gi)
13060 int id = gi->custom_id;
13062 if (game_status != GAME_MODE_PLAYING)
13067 case GAME_CTRL_ID_STOP:
13068 RequestQuitGame(TRUE);
13071 case GAME_CTRL_ID_PAUSE:
13072 if (options.network)
13074 #if defined(NETWORK_AVALIABLE)
13076 SendToServer_ContinuePlaying();
13078 SendToServer_PausePlaying();
13082 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13085 case GAME_CTRL_ID_PLAY:
13088 #if defined(NETWORK_AVALIABLE)
13089 if (options.network)
13090 SendToServer_ContinuePlaying();
13094 tape.pausing = FALSE;
13095 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13100 case SOUND_CTRL_ID_MUSIC:
13101 if (setup.sound_music)
13103 setup.sound_music = FALSE;
13106 else if (audio.music_available)
13108 setup.sound = setup.sound_music = TRUE;
13110 SetAudioMode(setup.sound);
13116 case SOUND_CTRL_ID_LOOPS:
13117 if (setup.sound_loops)
13118 setup.sound_loops = FALSE;
13119 else if (audio.loops_available)
13121 setup.sound = setup.sound_loops = TRUE;
13122 SetAudioMode(setup.sound);
13126 case SOUND_CTRL_ID_SIMPLE:
13127 if (setup.sound_simple)
13128 setup.sound_simple = FALSE;
13129 else if (audio.sound_available)
13131 setup.sound = setup.sound_simple = TRUE;
13132 SetAudioMode(setup.sound);