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)
3396 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3397 behaviour, for example when touching a yamyam that explodes to rocks
3398 with active deadly shield, a rock is created under the player !!! */
3399 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3401 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3402 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3403 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3405 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3408 if (IS_ACTIVE_BOMB(element))
3410 /* re-activate things under the bomb like gate or penguin */
3412 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3415 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3420 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3421 element_info[Feld[x][y]].token_name,
3422 Store[x][y], Store2[x][y]);
3429 /* save walkable background elements while explosion on same tile */
3431 if (IS_INDESTRUCTIBLE(element))
3432 Back[x][y] = element;
3436 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3437 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3438 Back[x][y] = element;
3440 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3441 (x != ex || y != ey))
3442 Back[x][y] = element;
3445 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3446 Back[x][y] = element;
3450 /* ignite explodable elements reached by other explosion */
3451 if (element == EL_EXPLOSION)
3452 element = Store2[x][y];
3455 if (AmoebaNr[x][y] &&
3456 (element == EL_AMOEBA_FULL ||
3457 element == EL_BD_AMOEBA ||
3458 element == EL_AMOEBA_GROWING))
3460 AmoebaCnt[AmoebaNr[x][y]]--;
3461 AmoebaCnt2[AmoebaNr[x][y]]--;
3467 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3469 switch(StorePlayer[ex][ey])
3472 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3475 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3478 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3482 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3487 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3488 Store[x][y] = EL_EMPTY;
3490 if (game.emulation == EMU_SUPAPLEX)
3491 Store[x][y] = EL_EMPTY;
3494 else if (center_element == EL_MOLE)
3495 Store[x][y] = EL_EMERALD_RED;
3496 else if (center_element == EL_PENGUIN)
3497 Store[x][y] = EL_EMERALD_PURPLE;
3498 else if (center_element == EL_BUG)
3499 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3500 else if (center_element == EL_BD_BUTTERFLY)
3501 Store[x][y] = EL_BD_DIAMOND;
3502 else if (center_element == EL_SP_ELECTRON)
3503 Store[x][y] = EL_SP_INFOTRON;
3504 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3505 Store[x][y] = level.amoeba_content;
3506 else if (center_element == EL_YAMYAM)
3507 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3508 else if (IS_CUSTOM_ELEMENT(center_element) &&
3509 element_info[center_element].content[xx][yy] != EL_EMPTY)
3510 Store[x][y] = element_info[center_element].content[xx][yy];
3511 else if (element == EL_WALL_EMERALD)
3512 Store[x][y] = EL_EMERALD;
3513 else if (element == EL_WALL_DIAMOND)
3514 Store[x][y] = EL_DIAMOND;
3515 else if (element == EL_WALL_BD_DIAMOND)
3516 Store[x][y] = EL_BD_DIAMOND;
3517 else if (element == EL_WALL_EMERALD_YELLOW)
3518 Store[x][y] = EL_EMERALD_YELLOW;
3519 else if (element == EL_WALL_EMERALD_RED)
3520 Store[x][y] = EL_EMERALD_RED;
3521 else if (element == EL_WALL_EMERALD_PURPLE)
3522 Store[x][y] = EL_EMERALD_PURPLE;
3523 else if (element == EL_WALL_PEARL)
3524 Store[x][y] = EL_PEARL;
3525 else if (element == EL_WALL_CRYSTAL)
3526 Store[x][y] = EL_CRYSTAL;
3527 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3528 Store[x][y] = element_info[element].content[1][1];
3530 Store[x][y] = EL_EMPTY;
3532 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3533 center_element == EL_AMOEBA_TO_DIAMOND)
3534 Store2[x][y] = element;
3537 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3538 element_info[Store2[x][y]].token_name);
3542 if (AmoebaNr[x][y] &&
3543 (element == EL_AMOEBA_FULL ||
3544 element == EL_BD_AMOEBA ||
3545 element == EL_AMOEBA_GROWING))
3547 AmoebaCnt[AmoebaNr[x][y]]--;
3548 AmoebaCnt2[AmoebaNr[x][y]]--;
3554 MovDir[x][y] = MovPos[x][y] = 0;
3555 GfxDir[x][y] = MovDir[x][y];
3560 Feld[x][y] = EL_EXPLOSION;
3562 GfxElement[x][y] = center_element;
3564 GfxElement[x][y] = EL_UNDEFINED;
3567 ExplodePhase[x][y] = 1;
3569 ExplodeDelay[x][y] = last_phase;
3574 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3576 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3583 if (center_element == EL_YAMYAM)
3584 game.yamyam_content_nr =
3585 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3588 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3589 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3603 GfxFrame[x][y] = 0; /* restart explosion animation */
3607 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3611 last_phase = ExplodeDelay[x][y];
3614 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3618 /* activate this even in non-DEBUG version until cause for crash in
3619 getGraphicAnimationFrame() (see below) is found and eliminated */
3623 if (GfxElement[x][y] == EL_UNDEFINED)
3626 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3627 printf("Explode(): This should never happen!\n");
3630 GfxElement[x][y] = EL_EMPTY;
3636 border_element = Store2[x][y];
3638 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3639 border_element = StorePlayer[x][y];
3641 if (IS_PLAYER(x, y))
3642 border_element = StorePlayer[x][y];
3646 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3647 element_info[border_element].token_name, Store2[x][y]);
3651 printf("::: phase == %d\n", phase);
3654 if (phase == element_info[border_element].ignition_delay ||
3655 phase == last_phase)
3657 boolean border_explosion = FALSE;
3661 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3662 !PLAYER_EXPLOSION_PROTECTED(x, y))
3664 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3667 if (IS_PLAYER(x, y))
3670 KillHeroUnlessExplosionProtected(x, y);
3671 border_explosion = TRUE;
3674 if (phase == last_phase)
3675 printf("::: IS_PLAYER\n");
3678 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3681 printf("::: %d,%d: %d %s\n", x, y, border_element,
3682 element_info[border_element].token_name);
3685 Feld[x][y] = Store2[x][y];
3688 border_explosion = TRUE;
3691 if (phase == last_phase)
3692 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3695 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3697 AmoebeUmwandeln(x, y);
3699 border_explosion = TRUE;
3702 if (phase == last_phase)
3703 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3704 element_info[border_element].explosion_delay,
3705 element_info[border_element].ignition_delay,
3711 /* if an element just explodes due to another explosion (chain-reaction),
3712 do not immediately end the new explosion when it was the last frame of
3713 the explosion (as it would be done in the following "if"-statement!) */
3714 if (border_explosion && phase == last_phase)
3721 if (phase == first_phase_after_start)
3723 int element = Store2[x][y];
3725 if (element == EL_BLACK_ORB)
3727 Feld[x][y] = Store2[x][y];
3732 else if (phase == half_phase)
3734 int element = Store2[x][y];
3736 if (IS_PLAYER(x, y))
3737 KillHeroUnlessExplosionProtected(x, y);
3738 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3740 Feld[x][y] = Store2[x][y];
3744 else if (element == EL_AMOEBA_TO_DIAMOND)
3745 AmoebeUmwandeln(x, y);
3749 if (phase == last_phase)
3754 printf("::: done: phase == %d\n", phase);
3758 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3761 element = Feld[x][y] = Store[x][y];
3762 Store[x][y] = Store2[x][y] = 0;
3763 GfxElement[x][y] = EL_UNDEFINED;
3765 /* player can escape from explosions and might therefore be still alive */
3766 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3767 element <= EL_PLAYER_IS_EXPLODING_4)
3768 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3770 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3771 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3772 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3775 /* restore probably existing indestructible background element */
3776 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3777 element = Feld[x][y] = Back[x][y];
3780 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3781 GfxDir[x][y] = MV_NO_MOVING;
3782 ChangeDelay[x][y] = 0;
3783 ChangePage[x][y] = -1;
3786 InitField_WithBug2(x, y, FALSE);
3788 InitField(x, y, FALSE);
3790 /* !!! not needed !!! */
3792 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3793 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3796 if (CAN_MOVE(element))
3801 DrawLevelField(x, y);
3803 TestIfElementTouchesCustomElement(x, y);
3805 if (GFX_CRUMBLED(element))
3806 DrawLevelFieldCrumbledSandNeighbours(x, y);
3808 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3809 StorePlayer[x][y] = 0;
3811 if (ELEM_IS_PLAYER(element))
3812 RelocatePlayer(x, y, element);
3815 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3817 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3821 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3823 int stored = Store[x][y];
3824 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3825 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3829 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3831 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3835 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3839 printf("::: %d / %d [%d - %d]\n",
3840 GfxFrame[x][y], phase - delay, phase, delay);
3844 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3845 element_info[GfxElement[x][y]].token_name,
3850 DrawLevelFieldCrumbledSand(x, y);
3852 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3854 DrawLevelElement(x, y, Back[x][y]);
3855 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3857 else if (IS_WALKABLE_UNDER(Back[x][y]))
3859 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3860 DrawLevelElementThruMask(x, y, Back[x][y]);
3862 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3863 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3867 void DynaExplode(int ex, int ey)
3870 int dynabomb_element = Feld[ex][ey];
3871 int dynabomb_size = 1;
3872 boolean dynabomb_xl = FALSE;
3873 struct PlayerInfo *player;
3874 static int xy[4][2] =
3882 if (IS_ACTIVE_BOMB(dynabomb_element))
3884 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3885 dynabomb_size = player->dynabomb_size;
3886 dynabomb_xl = player->dynabomb_xl;
3887 player->dynabombs_left++;
3890 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3892 for (i = 0; i < NUM_DIRECTIONS; i++)
3894 for (j = 1; j <= dynabomb_size; j++)
3896 int x = ex + j * xy[i][0];
3897 int y = ey + j * xy[i][1];
3900 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3903 element = Feld[x][y];
3905 /* do not restart explosions of fields with active bombs */
3906 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3909 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3913 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3914 !IS_DIGGABLE(element) && !dynabomb_xl)
3917 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3918 !CAN_GROW_INTO(element) && !dynabomb_xl)
3922 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3923 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3924 element != EL_SAND && !dynabomb_xl)
3931 void Bang(int x, int y)
3934 int element = MovingOrBlocked2Element(x, y);
3936 int element = Feld[x][y];
3940 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3942 if (IS_PLAYER(x, y))
3945 struct PlayerInfo *player = PLAYERINFO(x, y);
3947 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3948 player->element_nr);
3953 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3955 if (game.emulation == EMU_SUPAPLEX)
3956 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3958 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3963 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3971 case EL_BD_BUTTERFLY:
3974 case EL_DARK_YAMYAM:
3978 RaiseScoreElement(element);
3979 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3981 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3982 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3983 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3984 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3985 case EL_DYNABOMB_INCREASE_NUMBER:
3986 case EL_DYNABOMB_INCREASE_SIZE:
3987 case EL_DYNABOMB_INCREASE_POWER:
3992 case EL_LAMP_ACTIVE:
3994 case EL_AMOEBA_TO_DIAMOND:
3996 if (IS_PLAYER(x, y))
3997 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3999 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4003 if (element_info[element].explosion_type == EXPLODES_CROSS)
4005 if (CAN_EXPLODE_CROSS(element))
4008 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4013 else if (element_info[element].explosion_type == EXPLODES_1X1)
4015 else if (CAN_EXPLODE_1X1(element))
4017 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4019 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4023 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
4026 void SplashAcid(int x, int y)
4029 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4030 (!IN_LEV_FIELD(x - 1, y - 2) ||
4031 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4032 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4034 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4035 (!IN_LEV_FIELD(x + 1, y - 2) ||
4036 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4037 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4039 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4041 /* input: position of element entering acid (obsolete) */
4043 int element = Feld[x][y];
4045 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4048 if (element != EL_ACID_SPLASH_LEFT &&
4049 element != EL_ACID_SPLASH_RIGHT)
4051 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4053 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4054 (!IN_LEV_FIELD(x - 1, y - 1) ||
4055 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4056 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4058 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4059 (!IN_LEV_FIELD(x + 1, y - 1) ||
4060 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4061 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4066 static void InitBeltMovement()
4068 static int belt_base_element[4] =
4070 EL_CONVEYOR_BELT_1_LEFT,
4071 EL_CONVEYOR_BELT_2_LEFT,
4072 EL_CONVEYOR_BELT_3_LEFT,
4073 EL_CONVEYOR_BELT_4_LEFT
4075 static int belt_base_active_element[4] =
4077 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4078 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4079 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4080 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4085 /* set frame order for belt animation graphic according to belt direction */
4086 for (i = 0; i < NUM_BELTS; i++)
4090 for (j = 0; j < NUM_BELT_PARTS; j++)
4092 int element = belt_base_active_element[belt_nr] + j;
4093 int graphic = el2img(element);
4095 if (game.belt_dir[i] == MV_LEFT)
4096 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4098 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4102 for (y = 0; y < lev_fieldy; y++)
4104 for (x = 0; x < lev_fieldx; x++)
4106 int element = Feld[x][y];
4108 for (i = 0; i < NUM_BELTS; i++)
4110 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4112 int e_belt_nr = getBeltNrFromBeltElement(element);
4115 if (e_belt_nr == belt_nr)
4117 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4119 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4127 static void ToggleBeltSwitch(int x, int y)
4129 static int belt_base_element[4] =
4131 EL_CONVEYOR_BELT_1_LEFT,
4132 EL_CONVEYOR_BELT_2_LEFT,
4133 EL_CONVEYOR_BELT_3_LEFT,
4134 EL_CONVEYOR_BELT_4_LEFT
4136 static int belt_base_active_element[4] =
4138 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4139 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4140 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4141 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4143 static int belt_base_switch_element[4] =
4145 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4146 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4147 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4148 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4150 static int belt_move_dir[4] =
4158 int element = Feld[x][y];
4159 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4160 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4161 int belt_dir = belt_move_dir[belt_dir_nr];
4164 if (!IS_BELT_SWITCH(element))
4167 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4168 game.belt_dir[belt_nr] = belt_dir;
4170 if (belt_dir_nr == 3)
4173 /* set frame order for belt animation graphic according to belt direction */
4174 for (i = 0; i < NUM_BELT_PARTS; i++)
4176 int element = belt_base_active_element[belt_nr] + i;
4177 int graphic = el2img(element);
4179 if (belt_dir == MV_LEFT)
4180 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4182 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4185 for (yy = 0; yy < lev_fieldy; yy++)
4187 for (xx = 0; xx < lev_fieldx; xx++)
4189 int element = Feld[xx][yy];
4191 if (IS_BELT_SWITCH(element))
4193 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4195 if (e_belt_nr == belt_nr)
4197 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4198 DrawLevelField(xx, yy);
4201 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4203 int e_belt_nr = getBeltNrFromBeltElement(element);
4205 if (e_belt_nr == belt_nr)
4207 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4209 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4210 DrawLevelField(xx, yy);
4213 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4215 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4217 if (e_belt_nr == belt_nr)
4219 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4221 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4222 DrawLevelField(xx, yy);
4229 static void ToggleSwitchgateSwitch(int x, int y)
4233 game.switchgate_pos = !game.switchgate_pos;
4235 for (yy = 0; yy < lev_fieldy; yy++)
4237 for (xx = 0; xx < lev_fieldx; xx++)
4239 int element = Feld[xx][yy];
4241 if (element == EL_SWITCHGATE_SWITCH_UP ||
4242 element == EL_SWITCHGATE_SWITCH_DOWN)
4244 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4245 DrawLevelField(xx, yy);
4247 else if (element == EL_SWITCHGATE_OPEN ||
4248 element == EL_SWITCHGATE_OPENING)
4250 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4252 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4254 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4257 else if (element == EL_SWITCHGATE_CLOSED ||
4258 element == EL_SWITCHGATE_CLOSING)
4260 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4262 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4264 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4271 static int getInvisibleActiveFromInvisibleElement(int element)
4273 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4274 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4275 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4279 static int getInvisibleFromInvisibleActiveElement(int element)
4281 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4282 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4283 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4287 static void RedrawAllLightSwitchesAndInvisibleElements()
4291 for (y = 0; y < lev_fieldy; y++)
4293 for (x = 0; x < lev_fieldx; x++)
4295 int element = Feld[x][y];
4297 if (element == EL_LIGHT_SWITCH &&
4298 game.light_time_left > 0)
4300 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4301 DrawLevelField(x, y);
4303 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4304 game.light_time_left == 0)
4306 Feld[x][y] = EL_LIGHT_SWITCH;
4307 DrawLevelField(x, y);
4309 else if (element == EL_INVISIBLE_STEELWALL ||
4310 element == EL_INVISIBLE_WALL ||
4311 element == EL_INVISIBLE_SAND)
4313 if (game.light_time_left > 0)
4314 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4316 DrawLevelField(x, y);
4318 /* uncrumble neighbour fields, if needed */
4319 if (element == EL_INVISIBLE_SAND)
4320 DrawLevelFieldCrumbledSandNeighbours(x, y);
4322 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4323 element == EL_INVISIBLE_WALL_ACTIVE ||
4324 element == EL_INVISIBLE_SAND_ACTIVE)
4326 if (game.light_time_left == 0)
4327 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4329 DrawLevelField(x, y);
4331 /* re-crumble neighbour fields, if needed */
4332 if (element == EL_INVISIBLE_SAND)
4333 DrawLevelFieldCrumbledSandNeighbours(x, y);
4339 static void ToggleLightSwitch(int x, int y)
4341 int element = Feld[x][y];
4343 game.light_time_left =
4344 (element == EL_LIGHT_SWITCH ?
4345 level.time_light * FRAMES_PER_SECOND : 0);
4347 RedrawAllLightSwitchesAndInvisibleElements();
4350 static void ActivateTimegateSwitch(int x, int y)
4354 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4356 for (yy = 0; yy < lev_fieldy; yy++)
4358 for (xx = 0; xx < lev_fieldx; xx++)
4360 int element = Feld[xx][yy];
4362 if (element == EL_TIMEGATE_CLOSED ||
4363 element == EL_TIMEGATE_CLOSING)
4365 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4366 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4370 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4372 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4373 DrawLevelField(xx, yy);
4380 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4383 void Impact(int x, int y)
4385 boolean lastline = (y == lev_fieldy-1);
4386 boolean object_hit = FALSE;
4387 boolean impact = (lastline || object_hit);
4388 int element = Feld[x][y];
4389 int smashed = EL_STEELWALL;
4391 if (!lastline) /* check if element below was hit */
4393 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4396 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4397 MovDir[x][y + 1] != MV_DOWN ||
4398 MovPos[x][y + 1] <= TILEY / 2));
4401 object_hit = !IS_FREE(x, y + 1);
4404 /* do not smash moving elements that left the smashed field in time */
4405 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4406 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4410 smashed = MovingOrBlocked2Element(x, y + 1);
4412 impact = (lastline || object_hit);
4415 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4417 SplashAcid(x, y + 1);
4421 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4422 /* only reset graphic animation if graphic really changes after impact */
4424 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4426 ResetGfxAnimation(x, y);
4427 DrawLevelField(x, y);
4430 if (impact && CAN_EXPLODE_IMPACT(element))
4435 else if (impact && element == EL_PEARL)
4437 ResetGfxAnimation(x, y);
4439 Feld[x][y] = EL_PEARL_BREAKING;
4440 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4443 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4445 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4450 if (impact && element == EL_AMOEBA_DROP)
4452 if (object_hit && IS_PLAYER(x, y + 1))
4453 KillHeroUnlessEnemyProtected(x, y + 1);
4454 else if (object_hit && smashed == EL_PENGUIN)
4458 Feld[x][y] = EL_AMOEBA_GROWING;
4459 Store[x][y] = EL_AMOEBA_WET;
4461 ResetRandomAnimationValue(x, y);
4466 if (object_hit) /* check which object was hit */
4468 if (CAN_PASS_MAGIC_WALL(element) &&
4469 (smashed == EL_MAGIC_WALL ||
4470 smashed == EL_BD_MAGIC_WALL))
4473 int activated_magic_wall =
4474 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4475 EL_BD_MAGIC_WALL_ACTIVE);
4477 /* activate magic wall / mill */
4478 for (yy = 0; yy < lev_fieldy; yy++)
4479 for (xx = 0; xx < lev_fieldx; xx++)
4480 if (Feld[xx][yy] == smashed)
4481 Feld[xx][yy] = activated_magic_wall;
4483 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4484 game.magic_wall_active = TRUE;
4486 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4487 SND_MAGIC_WALL_ACTIVATING :
4488 SND_BD_MAGIC_WALL_ACTIVATING));
4491 if (IS_PLAYER(x, y + 1))
4493 if (CAN_SMASH_PLAYER(element))
4495 KillHeroUnlessEnemyProtected(x, y + 1);
4499 else if (smashed == EL_PENGUIN)
4501 if (CAN_SMASH_PLAYER(element))
4507 else if (element == EL_BD_DIAMOND)
4509 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4515 else if (((element == EL_SP_INFOTRON ||
4516 element == EL_SP_ZONK) &&
4517 (smashed == EL_SP_SNIKSNAK ||
4518 smashed == EL_SP_ELECTRON ||
4519 smashed == EL_SP_DISK_ORANGE)) ||
4520 (element == EL_SP_INFOTRON &&
4521 smashed == EL_SP_DISK_YELLOW))
4527 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4533 else if (CAN_SMASH_EVERYTHING(element))
4535 if (IS_CLASSIC_ENEMY(smashed) ||
4536 CAN_EXPLODE_SMASHED(smashed))
4541 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4543 if (smashed == EL_LAMP ||
4544 smashed == EL_LAMP_ACTIVE)
4549 else if (smashed == EL_NUT)
4551 Feld[x][y + 1] = EL_NUT_BREAKING;
4552 PlayLevelSound(x, y, SND_NUT_BREAKING);
4553 RaiseScoreElement(EL_NUT);
4556 else if (smashed == EL_PEARL)
4558 ResetGfxAnimation(x, y);
4560 Feld[x][y + 1] = EL_PEARL_BREAKING;
4561 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4564 else if (smashed == EL_DIAMOND)
4566 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4567 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4570 else if (IS_BELT_SWITCH(smashed))
4572 ToggleBeltSwitch(x, y + 1);
4574 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4575 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4577 ToggleSwitchgateSwitch(x, y + 1);
4579 else if (smashed == EL_LIGHT_SWITCH ||
4580 smashed == EL_LIGHT_SWITCH_ACTIVE)
4582 ToggleLightSwitch(x, y + 1);
4587 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4590 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4593 /* !!! TEST ONLY !!! */
4594 CheckElementChangeBySide(x, y + 1, smashed, element,
4595 CE_SWITCHED, CH_SIDE_TOP);
4596 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4597 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4599 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4600 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4601 CheckElementChangeBySide(x, y + 1, smashed, element,
4602 CE_SWITCHED, CH_SIDE_TOP);
4608 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4613 /* play sound of magic wall / mill */
4615 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4616 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4618 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4619 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4620 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4621 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4626 /* play sound of object that hits the ground */
4627 if (lastline || object_hit)
4628 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4631 inline static void TurnRoundExt(int x, int y)
4643 { 0, 0 }, { 0, 0 }, { 0, 0 },
4648 int left, right, back;
4652 { MV_DOWN, MV_UP, MV_RIGHT },
4653 { MV_UP, MV_DOWN, MV_LEFT },
4655 { MV_LEFT, MV_RIGHT, MV_DOWN },
4659 { MV_RIGHT, MV_LEFT, MV_UP }
4662 int element = Feld[x][y];
4663 int move_pattern = element_info[element].move_pattern;
4665 int old_move_dir = MovDir[x][y];
4666 int left_dir = turn[old_move_dir].left;
4667 int right_dir = turn[old_move_dir].right;
4668 int back_dir = turn[old_move_dir].back;
4670 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4671 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4672 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4673 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4675 int left_x = x + left_dx, left_y = y + left_dy;
4676 int right_x = x + right_dx, right_y = y + right_dy;
4677 int move_x = x + move_dx, move_y = y + move_dy;
4681 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4683 TestIfBadThingTouchesOtherBadThing(x, y);
4685 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4686 MovDir[x][y] = right_dir;
4687 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4688 MovDir[x][y] = left_dir;
4690 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4692 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4696 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4697 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4699 TestIfBadThingTouchesOtherBadThing(x, y);
4701 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4702 MovDir[x][y] = left_dir;
4703 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4704 MovDir[x][y] = right_dir;
4706 if ((element == EL_SPACESHIP ||
4707 element == EL_SP_SNIKSNAK ||
4708 element == EL_SP_ELECTRON)
4709 && MovDir[x][y] != old_move_dir)
4711 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4715 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4717 TestIfBadThingTouchesOtherBadThing(x, y);
4719 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4720 MovDir[x][y] = left_dir;
4721 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4722 MovDir[x][y] = right_dir;
4724 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4726 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4729 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4731 TestIfBadThingTouchesOtherBadThing(x, y);
4733 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4734 MovDir[x][y] = left_dir;
4735 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4736 MovDir[x][y] = right_dir;
4738 if (MovDir[x][y] != old_move_dir)
4742 else if (element == EL_YAMYAM)
4744 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4745 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4747 if (can_turn_left && can_turn_right)
4748 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4749 else if (can_turn_left)
4750 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4751 else if (can_turn_right)
4752 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4754 MovDir[x][y] = back_dir;
4756 MovDelay[x][y] = 16 + 16 * RND(3);
4758 else if (element == EL_DARK_YAMYAM)
4760 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4762 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4765 if (can_turn_left && can_turn_right)
4766 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4767 else if (can_turn_left)
4768 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4769 else if (can_turn_right)
4770 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4772 MovDir[x][y] = back_dir;
4774 MovDelay[x][y] = 16 + 16 * RND(3);
4776 else if (element == EL_PACMAN)
4778 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4779 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4781 if (can_turn_left && can_turn_right)
4782 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4783 else if (can_turn_left)
4784 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4785 else if (can_turn_right)
4786 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4788 MovDir[x][y] = back_dir;
4790 MovDelay[x][y] = 6 + RND(40);
4792 else if (element == EL_PIG)
4794 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4795 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4796 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4797 boolean should_turn_left, should_turn_right, should_move_on;
4799 int rnd = RND(rnd_value);
4801 should_turn_left = (can_turn_left &&
4803 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4804 y + back_dy + left_dy)));
4805 should_turn_right = (can_turn_right &&
4807 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4808 y + back_dy + right_dy)));
4809 should_move_on = (can_move_on &&
4812 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4813 y + move_dy + left_dy) ||
4814 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4815 y + move_dy + right_dy)));
4817 if (should_turn_left || should_turn_right || should_move_on)
4819 if (should_turn_left && should_turn_right && should_move_on)
4820 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4821 rnd < 2 * rnd_value / 3 ? right_dir :
4823 else if (should_turn_left && should_turn_right)
4824 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4825 else if (should_turn_left && should_move_on)
4826 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4827 else if (should_turn_right && should_move_on)
4828 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4829 else if (should_turn_left)
4830 MovDir[x][y] = left_dir;
4831 else if (should_turn_right)
4832 MovDir[x][y] = right_dir;
4833 else if (should_move_on)
4834 MovDir[x][y] = old_move_dir;
4836 else if (can_move_on && rnd > rnd_value / 8)
4837 MovDir[x][y] = old_move_dir;
4838 else if (can_turn_left && can_turn_right)
4839 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4840 else if (can_turn_left && rnd > rnd_value / 8)
4841 MovDir[x][y] = left_dir;
4842 else if (can_turn_right && rnd > rnd_value/8)
4843 MovDir[x][y] = right_dir;
4845 MovDir[x][y] = back_dir;
4847 xx = x + move_xy[MovDir[x][y]].x;
4848 yy = y + move_xy[MovDir[x][y]].y;
4851 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4852 if (!IN_LEV_FIELD(xx, yy) ||
4853 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4854 MovDir[x][y] = old_move_dir;
4856 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4857 MovDir[x][y] = old_move_dir;
4862 else if (element == EL_DRAGON)
4864 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4865 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4866 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4868 int rnd = RND(rnd_value);
4871 if (FrameCounter < 1 && x == 0 && y == 29)
4872 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4875 if (can_move_on && rnd > rnd_value / 8)
4876 MovDir[x][y] = old_move_dir;
4877 else if (can_turn_left && can_turn_right)
4878 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4879 else if (can_turn_left && rnd > rnd_value / 8)
4880 MovDir[x][y] = left_dir;
4881 else if (can_turn_right && rnd > rnd_value / 8)
4882 MovDir[x][y] = right_dir;
4884 MovDir[x][y] = back_dir;
4886 xx = x + move_xy[MovDir[x][y]].x;
4887 yy = y + move_xy[MovDir[x][y]].y;
4890 if (FrameCounter < 1 && x == 0 && y == 29)
4891 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4892 xx, yy, Feld[xx][yy],
4897 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4898 MovDir[x][y] = old_move_dir;
4900 if (!IS_FREE(xx, yy))
4901 MovDir[x][y] = old_move_dir;
4905 if (FrameCounter < 1 && x == 0 && y == 29)
4906 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4911 else if (element == EL_MOLE)
4913 boolean can_move_on =
4914 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4915 IS_AMOEBOID(Feld[move_x][move_y]) ||
4916 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4919 boolean can_turn_left =
4920 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4921 IS_AMOEBOID(Feld[left_x][left_y])));
4923 boolean can_turn_right =
4924 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4925 IS_AMOEBOID(Feld[right_x][right_y])));
4927 if (can_turn_left && can_turn_right)
4928 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4929 else if (can_turn_left)
4930 MovDir[x][y] = left_dir;
4932 MovDir[x][y] = right_dir;
4935 if (MovDir[x][y] != old_move_dir)
4938 else if (element == EL_BALLOON)
4940 MovDir[x][y] = game.balloon_dir;
4943 else if (element == EL_SPRING)
4946 if (MovDir[x][y] & MV_HORIZONTAL &&
4947 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4948 MovDir[x][y] = MV_NO_MOVING;
4950 if (MovDir[x][y] & MV_HORIZONTAL &&
4951 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4952 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4953 MovDir[x][y] = MV_NO_MOVING;
4958 else if (element == EL_ROBOT ||
4959 element == EL_SATELLITE ||
4960 element == EL_PENGUIN)
4962 int attr_x = -1, attr_y = -1;
4973 for (i = 0; i < MAX_PLAYERS; i++)
4975 struct PlayerInfo *player = &stored_player[i];
4976 int jx = player->jx, jy = player->jy;
4978 if (!player->active)
4982 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4991 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4992 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4993 game.engine_version < VERSION_IDENT(3,1,0,0)))
4995 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5002 if (element == EL_PENGUIN)
5005 static int xy[4][2] =
5013 for (i = 0; i < NUM_DIRECTIONS; i++)
5015 int ex = x + xy[i][0];
5016 int ey = y + xy[i][1];
5018 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5027 MovDir[x][y] = MV_NO_MOVING;
5029 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5030 else if (attr_x > x)
5031 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5033 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5034 else if (attr_y > y)
5035 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5037 if (element == EL_ROBOT)
5041 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5042 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5043 Moving2Blocked(x, y, &newx, &newy);
5045 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5046 MovDelay[x][y] = 8 + 8 * !RND(3);
5048 MovDelay[x][y] = 16;
5050 else if (element == EL_PENGUIN)
5056 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5058 boolean first_horiz = RND(2);
5059 int new_move_dir = MovDir[x][y];
5062 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5063 Moving2Blocked(x, y, &newx, &newy);
5065 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5069 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5070 Moving2Blocked(x, y, &newx, &newy);
5072 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5075 MovDir[x][y] = old_move_dir;
5079 else /* (element == EL_SATELLITE) */
5085 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5087 boolean first_horiz = RND(2);
5088 int new_move_dir = MovDir[x][y];
5091 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5092 Moving2Blocked(x, y, &newx, &newy);
5094 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5098 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5099 Moving2Blocked(x, y, &newx, &newy);
5101 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5104 MovDir[x][y] = old_move_dir;
5109 else if (move_pattern == MV_TURNING_LEFT ||
5110 move_pattern == MV_TURNING_RIGHT ||
5111 move_pattern == MV_TURNING_LEFT_RIGHT ||
5112 move_pattern == MV_TURNING_RIGHT_LEFT ||
5113 move_pattern == MV_TURNING_RANDOM ||
5114 move_pattern == MV_ALL_DIRECTIONS)
5116 boolean can_turn_left =
5117 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5118 boolean can_turn_right =
5119 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5121 #if USE_CAN_MOVE_NOT_MOVING
5122 if (element_info[element].move_stepsize == 0) /* not moving */
5126 if (move_pattern == MV_TURNING_LEFT)
5127 MovDir[x][y] = left_dir;
5128 else if (move_pattern == MV_TURNING_RIGHT)
5129 MovDir[x][y] = right_dir;
5130 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5131 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5132 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5133 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5134 else if (move_pattern == MV_TURNING_RANDOM)
5135 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5136 can_turn_right && !can_turn_left ? right_dir :
5137 RND(2) ? left_dir : right_dir);
5138 else if (can_turn_left && can_turn_right)
5139 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5140 else if (can_turn_left)
5141 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5142 else if (can_turn_right)
5143 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5145 MovDir[x][y] = back_dir;
5147 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5149 else if (move_pattern == MV_HORIZONTAL ||
5150 move_pattern == MV_VERTICAL)
5152 if (move_pattern & old_move_dir)
5153 MovDir[x][y] = back_dir;
5154 else if (move_pattern == MV_HORIZONTAL)
5155 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5156 else if (move_pattern == MV_VERTICAL)
5157 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5159 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5161 else if (move_pattern & MV_ANY_DIRECTION)
5163 MovDir[x][y] = move_pattern;
5164 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5166 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5168 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5169 MovDir[x][y] = left_dir;
5170 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5171 MovDir[x][y] = right_dir;
5173 if (MovDir[x][y] != old_move_dir)
5174 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5176 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5178 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5179 MovDir[x][y] = right_dir;
5180 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5181 MovDir[x][y] = left_dir;
5183 if (MovDir[x][y] != old_move_dir)
5184 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5186 else if (move_pattern == MV_TOWARDS_PLAYER ||
5187 move_pattern == MV_AWAY_FROM_PLAYER)
5189 int attr_x = -1, attr_y = -1;
5191 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5202 for (i = 0; i < MAX_PLAYERS; i++)
5204 struct PlayerInfo *player = &stored_player[i];
5205 int jx = player->jx, jy = player->jy;
5207 if (!player->active)
5211 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5219 MovDir[x][y] = MV_NO_MOVING;
5221 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5222 else if (attr_x > x)
5223 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5225 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5226 else if (attr_y > y)
5227 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5229 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5231 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5233 boolean first_horiz = RND(2);
5234 int new_move_dir = MovDir[x][y];
5236 #if USE_CAN_MOVE_NOT_MOVING
5237 if (element_info[element].move_stepsize == 0) /* not moving */
5239 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5240 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5247 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5248 Moving2Blocked(x, y, &newx, &newy);
5250 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5254 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5255 Moving2Blocked(x, y, &newx, &newy);
5257 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5260 MovDir[x][y] = old_move_dir;
5263 else if (move_pattern == MV_WHEN_PUSHED ||
5264 move_pattern == MV_WHEN_DROPPED)
5266 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5267 MovDir[x][y] = MV_NO_MOVING;
5271 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5273 static int test_xy[7][2] =
5283 static int test_dir[7] =
5293 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5294 int move_preference = -1000000; /* start with very low preference */
5295 int new_move_dir = MV_NO_MOVING;
5296 int start_test = RND(4);
5299 for (i = 0; i < NUM_DIRECTIONS; i++)
5301 int move_dir = test_dir[start_test + i];
5302 int move_dir_preference;
5304 xx = x + test_xy[start_test + i][0];
5305 yy = y + test_xy[start_test + i][1];
5307 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5308 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5310 new_move_dir = move_dir;
5315 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5318 move_dir_preference = -1 * RunnerVisit[xx][yy];
5319 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5320 move_dir_preference = PlayerVisit[xx][yy];
5322 if (move_dir_preference > move_preference)
5324 /* prefer field that has not been visited for the longest time */
5325 move_preference = move_dir_preference;
5326 new_move_dir = move_dir;
5328 else if (move_dir_preference == move_preference &&
5329 move_dir == old_move_dir)
5331 /* prefer last direction when all directions are preferred equally */
5332 move_preference = move_dir_preference;
5333 new_move_dir = move_dir;
5337 MovDir[x][y] = new_move_dir;
5338 if (old_move_dir != new_move_dir)
5341 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5349 static void TurnRound(int x, int y)
5351 int direction = MovDir[x][y];
5354 GfxDir[x][y] = MovDir[x][y];
5360 GfxDir[x][y] = MovDir[x][y];
5363 if (direction != MovDir[x][y])
5368 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5371 GfxAction[x][y] = ACTION_WAITING;
5375 static boolean JustBeingPushed(int x, int y)
5379 for (i = 0; i < MAX_PLAYERS; i++)
5381 struct PlayerInfo *player = &stored_player[i];
5383 if (player->active && player->is_pushing && player->MovPos)
5385 int next_jx = player->jx + (player->jx - player->last_jx);
5386 int next_jy = player->jy + (player->jy - player->last_jy);
5388 if (x == next_jx && y == next_jy)
5396 void StartMoving(int x, int y)
5399 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5401 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5402 int element = Feld[x][y];
5408 if (MovDelay[x][y] == 0)
5409 GfxAction[x][y] = ACTION_DEFAULT;
5411 /* !!! this should be handled more generic (not only for mole) !!! */
5412 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5413 GfxAction[x][y] = ACTION_DEFAULT;
5416 if (CAN_FALL(element) && y < lev_fieldy - 1)
5418 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5419 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5420 if (JustBeingPushed(x, y))
5423 if (element == EL_QUICKSAND_FULL)
5425 if (IS_FREE(x, y + 1))
5427 InitMovingField(x, y, MV_DOWN);
5428 started_moving = TRUE;
5430 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5431 Store[x][y] = EL_ROCK;
5433 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5435 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5438 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5440 if (!MovDelay[x][y])
5441 MovDelay[x][y] = TILEY + 1;
5450 Feld[x][y] = EL_QUICKSAND_EMPTY;
5451 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5452 Store[x][y + 1] = Store[x][y];
5455 PlayLevelSoundAction(x, y, ACTION_FILLING);
5457 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5461 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5462 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5464 InitMovingField(x, y, MV_DOWN);
5465 started_moving = TRUE;
5467 Feld[x][y] = EL_QUICKSAND_FILLING;
5468 Store[x][y] = element;
5470 PlayLevelSoundAction(x, y, ACTION_FILLING);
5472 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5475 else if (element == EL_MAGIC_WALL_FULL)
5477 if (IS_FREE(x, y + 1))
5479 InitMovingField(x, y, MV_DOWN);
5480 started_moving = TRUE;
5482 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5483 Store[x][y] = EL_CHANGED(Store[x][y]);
5485 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5487 if (!MovDelay[x][y])
5488 MovDelay[x][y] = TILEY/4 + 1;
5497 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5498 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5499 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5503 else if (element == EL_BD_MAGIC_WALL_FULL)
5505 if (IS_FREE(x, y + 1))
5507 InitMovingField(x, y, MV_DOWN);
5508 started_moving = TRUE;
5510 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5511 Store[x][y] = EL_CHANGED2(Store[x][y]);
5513 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5515 if (!MovDelay[x][y])
5516 MovDelay[x][y] = TILEY/4 + 1;
5525 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5526 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5527 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5531 else if (CAN_PASS_MAGIC_WALL(element) &&
5532 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5533 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5535 InitMovingField(x, y, MV_DOWN);
5536 started_moving = TRUE;
5539 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5540 EL_BD_MAGIC_WALL_FILLING);
5541 Store[x][y] = element;
5544 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5546 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5549 SplashAcid(x, y + 1);
5551 InitMovingField(x, y, MV_DOWN);
5552 started_moving = TRUE;
5554 Store[x][y] = EL_ACID;
5556 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5557 GfxAction[x][y + 1] = ACTION_ACTIVE;
5561 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5562 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5564 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5565 CAN_SMASH(element) && WasJustFalling[x][y] &&
5566 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5568 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5569 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5570 (Feld[x][y + 1] == EL_BLOCKED)))
5574 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5575 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5576 WasJustMoving[x][y] && !Pushed[x][y + 1])
5578 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5579 WasJustMoving[x][y])
5584 /* this is needed for a special case not covered by calling "Impact()"
5585 from "ContinueMoving()": if an element moves to a tile directly below
5586 another element which was just falling on that tile (which was empty
5587 in the previous frame), the falling element above would just stop
5588 instead of smashing the element below (in previous version, the above
5589 element was just checked for "moving" instead of "falling", resulting
5590 in incorrect smashes caused by horizontal movement of the above
5591 element; also, the case of the player being the element to smash was
5592 simply not covered here... :-/ ) */
5595 WasJustMoving[x][y] = 0;
5596 WasJustFalling[x][y] = 0;
5599 CheckCollision[x][y] = 0;
5602 if (IS_PLAYER(x, y + 1))
5603 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5608 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5610 if (MovDir[x][y] == MV_NO_MOVING)
5612 InitMovingField(x, y, MV_DOWN);
5613 started_moving = TRUE;
5616 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5618 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5619 MovDir[x][y] = MV_DOWN;
5621 InitMovingField(x, y, MV_DOWN);
5622 started_moving = TRUE;
5624 else if (element == EL_AMOEBA_DROP)
5626 Feld[x][y] = EL_AMOEBA_GROWING;
5627 Store[x][y] = EL_AMOEBA_WET;
5629 /* Store[x][y + 1] must be zero, because:
5630 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5633 #if OLD_GAME_BEHAVIOUR
5634 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5636 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5637 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5638 element != EL_DX_SUPABOMB)
5641 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5642 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5643 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5644 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5647 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5648 (IS_FREE(x - 1, y + 1) ||
5649 Feld[x - 1][y + 1] == EL_ACID));
5650 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5651 (IS_FREE(x + 1, y + 1) ||
5652 Feld[x + 1][y + 1] == EL_ACID));
5653 boolean can_fall_any = (can_fall_left || can_fall_right);
5654 boolean can_fall_both = (can_fall_left && can_fall_right);
5656 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5658 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5660 if (slippery_type == SLIPPERY_ONLY_LEFT)
5661 can_fall_right = FALSE;
5662 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5663 can_fall_left = FALSE;
5664 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5665 can_fall_right = FALSE;
5666 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5667 can_fall_left = FALSE;
5669 can_fall_any = (can_fall_left || can_fall_right);
5670 can_fall_both = (can_fall_left && can_fall_right);
5673 #if USE_NEW_SP_SLIPPERY
5674 /* !!! better use the same properties as for custom elements here !!! */
5675 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5676 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5678 can_fall_right = FALSE; /* slip down on left side */
5679 can_fall_both = FALSE;
5686 if (game.emulation == EMU_BOULDERDASH ||
5687 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5688 can_fall_right = FALSE; /* slip down on left side */
5690 can_fall_left = !(can_fall_right = RND(2));
5692 can_fall_both = FALSE;
5699 if (can_fall_both &&
5700 (game.emulation != EMU_BOULDERDASH &&
5701 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5702 can_fall_left = !(can_fall_right = RND(2));
5705 /* if not determined otherwise, prefer left side for slipping down */
5706 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5707 started_moving = TRUE;
5711 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5713 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5716 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5717 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5718 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5719 int belt_dir = game.belt_dir[belt_nr];
5721 if ((belt_dir == MV_LEFT && left_is_free) ||
5722 (belt_dir == MV_RIGHT && right_is_free))
5725 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5728 InitMovingField(x, y, belt_dir);
5729 started_moving = TRUE;
5732 Pushed[x][y] = TRUE;
5733 Pushed[nextx][y] = TRUE;
5736 GfxAction[x][y] = ACTION_DEFAULT;
5740 MovDir[x][y] = 0; /* if element was moving, stop it */
5745 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5747 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5749 if (CAN_MOVE(element) && !started_moving)
5752 int move_pattern = element_info[element].move_pattern;
5757 if (MovDir[x][y] == MV_NO_MOVING)
5759 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5760 x, y, element, element_info[element].token_name);
5761 printf("StartMoving(): This should never happen!\n");
5766 Moving2Blocked(x, y, &newx, &newy);
5769 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5772 if ((element == EL_SATELLITE ||
5773 element == EL_BALLOON ||
5774 element == EL_SPRING)
5775 && JustBeingPushed(x, y))
5782 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5783 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5785 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5786 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5787 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5791 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5792 element, element_info[element].token_name,
5793 WasJustMoving[x][y],
5794 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5795 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5796 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5797 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5801 WasJustMoving[x][y] = 0;
5804 CheckCollision[x][y] = 0;
5806 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5809 if (Feld[x][y] != element) /* element has changed */
5811 element = Feld[x][y];
5812 move_pattern = element_info[element].move_pattern;
5814 if (!CAN_MOVE(element))
5818 if (Feld[x][y] != element) /* element has changed */
5826 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5827 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5829 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5831 Moving2Blocked(x, y, &newx, &newy);
5832 if (Feld[newx][newy] == EL_BLOCKED)
5833 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5839 if (FrameCounter < 1 && x == 0 && y == 29)
5840 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5843 if (!MovDelay[x][y]) /* start new movement phase */
5845 /* all objects that can change their move direction after each step
5846 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5848 if (element != EL_YAMYAM &&
5849 element != EL_DARK_YAMYAM &&
5850 element != EL_PACMAN &&
5851 !(move_pattern & MV_ANY_DIRECTION) &&
5852 move_pattern != MV_TURNING_LEFT &&
5853 move_pattern != MV_TURNING_RIGHT &&
5854 move_pattern != MV_TURNING_LEFT_RIGHT &&
5855 move_pattern != MV_TURNING_RIGHT_LEFT &&
5856 move_pattern != MV_TURNING_RANDOM)
5861 if (FrameCounter < 1 && x == 0 && y == 29)
5862 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5865 if (MovDelay[x][y] && (element == EL_BUG ||
5866 element == EL_SPACESHIP ||
5867 element == EL_SP_SNIKSNAK ||
5868 element == EL_SP_ELECTRON ||
5869 element == EL_MOLE))
5870 DrawLevelField(x, y);
5874 if (MovDelay[x][y]) /* wait some time before next movement */
5879 if (element == EL_YAMYAM)
5882 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5883 DrawLevelElementAnimation(x, y, element);
5887 if (MovDelay[x][y]) /* element still has to wait some time */
5890 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5891 ResetGfxAnimation(x, y);
5895 if (GfxAction[x][y] != ACTION_WAITING)
5896 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5898 GfxAction[x][y] = ACTION_WAITING;
5902 if (element == EL_ROBOT ||
5904 element == EL_PACMAN ||
5906 element == EL_YAMYAM ||
5907 element == EL_DARK_YAMYAM)
5910 DrawLevelElementAnimation(x, y, element);
5912 DrawLevelElementAnimationIfNeeded(x, y, element);
5914 PlayLevelSoundAction(x, y, ACTION_WAITING);
5916 else if (element == EL_SP_ELECTRON)
5917 DrawLevelElementAnimationIfNeeded(x, y, element);
5918 else if (element == EL_DRAGON)
5921 int dir = MovDir[x][y];
5922 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5923 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5924 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5925 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5926 dir == MV_UP ? IMG_FLAMES_1_UP :
5927 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5928 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5931 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5934 GfxAction[x][y] = ACTION_ATTACKING;
5936 if (IS_PLAYER(x, y))
5937 DrawPlayerField(x, y);
5939 DrawLevelField(x, y);
5941 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5943 for (i = 1; i <= 3; i++)
5945 int xx = x + i * dx;
5946 int yy = y + i * dy;
5947 int sx = SCREENX(xx);
5948 int sy = SCREENY(yy);
5949 int flame_graphic = graphic + (i - 1);
5951 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5956 int flamed = MovingOrBlocked2Element(xx, yy);
5960 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5962 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5963 RemoveMovingField(xx, yy);
5965 RemoveField(xx, yy);
5967 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5970 RemoveMovingField(xx, yy);
5974 if (ChangeDelay[xx][yy])
5975 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5976 Feld[xx][yy] == EL_BLOCKED));
5980 ChangeDelay[xx][yy] = 0;
5982 Feld[xx][yy] = EL_FLAMES;
5983 if (IN_SCR_FIELD(sx, sy))
5985 DrawLevelFieldCrumbledSand(xx, yy);
5986 DrawGraphic(sx, sy, flame_graphic, frame);
5991 if (Feld[xx][yy] == EL_FLAMES)
5992 Feld[xx][yy] = EL_EMPTY;
5993 DrawLevelField(xx, yy);
5998 if (MovDelay[x][y]) /* element still has to wait some time */
6000 PlayLevelSoundAction(x, y, ACTION_WAITING);
6006 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6007 for all other elements GfxAction will be set by InitMovingField() */
6008 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6009 GfxAction[x][y] = ACTION_MOVING;
6013 /* now make next step */
6015 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6017 if (DONT_COLLIDE_WITH(element) &&
6018 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6019 !PLAYER_ENEMY_PROTECTED(newx, newy))
6022 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6026 /* player killed by element which is deadly when colliding with */
6028 KillHero(PLAYERINFO(newx, newy));
6035 else if (CAN_MOVE_INTO_ACID(element) &&
6036 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6037 (MovDir[x][y] == MV_DOWN ||
6038 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6040 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6041 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6045 else if ((element == EL_PENGUIN ||
6046 element == EL_ROBOT ||
6047 element == EL_SATELLITE ||
6048 element == EL_BALLOON ||
6049 IS_CUSTOM_ELEMENT(element)) &&
6050 IN_LEV_FIELD(newx, newy) &&
6051 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6054 SplashAcid(newx, newy);
6055 Store[x][y] = EL_ACID;
6057 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6059 if (Feld[newx][newy] == EL_EXIT_OPEN)
6063 DrawLevelField(x, y);
6065 Feld[x][y] = EL_EMPTY;
6066 DrawLevelField(x, y);
6069 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6070 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6071 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6073 local_player->friends_still_needed--;
6074 if (!local_player->friends_still_needed &&
6075 !local_player->GameOver && AllPlayersGone)
6076 local_player->LevelSolved = local_player->GameOver = TRUE;
6080 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6082 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6083 DrawLevelField(newx, newy);
6085 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6087 else if (!IS_FREE(newx, newy))
6089 GfxAction[x][y] = ACTION_WAITING;
6091 if (IS_PLAYER(x, y))
6092 DrawPlayerField(x, y);
6094 DrawLevelField(x, y);
6099 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6101 if (IS_FOOD_PIG(Feld[newx][newy]))
6103 if (IS_MOVING(newx, newy))
6104 RemoveMovingField(newx, newy);
6107 Feld[newx][newy] = EL_EMPTY;
6108 DrawLevelField(newx, newy);
6111 PlayLevelSound(x, y, SND_PIG_DIGGING);
6113 else if (!IS_FREE(newx, newy))
6115 if (IS_PLAYER(x, y))
6116 DrawPlayerField(x, y);
6118 DrawLevelField(x, y);
6127 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6130 else if (IS_CUSTOM_ELEMENT(element) &&
6131 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6135 !IS_FREE(newx, newy)
6140 int new_element = Feld[newx][newy];
6143 printf("::: '%s' digs '%s' [%d]\n",
6144 element_info[element].token_name,
6145 element_info[Feld[newx][newy]].token_name,
6146 StorePlayer[newx][newy]);
6149 if (!IS_FREE(newx, newy))
6151 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6152 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6155 /* no element can dig solid indestructible elements */
6156 if (IS_INDESTRUCTIBLE(new_element) &&
6157 !IS_DIGGABLE(new_element) &&
6158 !IS_COLLECTIBLE(new_element))
6161 if (AmoebaNr[newx][newy] &&
6162 (new_element == EL_AMOEBA_FULL ||
6163 new_element == EL_BD_AMOEBA ||
6164 new_element == EL_AMOEBA_GROWING))
6166 AmoebaCnt[AmoebaNr[newx][newy]]--;
6167 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6170 if (IS_MOVING(newx, newy))
6171 RemoveMovingField(newx, newy);
6174 RemoveField(newx, newy);
6175 DrawLevelField(newx, newy);
6178 /* if digged element was about to explode, prevent the explosion */
6179 ExplodeField[newx][newy] = EX_TYPE_NONE;
6181 PlayLevelSoundAction(x, y, action);
6186 Store[newx][newy] = EL_EMPTY;
6187 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6188 Store[newx][newy] = element_info[element].move_leave_element;
6190 Store[newx][newy] = EL_EMPTY;
6191 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6192 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6193 Store[newx][newy] = element_info[element].move_leave_element;
6196 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6197 element_info[element].can_leave_element = TRUE;
6200 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6202 RunnerVisit[x][y] = FrameCounter;
6203 PlayerVisit[x][y] /= 8; /* expire player visit path */
6209 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6211 if (!IS_FREE(newx, newy))
6213 if (IS_PLAYER(x, y))
6214 DrawPlayerField(x, y);
6216 DrawLevelField(x, y);
6222 boolean wanna_flame = !RND(10);
6223 int dx = newx - x, dy = newy - y;
6224 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6225 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6226 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6227 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6228 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6229 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6232 IS_CLASSIC_ENEMY(element1) ||
6233 IS_CLASSIC_ENEMY(element2)) &&
6234 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6235 element1 != EL_FLAMES && element2 != EL_FLAMES)
6238 ResetGfxAnimation(x, y);
6239 GfxAction[x][y] = ACTION_ATTACKING;
6242 if (IS_PLAYER(x, y))
6243 DrawPlayerField(x, y);
6245 DrawLevelField(x, y);
6247 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6249 MovDelay[x][y] = 50;
6253 RemoveField(newx, newy);
6255 Feld[newx][newy] = EL_FLAMES;
6256 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6259 RemoveField(newx1, newy1);
6261 Feld[newx1][newy1] = EL_FLAMES;
6263 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6266 RemoveField(newx2, newy2);
6268 Feld[newx2][newy2] = EL_FLAMES;
6275 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6276 Feld[newx][newy] == EL_DIAMOND)
6278 if (IS_MOVING(newx, newy))
6279 RemoveMovingField(newx, newy);
6282 Feld[newx][newy] = EL_EMPTY;
6283 DrawLevelField(newx, newy);
6286 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6288 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6289 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6291 if (AmoebaNr[newx][newy])
6293 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6294 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6295 Feld[newx][newy] == EL_BD_AMOEBA)
6296 AmoebaCnt[AmoebaNr[newx][newy]]--;
6301 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6303 if (IS_MOVING(newx, newy))
6306 RemoveMovingField(newx, newy);
6310 Feld[newx][newy] = EL_EMPTY;
6311 DrawLevelField(newx, newy);
6314 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6316 else if ((element == EL_PACMAN || element == EL_MOLE)
6317 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6319 if (AmoebaNr[newx][newy])
6321 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6322 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6323 Feld[newx][newy] == EL_BD_AMOEBA)
6324 AmoebaCnt[AmoebaNr[newx][newy]]--;
6327 if (element == EL_MOLE)
6329 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6330 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6332 ResetGfxAnimation(x, y);
6333 GfxAction[x][y] = ACTION_DIGGING;
6334 DrawLevelField(x, y);
6336 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6338 return; /* wait for shrinking amoeba */
6340 else /* element == EL_PACMAN */
6342 Feld[newx][newy] = EL_EMPTY;
6343 DrawLevelField(newx, newy);
6344 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6347 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6348 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6349 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6351 /* wait for shrinking amoeba to completely disappear */
6354 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6356 /* object was running against a wall */
6361 if (move_pattern & MV_ANY_DIRECTION &&
6362 move_pattern == MovDir[x][y])
6364 int blocking_element =
6365 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6368 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6369 element_info[element].token_name,
6370 element_info[blocking_element].token_name,
6374 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6377 element = Feld[x][y]; /* element might have changed */
6382 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6383 DrawLevelElementAnimation(x, y, element);
6385 if (element == EL_BUG ||
6386 element == EL_SPACESHIP ||
6387 element == EL_SP_SNIKSNAK)
6388 DrawLevelField(x, y);
6389 else if (element == EL_MOLE)
6390 DrawLevelField(x, y);
6391 else if (element == EL_BD_BUTTERFLY ||
6392 element == EL_BD_FIREFLY)
6393 DrawLevelElementAnimationIfNeeded(x, y, element);
6394 else if (element == EL_SATELLITE)
6395 DrawLevelElementAnimationIfNeeded(x, y, element);
6396 else if (element == EL_SP_ELECTRON)
6397 DrawLevelElementAnimationIfNeeded(x, y, element);
6400 if (DONT_TOUCH(element))
6401 TestIfBadThingTouchesHero(x, y);
6404 PlayLevelSoundAction(x, y, ACTION_WAITING);
6410 InitMovingField(x, y, MovDir[x][y]);
6412 PlayLevelSoundAction(x, y, ACTION_MOVING);
6416 ContinueMoving(x, y);
6419 void ContinueMoving(int x, int y)
6421 int element = Feld[x][y];
6422 int stored = Store[x][y];
6423 struct ElementInfo *ei = &element_info[element];
6424 int direction = MovDir[x][y];
6425 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6426 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6427 int newx = x + dx, newy = y + dy;
6429 int nextx = newx + dx, nexty = newy + dy;
6432 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6433 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6435 boolean pushed_by_player = Pushed[x][y];
6438 MovPos[x][y] += getElementMoveStepsize(x, y);
6441 if (pushed_by_player && IS_PLAYER(x, y))
6443 /* special case: moving object pushed by player */
6444 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6447 if (pushed_by_player) /* special case: moving object pushed by player */
6448 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6451 if (ABS(MovPos[x][y]) < TILEX)
6453 DrawLevelField(x, y);
6455 return; /* element is still moving */
6458 /* element reached destination field */
6460 Feld[x][y] = EL_EMPTY;
6461 Feld[newx][newy] = element;
6462 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6465 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6467 element = Feld[newx][newy] = EL_ACID;
6470 else if (element == EL_MOLE)
6472 Feld[x][y] = EL_SAND;
6474 DrawLevelFieldCrumbledSandNeighbours(x, y);
6476 else if (element == EL_QUICKSAND_FILLING)
6478 element = Feld[newx][newy] = get_next_element(element);
6479 Store[newx][newy] = Store[x][y];
6481 else if (element == EL_QUICKSAND_EMPTYING)
6483 Feld[x][y] = get_next_element(element);
6484 element = Feld[newx][newy] = Store[x][y];
6486 else if (element == EL_MAGIC_WALL_FILLING)
6488 element = Feld[newx][newy] = get_next_element(element);
6489 if (!game.magic_wall_active)
6490 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6491 Store[newx][newy] = Store[x][y];
6493 else if (element == EL_MAGIC_WALL_EMPTYING)
6495 Feld[x][y] = get_next_element(element);
6496 if (!game.magic_wall_active)
6497 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6498 element = Feld[newx][newy] = Store[x][y];
6500 else if (element == EL_BD_MAGIC_WALL_FILLING)
6502 element = Feld[newx][newy] = get_next_element(element);
6503 if (!game.magic_wall_active)
6504 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6505 Store[newx][newy] = Store[x][y];
6507 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6509 Feld[x][y] = get_next_element(element);
6510 if (!game.magic_wall_active)
6511 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6512 element = Feld[newx][newy] = Store[x][y];
6514 else if (element == EL_AMOEBA_DROPPING)
6516 Feld[x][y] = get_next_element(element);
6517 element = Feld[newx][newy] = Store[x][y];
6519 else if (element == EL_SOKOBAN_OBJECT)
6522 Feld[x][y] = Back[x][y];
6524 if (Back[newx][newy])
6525 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6527 Back[x][y] = Back[newx][newy] = 0;
6530 else if (Store[x][y] == EL_ACID)
6532 element = Feld[newx][newy] = EL_ACID;
6536 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6537 ei->move_leave_element != EL_EMPTY &&
6538 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6539 Store[x][y] != EL_EMPTY))
6541 /* some elements can leave other elements behind after moving */
6543 Feld[x][y] = ei->move_leave_element;
6544 InitField(x, y, FALSE);
6546 if (GFX_CRUMBLED(Feld[x][y]))
6547 DrawLevelFieldCrumbledSandNeighbours(x, y);
6551 Store[x][y] = EL_EMPTY;
6555 MovDelay[newx][newy] = 0;
6557 if (CAN_CHANGE(element))
6559 /* copy element change control values to new field */
6560 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6561 ChangePage[newx][newy] = ChangePage[x][y];
6562 Changed[newx][newy] = Changed[x][y];
6563 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6566 ChangeDelay[x][y] = 0;
6567 ChangePage[x][y] = -1;
6568 Changed[x][y] = CE_BITMASK_DEFAULT;
6569 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6571 /* copy animation control values to new field */
6572 GfxFrame[newx][newy] = GfxFrame[x][y];
6573 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6574 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6575 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6577 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6580 /* do this after checking for left-behind element */
6581 ResetGfxAnimation(x, y); /* reset animation values for old field */
6585 /* some elements can leave other elements behind after moving */
6587 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6588 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6589 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6591 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6592 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6596 int move_leave_element = ei->move_leave_element;
6598 Feld[x][y] = move_leave_element;
6600 #if USE_PREVIOUS_MOVE_DIR
6601 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6602 MovDir[x][y] = direction;
6605 InitField(x, y, FALSE);
6607 if (GFX_CRUMBLED(Feld[x][y]))
6608 DrawLevelFieldCrumbledSandNeighbours(x, y);
6610 if (ELEM_IS_PLAYER(move_leave_element))
6611 RelocatePlayer(x, y, move_leave_element);
6616 /* some elements can leave other elements behind after moving */
6617 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6618 ei->move_leave_element != EL_EMPTY &&
6619 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6620 ei->can_leave_element_last))
6622 Feld[x][y] = ei->move_leave_element;
6623 InitField(x, y, FALSE);
6625 if (GFX_CRUMBLED(Feld[x][y]))
6626 DrawLevelFieldCrumbledSandNeighbours(x, y);
6629 ei->can_leave_element_last = ei->can_leave_element;
6630 ei->can_leave_element = FALSE;
6634 /* do this after checking for left-behind element */
6635 ResetGfxAnimation(x, y); /* reset animation values for old field */
6639 /* 2.1.1 (does not work correctly for spring) */
6640 if (!CAN_MOVE(element))
6641 MovDir[newx][newy] = 0;
6645 /* (does not work for falling objects that slide horizontally) */
6646 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6647 MovDir[newx][newy] = 0;
6650 if (!CAN_MOVE(element) ||
6651 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6652 MovDir[newx][newy] = 0;
6656 if (!CAN_MOVE(element) ||
6657 (CAN_FALL(element) && direction == MV_DOWN))
6658 GfxDir[x][y] = MovDir[newx][newy] = 0;
6660 if (!CAN_MOVE(element) ||
6661 (CAN_FALL(element) && direction == MV_DOWN &&
6662 (element == EL_SPRING ||
6663 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6664 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6665 GfxDir[x][y] = MovDir[newx][newy] = 0;
6671 DrawLevelField(x, y);
6672 DrawLevelField(newx, newy);
6674 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6676 /* prevent pushed element from moving on in pushed direction */
6677 if (pushed_by_player && CAN_MOVE(element) &&
6678 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6679 !(element_info[element].move_pattern & direction))
6680 TurnRound(newx, newy);
6683 /* prevent elements on conveyor belt from moving on in last direction */
6684 if (pushed_by_conveyor && CAN_FALL(element) &&
6685 direction & MV_HORIZONTAL)
6688 if (CAN_MOVE(element))
6689 InitMovDir(newx, newy);
6691 MovDir[newx][newy] = 0;
6693 MovDir[newx][newy] = 0;
6698 if (!pushed_by_player)
6700 int nextx = newx + dx, nexty = newy + dy;
6701 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6703 WasJustMoving[newx][newy] = 3;
6705 if (CAN_FALL(element) && direction == MV_DOWN)
6706 WasJustFalling[newx][newy] = 3;
6708 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6709 CheckCollision[newx][newy] = 2;
6712 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6714 TestIfBadThingTouchesHero(newx, newy);
6715 TestIfBadThingTouchesFriend(newx, newy);
6717 if (!IS_CUSTOM_ELEMENT(element))
6718 TestIfBadThingTouchesOtherBadThing(newx, newy);
6720 else if (element == EL_PENGUIN)
6721 TestIfFriendTouchesBadThing(newx, newy);
6723 #if USE_NEW_MOVE_STYLE
6725 if (CAN_FALL(element) && direction == MV_DOWN &&
6726 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6727 IS_PLAYER(x, newy + 1))
6728 printf("::: we would now kill the player [%d]\n", FrameCounter);
6731 /* give the player one last chance (one more frame) to move away */
6732 if (CAN_FALL(element) && direction == MV_DOWN &&
6733 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6734 (!IS_PLAYER(x, newy + 1) ||
6735 game.engine_version < VERSION_IDENT(3,1,1,0)))
6738 if (CAN_FALL(element) && direction == MV_DOWN &&
6739 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6747 if (pushed_by_player && !game.use_change_when_pushing_bug)
6749 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6752 if (pushed_by_player)
6757 int dig_side = MV_DIR_OPPOSITE(direction);
6759 static int trigger_sides[4] =
6761 CH_SIDE_RIGHT, /* moving left */
6762 CH_SIDE_LEFT, /* moving right */
6763 CH_SIDE_BOTTOM, /* moving up */
6764 CH_SIDE_TOP, /* moving down */
6766 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6768 struct PlayerInfo *player = PLAYERINFO(x, y);
6770 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6771 player->index_bit, dig_side);
6772 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6773 player->index_bit, dig_side);
6778 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6782 if (ChangePage[newx][newy] != -1) /* delayed change */
6783 ChangeElement(newx, newy, ChangePage[newx][newy]);
6788 TestIfElementHitsCustomElement(newx, newy, direction);
6792 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6794 int hitting_element = Feld[newx][newy];
6796 /* !!! fix side (direction) orientation here and elsewhere !!! */
6797 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6801 if (IN_LEV_FIELD(nextx, nexty))
6803 int opposite_direction = MV_DIR_OPPOSITE(direction);
6804 int hitting_side = direction;
6805 int touched_side = opposite_direction;
6806 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6807 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6808 MovDir[nextx][nexty] != direction ||
6809 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6815 CheckElementChangeBySide(nextx, nexty, touched_element,
6816 CE_HIT_BY_SOMETHING, opposite_direction);
6818 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6819 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6821 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6823 struct ElementChangeInfo *change =
6824 &element_info[hitting_element].change_page[i];
6826 if (change->can_change &&
6827 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6828 change->trigger_side & touched_side &&
6829 change->trigger_element == touched_element)
6831 CheckElementChangeByPage(newx, newy, hitting_element,
6832 touched_element, CE_OTHER_IS_HITTING,i);
6838 if (IS_CUSTOM_ELEMENT(touched_element) &&
6839 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6841 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6843 struct ElementChangeInfo *change =
6844 &element_info[touched_element].change_page[i];
6846 if (change->can_change &&
6847 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6848 change->trigger_side & hitting_side &&
6849 change->trigger_element == hitting_element)
6851 CheckElementChangeByPage(nextx, nexty, touched_element,
6852 hitting_element, CE_OTHER_GETS_HIT, i);
6863 TestIfPlayerTouchesCustomElement(newx, newy);
6864 TestIfElementTouchesCustomElement(newx, newy);
6867 int AmoebeNachbarNr(int ax, int ay)
6870 int element = Feld[ax][ay];
6872 static int xy[4][2] =
6880 for (i = 0; i < NUM_DIRECTIONS; i++)
6882 int x = ax + xy[i][0];
6883 int y = ay + xy[i][1];
6885 if (!IN_LEV_FIELD(x, y))
6888 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6889 group_nr = AmoebaNr[x][y];
6895 void AmoebenVereinigen(int ax, int ay)
6897 int i, x, y, xx, yy;
6898 int new_group_nr = AmoebaNr[ax][ay];
6899 static int xy[4][2] =
6907 if (new_group_nr == 0)
6910 for (i = 0; i < NUM_DIRECTIONS; i++)
6915 if (!IN_LEV_FIELD(x, y))
6918 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6919 Feld[x][y] == EL_BD_AMOEBA ||
6920 Feld[x][y] == EL_AMOEBA_DEAD) &&
6921 AmoebaNr[x][y] != new_group_nr)
6923 int old_group_nr = AmoebaNr[x][y];
6925 if (old_group_nr == 0)
6928 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6929 AmoebaCnt[old_group_nr] = 0;
6930 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6931 AmoebaCnt2[old_group_nr] = 0;
6933 for (yy = 0; yy < lev_fieldy; yy++)
6935 for (xx = 0; xx < lev_fieldx; xx++)
6937 if (AmoebaNr[xx][yy] == old_group_nr)
6938 AmoebaNr[xx][yy] = new_group_nr;
6945 void AmoebeUmwandeln(int ax, int ay)
6949 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6951 int group_nr = AmoebaNr[ax][ay];
6956 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6957 printf("AmoebeUmwandeln(): This should never happen!\n");
6962 for (y = 0; y < lev_fieldy; y++)
6964 for (x = 0; x < lev_fieldx; x++)
6966 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6969 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6973 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6974 SND_AMOEBA_TURNING_TO_GEM :
6975 SND_AMOEBA_TURNING_TO_ROCK));
6980 static int xy[4][2] =
6988 for (i = 0; i < NUM_DIRECTIONS; i++)
6993 if (!IN_LEV_FIELD(x, y))
6996 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6998 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6999 SND_AMOEBA_TURNING_TO_GEM :
7000 SND_AMOEBA_TURNING_TO_ROCK));
7007 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7010 int group_nr = AmoebaNr[ax][ay];
7011 boolean done = FALSE;
7016 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7017 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7022 for (y = 0; y < lev_fieldy; y++)
7024 for (x = 0; x < lev_fieldx; x++)
7026 if (AmoebaNr[x][y] == group_nr &&
7027 (Feld[x][y] == EL_AMOEBA_DEAD ||
7028 Feld[x][y] == EL_BD_AMOEBA ||
7029 Feld[x][y] == EL_AMOEBA_GROWING))
7032 Feld[x][y] = new_element;
7033 InitField(x, y, FALSE);
7034 DrawLevelField(x, y);
7041 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7042 SND_BD_AMOEBA_TURNING_TO_ROCK :
7043 SND_BD_AMOEBA_TURNING_TO_GEM));
7046 void AmoebeWaechst(int x, int y)
7048 static unsigned long sound_delay = 0;
7049 static unsigned long sound_delay_value = 0;
7051 if (!MovDelay[x][y]) /* start new growing cycle */
7055 if (DelayReached(&sound_delay, sound_delay_value))
7058 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7060 if (Store[x][y] == EL_BD_AMOEBA)
7061 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7063 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7065 sound_delay_value = 30;
7069 if (MovDelay[x][y]) /* wait some time before growing bigger */
7072 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7074 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7075 6 - MovDelay[x][y]);
7077 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7080 if (!MovDelay[x][y])
7082 Feld[x][y] = Store[x][y];
7084 DrawLevelField(x, y);
7089 void AmoebaDisappearing(int x, int y)
7091 static unsigned long sound_delay = 0;
7092 static unsigned long sound_delay_value = 0;
7094 if (!MovDelay[x][y]) /* start new shrinking cycle */
7098 if (DelayReached(&sound_delay, sound_delay_value))
7099 sound_delay_value = 30;
7102 if (MovDelay[x][y]) /* wait some time before shrinking */
7105 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7107 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7108 6 - MovDelay[x][y]);
7110 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7113 if (!MovDelay[x][y])
7115 Feld[x][y] = EL_EMPTY;
7116 DrawLevelField(x, y);
7118 /* don't let mole enter this field in this cycle;
7119 (give priority to objects falling to this field from above) */
7125 void AmoebeAbleger(int ax, int ay)
7128 int element = Feld[ax][ay];
7129 int graphic = el2img(element);
7130 int newax = ax, neway = ay;
7131 static int xy[4][2] =
7139 if (!level.amoeba_speed)
7141 Feld[ax][ay] = EL_AMOEBA_DEAD;
7142 DrawLevelField(ax, ay);
7146 if (IS_ANIMATED(graphic))
7147 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7149 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7150 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7152 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7155 if (MovDelay[ax][ay])
7159 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7162 int x = ax + xy[start][0];
7163 int y = ay + xy[start][1];
7165 if (!IN_LEV_FIELD(x, y))
7169 if (IS_FREE(x, y) ||
7170 CAN_GROW_INTO(Feld[x][y]) ||
7171 Feld[x][y] == EL_QUICKSAND_EMPTY)
7177 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7178 if (IS_FREE(x, y) ||
7179 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7186 if (newax == ax && neway == ay)
7189 else /* normal or "filled" (BD style) amoeba */
7192 boolean waiting_for_player = FALSE;
7194 for (i = 0; i < NUM_DIRECTIONS; i++)
7196 int j = (start + i) % 4;
7197 int x = ax + xy[j][0];
7198 int y = ay + xy[j][1];
7200 if (!IN_LEV_FIELD(x, y))
7204 if (IS_FREE(x, y) ||
7205 CAN_GROW_INTO(Feld[x][y]) ||
7206 Feld[x][y] == EL_QUICKSAND_EMPTY)
7213 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7214 if (IS_FREE(x, y) ||
7215 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7222 else if (IS_PLAYER(x, y))
7223 waiting_for_player = TRUE;
7226 if (newax == ax && neway == ay) /* amoeba cannot grow */
7229 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7231 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7234 Feld[ax][ay] = EL_AMOEBA_DEAD;
7235 DrawLevelField(ax, ay);
7236 AmoebaCnt[AmoebaNr[ax][ay]]--;
7238 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7240 if (element == EL_AMOEBA_FULL)
7241 AmoebeUmwandeln(ax, ay);
7242 else if (element == EL_BD_AMOEBA)
7243 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7248 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7250 /* amoeba gets larger by growing in some direction */
7252 int new_group_nr = AmoebaNr[ax][ay];
7255 if (new_group_nr == 0)
7257 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7258 printf("AmoebeAbleger(): This should never happen!\n");
7263 AmoebaNr[newax][neway] = new_group_nr;
7264 AmoebaCnt[new_group_nr]++;
7265 AmoebaCnt2[new_group_nr]++;
7267 /* if amoeba touches other amoeba(s) after growing, unify them */
7268 AmoebenVereinigen(newax, neway);
7270 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7272 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7278 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7279 (neway == lev_fieldy - 1 && newax != ax))
7281 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7282 Store[newax][neway] = element;
7284 else if (neway == ay)
7286 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7288 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7290 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7295 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7296 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7297 Store[ax][ay] = EL_AMOEBA_DROP;
7298 ContinueMoving(ax, ay);
7302 DrawLevelField(newax, neway);
7305 void Life(int ax, int ay)
7308 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7310 int element = Feld[ax][ay];
7311 int graphic = el2img(element);
7312 boolean changed = FALSE;
7314 if (IS_ANIMATED(graphic))
7315 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7320 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7321 MovDelay[ax][ay] = life_time;
7323 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7326 if (MovDelay[ax][ay])
7330 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7332 int xx = ax+x1, yy = ay+y1;
7335 if (!IN_LEV_FIELD(xx, yy))
7338 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7340 int x = xx+x2, y = yy+y2;
7342 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7345 if (((Feld[x][y] == element ||
7346 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7348 (IS_FREE(x, y) && Stop[x][y]))
7352 if (xx == ax && yy == ay) /* field in the middle */
7354 if (nachbarn < life[0] || nachbarn > life[1])
7356 Feld[xx][yy] = EL_EMPTY;
7358 DrawLevelField(xx, yy);
7359 Stop[xx][yy] = TRUE;
7364 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7365 { /* free border field */
7366 if (nachbarn >= life[2] && nachbarn <= life[3])
7368 Feld[xx][yy] = element;
7369 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7371 DrawLevelField(xx, yy);
7372 Stop[xx][yy] = TRUE;
7377 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7378 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7379 { /* free border field */
7380 if (nachbarn >= life[2] && nachbarn <= life[3])
7382 Feld[xx][yy] = element;
7383 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7385 DrawLevelField(xx, yy);
7386 Stop[xx][yy] = TRUE;
7394 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7395 SND_GAME_OF_LIFE_GROWING);
7398 static void InitRobotWheel(int x, int y)
7400 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7403 static void RunRobotWheel(int x, int y)
7405 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7408 static void StopRobotWheel(int x, int y)
7410 if (ZX == x && ZY == y)
7414 static void InitTimegateWheel(int x, int y)
7417 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7419 /* another brainless, "type style" bug ... :-( */
7420 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7424 static void RunTimegateWheel(int x, int y)
7426 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7429 void CheckExit(int x, int y)
7431 if (local_player->gems_still_needed > 0 ||
7432 local_player->sokobanfields_still_needed > 0 ||
7433 local_player->lights_still_needed > 0)
7435 int element = Feld[x][y];
7436 int graphic = el2img(element);
7438 if (IS_ANIMATED(graphic))
7439 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7444 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7447 Feld[x][y] = EL_EXIT_OPENING;
7449 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7452 void CheckExitSP(int x, int y)
7454 if (local_player->gems_still_needed > 0)
7456 int element = Feld[x][y];
7457 int graphic = el2img(element);
7459 if (IS_ANIMATED(graphic))
7460 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7465 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7468 Feld[x][y] = EL_SP_EXIT_OPENING;
7470 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7473 static void CloseAllOpenTimegates()
7477 for (y = 0; y < lev_fieldy; y++)
7479 for (x = 0; x < lev_fieldx; x++)
7481 int element = Feld[x][y];
7483 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7485 Feld[x][y] = EL_TIMEGATE_CLOSING;
7487 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7489 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7496 void EdelsteinFunkeln(int x, int y)
7498 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7501 if (Feld[x][y] == EL_BD_DIAMOND)
7504 if (MovDelay[x][y] == 0) /* next animation frame */
7505 MovDelay[x][y] = 11 * !SimpleRND(500);
7507 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7511 if (setup.direct_draw && MovDelay[x][y])
7512 SetDrawtoField(DRAW_BUFFERED);
7514 DrawLevelElementAnimation(x, y, Feld[x][y]);
7516 if (MovDelay[x][y] != 0)
7518 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7519 10 - MovDelay[x][y]);
7521 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7523 if (setup.direct_draw)
7527 dest_x = FX + SCREENX(x) * TILEX;
7528 dest_y = FY + SCREENY(y) * TILEY;
7530 BlitBitmap(drawto_field, window,
7531 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7532 SetDrawtoField(DRAW_DIRECT);
7538 void MauerWaechst(int x, int y)
7542 if (!MovDelay[x][y]) /* next animation frame */
7543 MovDelay[x][y] = 3 * delay;
7545 if (MovDelay[x][y]) /* wait some time before next frame */
7549 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7551 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7552 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7554 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7557 if (!MovDelay[x][y])
7559 if (MovDir[x][y] == MV_LEFT)
7561 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7562 DrawLevelField(x - 1, y);
7564 else if (MovDir[x][y] == MV_RIGHT)
7566 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7567 DrawLevelField(x + 1, y);
7569 else if (MovDir[x][y] == MV_UP)
7571 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7572 DrawLevelField(x, y - 1);
7576 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7577 DrawLevelField(x, y + 1);
7580 Feld[x][y] = Store[x][y];
7582 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7583 DrawLevelField(x, y);
7588 void MauerAbleger(int ax, int ay)
7590 int element = Feld[ax][ay];
7591 int graphic = el2img(element);
7592 boolean oben_frei = FALSE, unten_frei = FALSE;
7593 boolean links_frei = FALSE, rechts_frei = FALSE;
7594 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7595 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7596 boolean new_wall = FALSE;
7598 if (IS_ANIMATED(graphic))
7599 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7601 if (!MovDelay[ax][ay]) /* start building new wall */
7602 MovDelay[ax][ay] = 6;
7604 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7607 if (MovDelay[ax][ay])
7611 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7613 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7615 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7617 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7620 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7621 element == EL_EXPANDABLE_WALL_ANY)
7625 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7626 Store[ax][ay-1] = element;
7627 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7628 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7629 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7630 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7635 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7636 Store[ax][ay+1] = element;
7637 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7638 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7639 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7640 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7645 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7646 element == EL_EXPANDABLE_WALL_ANY ||
7647 element == EL_EXPANDABLE_WALL)
7651 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7652 Store[ax-1][ay] = element;
7653 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7654 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7655 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7656 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7662 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7663 Store[ax+1][ay] = element;
7664 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7665 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7666 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7667 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7672 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7673 DrawLevelField(ax, ay);
7675 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7677 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7678 unten_massiv = TRUE;
7679 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7680 links_massiv = TRUE;
7681 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7682 rechts_massiv = TRUE;
7684 if (((oben_massiv && unten_massiv) ||
7685 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7686 element == EL_EXPANDABLE_WALL) &&
7687 ((links_massiv && rechts_massiv) ||
7688 element == EL_EXPANDABLE_WALL_VERTICAL))
7689 Feld[ax][ay] = EL_WALL;
7693 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7695 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7699 void CheckForDragon(int x, int y)
7702 boolean dragon_found = FALSE;
7703 static int xy[4][2] =
7711 for (i = 0; i < NUM_DIRECTIONS; i++)
7713 for (j = 0; j < 4; j++)
7715 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7717 if (IN_LEV_FIELD(xx, yy) &&
7718 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7720 if (Feld[xx][yy] == EL_DRAGON)
7721 dragon_found = TRUE;
7730 for (i = 0; i < NUM_DIRECTIONS; i++)
7732 for (j = 0; j < 3; j++)
7734 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7736 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7738 Feld[xx][yy] = EL_EMPTY;
7739 DrawLevelField(xx, yy);
7748 static void InitBuggyBase(int x, int y)
7750 int element = Feld[x][y];
7751 int activating_delay = FRAMES_PER_SECOND / 4;
7754 (element == EL_SP_BUGGY_BASE ?
7755 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7756 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7758 element == EL_SP_BUGGY_BASE_ACTIVE ?
7759 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7762 static void WarnBuggyBase(int x, int y)
7765 static int xy[4][2] =
7773 for (i = 0; i < NUM_DIRECTIONS; i++)
7775 int xx = x + xy[i][0], yy = y + xy[i][1];
7777 if (IS_PLAYER(xx, yy))
7779 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7786 static void InitTrap(int x, int y)
7788 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7791 static void ActivateTrap(int x, int y)
7793 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7796 static void ChangeActiveTrap(int x, int y)
7798 int graphic = IMG_TRAP_ACTIVE;
7800 /* if new animation frame was drawn, correct crumbled sand border */
7801 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7802 DrawLevelFieldCrumbledSand(x, y);
7805 static void ChangeElementNowExt(int x, int y, int target_element)
7807 int previous_move_direction = MovDir[x][y];
7809 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7810 IS_WALKABLE(Feld[x][y]));
7812 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7813 IS_WALKABLE(Feld[x][y]) &&
7817 /* check if element under player changes from accessible to unaccessible
7818 (needed for special case of dropping element which then changes) */
7819 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7820 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7823 printf("::: BOOOM! [%d, '%s']\n", target_element,
7824 element_info[target_element].token_name);
7836 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7837 RemoveMovingField(x, y);
7841 Feld[x][y] = target_element;
7844 Feld[x][y] = target_element;
7847 ResetGfxAnimation(x, y);
7848 ResetRandomAnimationValue(x, y);
7850 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7851 MovDir[x][y] = previous_move_direction;
7854 InitField_WithBug1(x, y, FALSE);
7856 InitField(x, y, FALSE);
7857 if (CAN_MOVE(Feld[x][y]))
7861 DrawLevelField(x, y);
7863 if (GFX_CRUMBLED(Feld[x][y]))
7864 DrawLevelFieldCrumbledSandNeighbours(x, y);
7868 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7872 TestIfBadThingTouchesHero(x, y);
7873 TestIfPlayerTouchesCustomElement(x, y);
7874 TestIfElementTouchesCustomElement(x, y);
7877 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7878 if (ELEM_IS_PLAYER(target_element))
7879 RelocatePlayer(x, y, target_element);
7882 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7886 TestIfBadThingTouchesHero(x, y);
7887 TestIfPlayerTouchesCustomElement(x, y);
7888 TestIfElementTouchesCustomElement(x, y);
7892 static boolean ChangeElementNow(int x, int y, int element, int page)
7894 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7896 int old_element = Feld[x][y];
7898 /* always use default change event to prevent running into a loop */
7899 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7900 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7902 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7904 /* reset actual trigger element and player */
7905 change->actual_trigger_element = EL_EMPTY;
7906 change->actual_trigger_player = EL_PLAYER_1;
7909 /* do not change already changed elements with same change event */
7911 if (Changed[x][y] & ChangeEvent[x][y])
7918 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7921 /* !!! indirect change before direct change !!! */
7922 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7925 if (change->explode)
7932 if (change->use_target_content)
7934 boolean complete_replace = TRUE;
7935 boolean can_replace[3][3];
7938 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7941 boolean is_walkable;
7942 boolean is_diggable;
7943 boolean is_collectible;
7944 boolean is_removable;
7945 boolean is_destructible;
7946 int ex = x + xx - 1;
7947 int ey = y + yy - 1;
7948 int content_element = change->target_content[xx][yy];
7951 can_replace[xx][yy] = TRUE;
7953 if (ex == x && ey == y) /* do not check changing element itself */
7956 if (content_element == EL_EMPTY_SPACE)
7958 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7963 if (!IN_LEV_FIELD(ex, ey))
7965 can_replace[xx][yy] = FALSE;
7966 complete_replace = FALSE;
7972 if (Changed[ex][ey]) /* do not change already changed elements */
7974 can_replace[xx][yy] = FALSE;
7975 complete_replace = FALSE;
7983 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7984 e = MovingOrBlocked2Element(ex, ey);
7989 is_empty = (IS_FREE(ex, ey) ||
7990 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7991 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7992 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7996 is_empty = (IS_FREE(ex, ey) ||
7997 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7999 is_empty = (IS_FREE(ex, ey) ||
8000 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8005 is_walkable = (is_empty || IS_WALKABLE(e));
8006 is_diggable = (is_empty || IS_DIGGABLE(e));
8007 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8008 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8009 is_removable = (is_diggable || is_collectible);
8011 can_replace[xx][yy] =
8012 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8013 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8014 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8015 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8016 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8017 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8018 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8020 if (!can_replace[xx][yy])
8021 complete_replace = FALSE;
8023 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8024 IS_WALKABLE(content_element)));
8026 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8028 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8031 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8032 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8033 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8035 can_replace[xx][yy] = FALSE;
8036 complete_replace = FALSE;
8041 if (!change->only_if_complete || complete_replace)
8043 boolean something_has_changed = FALSE;
8045 if (change->only_if_complete && change->use_random_replace &&
8046 RND(100) < change->random_percentage)
8049 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8051 int ex = x + xx - 1;
8052 int ey = y + yy - 1;
8053 int content_element;
8055 if (can_replace[xx][yy] && (!change->use_random_replace ||
8056 RND(100) < change->random_percentage))
8058 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8059 RemoveMovingField(ex, ey);
8061 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8063 content_element = change->target_content[xx][yy];
8064 target_element = GET_TARGET_ELEMENT(content_element, change);
8066 ChangeElementNowExt(ex, ey, target_element);
8068 something_has_changed = TRUE;
8070 /* for symmetry reasons, freeze newly created border elements */
8071 if (ex != x || ey != y)
8072 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8076 if (something_has_changed)
8077 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8082 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8084 ChangeElementNowExt(x, y, target_element);
8086 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8090 /* this uses direct change before indirect change */
8091 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
8097 static void ChangeElement(int x, int y, int page)
8099 int element = MovingOrBlocked2Element(x, y);
8100 struct ElementInfo *ei = &element_info[element];
8101 struct ElementChangeInfo *change = &ei->change_page[page];
8104 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8107 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8108 x, y, element, element_info[element].token_name);
8109 printf("ChangeElement(): This should never happen!\n");
8114 /* this can happen with classic bombs on walkable, changing elements */
8115 if (!CAN_CHANGE(element))
8118 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8119 ChangeDelay[x][y] = 0;
8125 if (ChangeDelay[x][y] == 0) /* initialize element change */
8127 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8128 RND(change->delay_random * change->delay_frames)) + 1;
8130 ResetGfxAnimation(x, y);
8131 ResetRandomAnimationValue(x, y);
8133 if (change->pre_change_function)
8134 change->pre_change_function(x, y);
8137 ChangeDelay[x][y]--;
8139 if (ChangeDelay[x][y] != 0) /* continue element change */
8141 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8143 if (IS_ANIMATED(graphic))
8144 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8146 if (change->change_function)
8147 change->change_function(x, y);
8149 else /* finish element change */
8151 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8153 page = ChangePage[x][y];
8154 ChangePage[x][y] = -1;
8156 change = &ei->change_page[page];
8160 if (IS_MOVING(x, y) && !change->explode)
8162 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8165 ChangeDelay[x][y] = 1; /* try change after next move step */
8166 ChangePage[x][y] = page; /* remember page to use for change */
8171 if (ChangeElementNow(x, y, element, page))
8173 if (change->post_change_function)
8174 change->post_change_function(x, y);
8179 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8180 int trigger_element,
8187 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8189 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8192 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8194 int element = EL_CUSTOM_START + i;
8196 boolean change_element = FALSE;
8199 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8202 for (j = 0; j < element_info[element].num_change_pages; j++)
8204 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8206 if (change->can_change &&
8207 change->events & CH_EVENT_BIT(trigger_event) &&
8208 change->trigger_side & trigger_side &&
8209 change->trigger_player & trigger_player &&
8210 change->trigger_page & trigger_page_bits &&
8211 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8214 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8215 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8216 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8219 change_element = TRUE;
8222 change->actual_trigger_element = trigger_element;
8223 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8229 if (!change_element)
8232 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8235 if (x == lx && y == ly) /* do not change trigger element itself */
8239 if (Feld[x][y] == element)
8241 ChangeDelay[x][y] = 1;
8242 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8243 ChangeElement(x, y, page);
8251 static boolean CheckElementChangeExt(int x, int y,
8253 int trigger_element,
8259 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8262 if (Feld[x][y] == EL_BLOCKED)
8264 Blocked2Moving(x, y, &x, &y);
8265 element = Feld[x][y];
8269 if (Feld[x][y] != element) /* check if element has already changed */
8272 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8273 Feld[x][y], element_info[Feld[x][y]].token_name,
8274 element, element_info[element].token_name,
8283 if (trigger_page < 0)
8285 boolean change_element = FALSE;
8288 for (i = 0; i < element_info[element].num_change_pages; i++)
8290 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8292 if (change->can_change &&
8293 change->events & CH_EVENT_BIT(trigger_event) &&
8294 change->trigger_side & trigger_side &&
8295 change->trigger_player & trigger_player)
8297 change_element = TRUE;
8300 change->actual_trigger_element = trigger_element;
8301 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8307 if (!change_element)
8312 struct ElementInfo *ei = &element_info[element];
8313 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8315 change->actual_trigger_element = trigger_element;
8316 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8321 /* !!! this check misses pages with same event, but different side !!! */
8323 if (trigger_page < 0)
8324 trigger_page = element_info[element].event_page_nr[trigger_event];
8326 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8330 ChangeDelay[x][y] = 1;
8331 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8332 ChangeElement(x, y, trigger_page);
8337 static void PlayPlayerSound(struct PlayerInfo *player)
8339 int jx = player->jx, jy = player->jy;
8340 int element = player->element_nr;
8341 int last_action = player->last_action_waiting;
8342 int action = player->action_waiting;
8344 if (player->is_waiting)
8346 if (action != last_action)
8347 PlayLevelSoundElementAction(jx, jy, element, action);
8349 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8353 if (action != last_action)
8354 StopSound(element_info[element].sound[last_action]);
8356 if (last_action == ACTION_SLEEPING)
8357 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8361 static void PlayAllPlayersSound()
8365 for (i = 0; i < MAX_PLAYERS; i++)
8366 if (stored_player[i].active)
8367 PlayPlayerSound(&stored_player[i]);
8370 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8372 boolean last_waiting = player->is_waiting;
8373 int move_dir = player->MovDir;
8375 player->last_action_waiting = player->action_waiting;
8379 if (!last_waiting) /* not waiting -> waiting */
8381 player->is_waiting = TRUE;
8383 player->frame_counter_bored =
8385 game.player_boring_delay_fixed +
8386 SimpleRND(game.player_boring_delay_random);
8387 player->frame_counter_sleeping =
8389 game.player_sleeping_delay_fixed +
8390 SimpleRND(game.player_sleeping_delay_random);
8392 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8395 if (game.player_sleeping_delay_fixed +
8396 game.player_sleeping_delay_random > 0 &&
8397 player->anim_delay_counter == 0 &&
8398 player->post_delay_counter == 0 &&
8399 FrameCounter >= player->frame_counter_sleeping)
8400 player->is_sleeping = TRUE;
8401 else if (game.player_boring_delay_fixed +
8402 game.player_boring_delay_random > 0 &&
8403 FrameCounter >= player->frame_counter_bored)
8404 player->is_bored = TRUE;
8406 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8407 player->is_bored ? ACTION_BORING :
8410 if (player->is_sleeping)
8412 if (player->num_special_action_sleeping > 0)
8414 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8416 int last_special_action = player->special_action_sleeping;
8417 int num_special_action = player->num_special_action_sleeping;
8418 int special_action =
8419 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8420 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8421 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8422 last_special_action + 1 : ACTION_SLEEPING);
8423 int special_graphic =
8424 el_act_dir2img(player->element_nr, special_action, move_dir);
8426 player->anim_delay_counter =
8427 graphic_info[special_graphic].anim_delay_fixed +
8428 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8429 player->post_delay_counter =
8430 graphic_info[special_graphic].post_delay_fixed +
8431 SimpleRND(graphic_info[special_graphic].post_delay_random);
8433 player->special_action_sleeping = special_action;
8436 if (player->anim_delay_counter > 0)
8438 player->action_waiting = player->special_action_sleeping;
8439 player->anim_delay_counter--;
8441 else if (player->post_delay_counter > 0)
8443 player->post_delay_counter--;
8447 else if (player->is_bored)
8449 if (player->num_special_action_bored > 0)
8451 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8453 int special_action =
8454 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8455 int special_graphic =
8456 el_act_dir2img(player->element_nr, special_action, move_dir);
8458 player->anim_delay_counter =
8459 graphic_info[special_graphic].anim_delay_fixed +
8460 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8461 player->post_delay_counter =
8462 graphic_info[special_graphic].post_delay_fixed +
8463 SimpleRND(graphic_info[special_graphic].post_delay_random);
8465 player->special_action_bored = special_action;
8468 if (player->anim_delay_counter > 0)
8470 player->action_waiting = player->special_action_bored;
8471 player->anim_delay_counter--;
8473 else if (player->post_delay_counter > 0)
8475 player->post_delay_counter--;
8480 else if (last_waiting) /* waiting -> not waiting */
8482 player->is_waiting = FALSE;
8483 player->is_bored = FALSE;
8484 player->is_sleeping = FALSE;
8486 player->frame_counter_bored = -1;
8487 player->frame_counter_sleeping = -1;
8489 player->anim_delay_counter = 0;
8490 player->post_delay_counter = 0;
8492 player->action_waiting = ACTION_DEFAULT;
8494 player->special_action_bored = ACTION_DEFAULT;
8495 player->special_action_sleeping = ACTION_DEFAULT;
8500 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8503 static byte stored_player_action[MAX_PLAYERS];
8504 static int num_stored_actions = 0;
8506 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8507 int left = player_action & JOY_LEFT;
8508 int right = player_action & JOY_RIGHT;
8509 int up = player_action & JOY_UP;
8510 int down = player_action & JOY_DOWN;
8511 int button1 = player_action & JOY_BUTTON_1;
8512 int button2 = player_action & JOY_BUTTON_2;
8513 int dx = (left ? -1 : right ? 1 : 0);
8514 int dy = (up ? -1 : down ? 1 : 0);
8517 stored_player_action[player->index_nr] = 0;
8518 num_stored_actions++;
8522 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8525 if (!player->active || tape.pausing)
8529 printf("::: [%d %d %d %d] [%d %d]\n",
8530 left, right, up, down, button1, button2);
8536 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8541 if (player->MovPos == 0)
8542 CheckGravityMovement(player);
8545 snapped = SnapField(player, dx, dy);
8549 dropped = DropElement(player);
8551 moved = MovePlayer(player, dx, dy);
8554 if (tape.single_step && tape.recording && !tape.pausing)
8556 if (button1 || (dropped && !moved))
8558 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8559 SnapField(player, 0, 0); /* stop snapping */
8563 SetPlayerWaiting(player, FALSE);
8566 return player_action;
8568 stored_player_action[player->index_nr] = player_action;
8574 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8577 /* no actions for this player (no input at player's configured device) */
8579 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8580 SnapField(player, 0, 0);
8581 CheckGravityMovementWhenNotMoving(player);
8583 if (player->MovPos == 0)
8584 SetPlayerWaiting(player, TRUE);
8586 if (player->MovPos == 0) /* needed for tape.playing */
8587 player->is_moving = FALSE;
8589 player->is_dropping = FALSE;
8595 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8597 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8599 TapeRecordAction(stored_player_action);
8600 num_stored_actions = 0;
8607 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8609 static byte stored_player_action[MAX_PLAYERS];
8610 static int num_stored_actions = 0;
8611 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8612 int left = player_action & JOY_LEFT;
8613 int right = player_action & JOY_RIGHT;
8614 int up = player_action & JOY_UP;
8615 int down = player_action & JOY_DOWN;
8616 int button1 = player_action & JOY_BUTTON_1;
8617 int button2 = player_action & JOY_BUTTON_2;
8618 int dx = (left ? -1 : right ? 1 : 0);
8619 int dy = (up ? -1 : down ? 1 : 0);
8621 stored_player_action[player->index_nr] = 0;
8622 num_stored_actions++;
8624 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8626 if (!player->active || tape.pausing)
8631 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8634 snapped = SnapField(player, dx, dy);
8638 dropped = DropElement(player);
8640 moved = MovePlayer(player, dx, dy);
8643 if (tape.single_step && tape.recording && !tape.pausing)
8645 if (button1 || (dropped && !moved))
8647 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8648 SnapField(player, 0, 0); /* stop snapping */
8652 stored_player_action[player->index_nr] = player_action;
8656 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8658 /* no actions for this player (no input at player's configured device) */
8660 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8661 SnapField(player, 0, 0);
8662 CheckGravityMovementWhenNotMoving(player);
8664 if (player->MovPos == 0)
8665 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8667 if (player->MovPos == 0) /* needed for tape.playing */
8668 player->is_moving = FALSE;
8671 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8673 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8675 TapeRecordAction(stored_player_action);
8676 num_stored_actions = 0;
8681 void AdvanceFrameAndPlayerCounters(int player_nr)
8685 /* advance frame counters (global frame counter and time frame counter) */
8689 /* advance player counters (counters for move delay, move animation etc.) */
8690 for (i = 0; i < MAX_PLAYERS; i++)
8692 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8694 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8696 if (!advance_player_counters) /* not all players may be affected */
8699 stored_player[i].Frame += move_frames;
8701 if (stored_player[i].MovPos != 0)
8702 stored_player[i].StepFrame += move_frames;
8704 #if USE_NEW_MOVE_DELAY
8705 if (stored_player[i].move_delay > 0)
8706 stored_player[i].move_delay--;
8709 #if USE_NEW_PUSH_DELAY
8710 /* due to bugs in previous versions, counter must count up, not down */
8711 if (stored_player[i].push_delay != -1)
8712 stored_player[i].push_delay++;
8715 if (stored_player[i].drop_delay > 0)
8716 stored_player[i].drop_delay--;
8722 static unsigned long game_frame_delay = 0;
8723 unsigned long game_frame_delay_value;
8724 int magic_wall_x = 0, magic_wall_y = 0;
8725 int i, x, y, element, graphic;
8726 byte *recorded_player_action;
8727 byte summarized_player_action = 0;
8729 byte tape_action[MAX_PLAYERS];
8732 if (game_status != GAME_MODE_PLAYING)
8735 game_frame_delay_value =
8736 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8738 if (tape.playing && tape.warp_forward && !tape.pausing)
8739 game_frame_delay_value = 0;
8741 /* ---------- main game synchronization point ---------- */
8743 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8745 if (network_playing && !network_player_action_received)
8749 printf("DEBUG: try to get network player actions in time\n");
8753 #if defined(NETWORK_AVALIABLE)
8754 /* last chance to get network player actions without main loop delay */
8758 if (game_status != GAME_MODE_PLAYING)
8761 if (!network_player_action_received)
8765 printf("DEBUG: failed to get network player actions in time\n");
8776 printf("::: getting new tape action [%d]\n", FrameCounter);
8779 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8782 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8783 if (recorded_player_action == NULL && tape.pausing)
8788 printf("::: %d\n", stored_player[0].action);
8792 if (recorded_player_action != NULL)
8793 for (i = 0; i < MAX_PLAYERS; i++)
8794 stored_player[i].action = recorded_player_action[i];
8797 for (i = 0; i < MAX_PLAYERS; i++)
8799 summarized_player_action |= stored_player[i].action;
8801 if (!network_playing)
8802 stored_player[i].effective_action = stored_player[i].action;
8805 #if defined(NETWORK_AVALIABLE)
8806 if (network_playing)
8807 SendToServer_MovePlayer(summarized_player_action);
8810 if (!options.network && !setup.team_mode)
8811 local_player->effective_action = summarized_player_action;
8814 if (recorded_player_action != NULL)
8815 for (i = 0; i < MAX_PLAYERS; i++)
8816 stored_player[i].effective_action = recorded_player_action[i];
8820 for (i = 0; i < MAX_PLAYERS; i++)
8822 tape_action[i] = stored_player[i].effective_action;
8824 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8825 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8828 /* only save actions from input devices, but not programmed actions */
8830 TapeRecordAction(tape_action);
8833 for (i = 0; i < MAX_PLAYERS; i++)
8835 int actual_player_action = stored_player[i].effective_action;
8838 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8839 - rnd_equinox_tetrachloride 048
8840 - rnd_equinox_tetrachloride_ii 096
8841 - rnd_emanuel_schmieg 002
8842 - doctor_sloan_ww 001, 020
8844 if (stored_player[i].MovPos == 0)
8845 CheckGravityMovement(&stored_player[i]);
8849 /* overwrite programmed action with tape action */
8850 if (stored_player[i].programmed_action)
8851 actual_player_action = stored_player[i].programmed_action;
8855 if (stored_player[i].programmed_action)
8856 printf("::: %d\n", stored_player[i].programmed_action);
8859 if (recorded_player_action)
8862 if (stored_player[i].programmed_action &&
8863 stored_player[i].programmed_action != recorded_player_action[i])
8864 printf("::: %d: %d <-> %d\n", i,
8865 stored_player[i].programmed_action, recorded_player_action[i]);
8869 actual_player_action = recorded_player_action[i];
8874 /* overwrite tape action with programmed action */
8875 if (stored_player[i].programmed_action)
8876 actual_player_action = stored_player[i].programmed_action;
8881 printf("::: action: %d: %x [%d]\n",
8882 stored_player[i].MovPos, actual_player_action, FrameCounter);
8886 PlayerActions(&stored_player[i], actual_player_action);
8888 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8890 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8891 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8894 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8899 TapeRecordAction(tape_action);
8902 network_player_action_received = FALSE;
8904 ScrollScreen(NULL, SCROLL_GO_ON);
8910 for (i = 0; i < MAX_PLAYERS; i++)
8911 stored_player[i].Frame++;
8915 /* for backwards compatibility, the following code emulates a fixed bug that
8916 occured when pushing elements (causing elements that just made their last
8917 pushing step to already (if possible) make their first falling step in the
8918 same game frame, which is bad); this code is also needed to use the famous
8919 "spring push bug" which is used in older levels and might be wanted to be
8920 used also in newer levels, but in this case the buggy pushing code is only
8921 affecting the "spring" element and no other elements */
8924 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8926 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8929 for (i = 0; i < MAX_PLAYERS; i++)
8931 struct PlayerInfo *player = &stored_player[i];
8936 if (player->active && player->is_pushing && player->is_moving &&
8938 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8939 Feld[x][y] == EL_SPRING))
8941 if (player->active && player->is_pushing && player->is_moving &&
8945 ContinueMoving(x, y);
8947 /* continue moving after pushing (this is actually a bug) */
8948 if (!IS_MOVING(x, y))
8957 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8959 Changed[x][y] = CE_BITMASK_DEFAULT;
8960 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8962 #if USE_NEW_BLOCK_STYLE
8963 /* this must be handled before main playfield loop */
8964 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8967 if (MovDelay[x][y] <= 0)
8973 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8975 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8976 printf("GameActions(): This should never happen!\n");
8978 ChangePage[x][y] = -1;
8983 if (WasJustMoving[x][y] > 0)
8984 WasJustMoving[x][y]--;
8985 if (WasJustFalling[x][y] > 0)
8986 WasJustFalling[x][y]--;
8987 if (CheckCollision[x][y] > 0)
8988 CheckCollision[x][y]--;
8993 /* reset finished pushing action (not done in ContinueMoving() to allow
8994 continous pushing animation for elements with zero push delay) */
8995 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8997 ResetGfxAnimation(x, y);
8998 DrawLevelField(x, y);
9003 if (IS_BLOCKED(x, y))
9007 Blocked2Moving(x, y, &oldx, &oldy);
9008 if (!IS_MOVING(oldx, oldy))
9010 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9011 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9012 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9013 printf("GameActions(): This should never happen!\n");
9019 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9021 element = Feld[x][y];
9023 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9025 graphic = el2img(element);
9031 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9033 element = graphic = 0;
9037 if (graphic_info[graphic].anim_global_sync)
9038 GfxFrame[x][y] = FrameCounter;
9040 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9041 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9042 ResetRandomAnimationValue(x, y);
9044 SetRandomAnimationValue(x, y);
9047 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9050 if (IS_INACTIVE(element))
9052 if (IS_ANIMATED(graphic))
9053 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9059 /* this may take place after moving, so 'element' may have changed */
9061 if (IS_CHANGING(x, y))
9063 if (IS_CHANGING(x, y) &&
9064 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9068 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9069 element_info[element].event_page_nr[CE_DELAY]);
9071 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9074 element = Feld[x][y];
9075 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9079 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9084 element = Feld[x][y];
9085 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9087 if (element == EL_MOLE)
9088 printf("::: %d, %d, %d [%d]\n",
9089 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9093 if (element == EL_YAMYAM)
9094 printf("::: %d, %d, %d\n",
9095 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9099 if (IS_ANIMATED(graphic) &&
9103 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9106 if (element == EL_BUG)
9107 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9111 if (element == EL_MOLE)
9112 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9116 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9117 EdelsteinFunkeln(x, y);
9119 else if ((element == EL_ACID ||
9120 element == EL_EXIT_OPEN ||
9121 element == EL_SP_EXIT_OPEN ||
9122 element == EL_SP_TERMINAL ||
9123 element == EL_SP_TERMINAL_ACTIVE ||
9124 element == EL_EXTRA_TIME ||
9125 element == EL_SHIELD_NORMAL ||
9126 element == EL_SHIELD_DEADLY) &&
9127 IS_ANIMATED(graphic))
9128 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9129 else if (IS_MOVING(x, y))
9130 ContinueMoving(x, y);
9131 else if (IS_ACTIVE_BOMB(element))
9132 CheckDynamite(x, y);
9134 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9135 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9137 else if (element == EL_AMOEBA_GROWING)
9138 AmoebeWaechst(x, y);
9139 else if (element == EL_AMOEBA_SHRINKING)
9140 AmoebaDisappearing(x, y);
9142 #if !USE_NEW_AMOEBA_CODE
9143 else if (IS_AMOEBALIVE(element))
9144 AmoebeAbleger(x, y);
9147 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9149 else if (element == EL_EXIT_CLOSED)
9151 else if (element == EL_SP_EXIT_CLOSED)
9153 else if (element == EL_EXPANDABLE_WALL_GROWING)
9155 else if (element == EL_EXPANDABLE_WALL ||
9156 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9157 element == EL_EXPANDABLE_WALL_VERTICAL ||
9158 element == EL_EXPANDABLE_WALL_ANY)
9160 else if (element == EL_FLAMES)
9161 CheckForDragon(x, y);
9163 else if (IS_AUTO_CHANGING(element))
9164 ChangeElement(x, y);
9166 else if (element == EL_EXPLOSION)
9167 ; /* drawing of correct explosion animation is handled separately */
9168 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9169 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9172 /* this may take place after moving, so 'element' may have changed */
9173 if (IS_AUTO_CHANGING(Feld[x][y]))
9174 ChangeElement(x, y);
9177 if (IS_BELT_ACTIVE(element))
9178 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9180 if (game.magic_wall_active)
9182 int jx = local_player->jx, jy = local_player->jy;
9184 /* play the element sound at the position nearest to the player */
9185 if ((element == EL_MAGIC_WALL_FULL ||
9186 element == EL_MAGIC_WALL_ACTIVE ||
9187 element == EL_MAGIC_WALL_EMPTYING ||
9188 element == EL_BD_MAGIC_WALL_FULL ||
9189 element == EL_BD_MAGIC_WALL_ACTIVE ||
9190 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9191 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9199 #if USE_NEW_AMOEBA_CODE
9200 /* new experimental amoeba growth stuff */
9202 if (!(FrameCounter % 8))
9205 static unsigned long random = 1684108901;
9207 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9210 x = (random >> 10) % lev_fieldx;
9211 y = (random >> 20) % lev_fieldy;
9213 x = RND(lev_fieldx);
9214 y = RND(lev_fieldy);
9216 element = Feld[x][y];
9219 if (!IS_PLAYER(x,y) &&
9220 (element == EL_EMPTY ||
9221 CAN_GROW_INTO(element) ||
9222 element == EL_QUICKSAND_EMPTY ||
9223 element == EL_ACID_SPLASH_LEFT ||
9224 element == EL_ACID_SPLASH_RIGHT))
9226 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9227 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9228 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9229 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9230 Feld[x][y] = EL_AMOEBA_DROP;
9233 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9234 if (!IS_PLAYER(x,y) &&
9235 (element == EL_EMPTY ||
9236 element == EL_SAND ||
9237 element == EL_QUICKSAND_EMPTY ||
9238 element == EL_ACID_SPLASH_LEFT ||
9239 element == EL_ACID_SPLASH_RIGHT))
9241 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9242 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9243 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9244 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9245 Feld[x][y] = EL_AMOEBA_DROP;
9249 random = random * 129 + 1;
9255 if (game.explosions_delayed)
9258 game.explosions_delayed = FALSE;
9260 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9262 element = Feld[x][y];
9264 if (ExplodeField[x][y])
9265 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9266 else if (element == EL_EXPLOSION)
9267 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9269 ExplodeField[x][y] = EX_TYPE_NONE;
9272 game.explosions_delayed = TRUE;
9275 if (game.magic_wall_active)
9277 if (!(game.magic_wall_time_left % 4))
9279 int element = Feld[magic_wall_x][magic_wall_y];
9281 if (element == EL_BD_MAGIC_WALL_FULL ||
9282 element == EL_BD_MAGIC_WALL_ACTIVE ||
9283 element == EL_BD_MAGIC_WALL_EMPTYING)
9284 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9286 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9289 if (game.magic_wall_time_left > 0)
9291 game.magic_wall_time_left--;
9292 if (!game.magic_wall_time_left)
9294 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9296 element = Feld[x][y];
9298 if (element == EL_MAGIC_WALL_ACTIVE ||
9299 element == EL_MAGIC_WALL_FULL)
9301 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9302 DrawLevelField(x, y);
9304 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9305 element == EL_BD_MAGIC_WALL_FULL)
9307 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9308 DrawLevelField(x, y);
9312 game.magic_wall_active = FALSE;
9317 if (game.light_time_left > 0)
9319 game.light_time_left--;
9321 if (game.light_time_left == 0)
9322 RedrawAllLightSwitchesAndInvisibleElements();
9325 if (game.timegate_time_left > 0)
9327 game.timegate_time_left--;
9329 if (game.timegate_time_left == 0)
9330 CloseAllOpenTimegates();
9333 for (i = 0; i < MAX_PLAYERS; i++)
9335 struct PlayerInfo *player = &stored_player[i];
9337 if (SHIELD_ON(player))
9339 if (player->shield_deadly_time_left)
9340 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9341 else if (player->shield_normal_time_left)
9342 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9346 if (TimeFrames >= FRAMES_PER_SECOND)
9351 for (i = 0; i < MAX_PLAYERS; i++)
9353 struct PlayerInfo *player = &stored_player[i];
9355 if (SHIELD_ON(player))
9357 player->shield_normal_time_left--;
9359 if (player->shield_deadly_time_left > 0)
9360 player->shield_deadly_time_left--;
9364 if (!level.use_step_counter)
9372 if (TimeLeft <= 10 && setup.time_limit)
9373 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9375 DrawGameValue_Time(TimeLeft);
9377 if (!TimeLeft && setup.time_limit)
9378 for (i = 0; i < MAX_PLAYERS; i++)
9379 KillHero(&stored_player[i]);
9381 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9382 DrawGameValue_Time(TimePlayed);
9385 if (tape.recording || tape.playing)
9386 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9390 PlayAllPlayersSound();
9392 if (options.debug) /* calculate frames per second */
9394 static unsigned long fps_counter = 0;
9395 static int fps_frames = 0;
9396 unsigned long fps_delay_ms = Counter() - fps_counter;
9400 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9402 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9405 fps_counter = Counter();
9408 redraw_mask |= REDRAW_FPS;
9412 if (stored_player[0].jx != stored_player[0].last_jx ||
9413 stored_player[0].jy != stored_player[0].last_jy)
9414 printf("::: %d, %d, %d, %d, %d\n",
9415 stored_player[0].MovDir,
9416 stored_player[0].MovPos,
9417 stored_player[0].GfxPos,
9418 stored_player[0].Frame,
9419 stored_player[0].StepFrame);
9422 #if USE_NEW_MOVE_DELAY
9423 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9428 for (i = 0; i < MAX_PLAYERS; i++)
9431 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9433 stored_player[i].Frame += move_frames;
9435 if (stored_player[i].MovPos != 0)
9436 stored_player[i].StepFrame += move_frames;
9438 #if USE_NEW_MOVE_DELAY
9439 if (stored_player[i].move_delay > 0)
9440 stored_player[i].move_delay--;
9443 if (stored_player[i].drop_delay > 0)
9444 stored_player[i].drop_delay--;
9449 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9451 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9453 local_player->show_envelope = 0;
9457 #if USE_NEW_RANDOMIZE
9458 /* use random number generator in every frame to make it less predictable */
9459 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9464 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9466 int min_x = x, min_y = y, max_x = x, max_y = y;
9469 for (i = 0; i < MAX_PLAYERS; i++)
9471 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9473 if (!stored_player[i].active || &stored_player[i] == player)
9476 min_x = MIN(min_x, jx);
9477 min_y = MIN(min_y, jy);
9478 max_x = MAX(max_x, jx);
9479 max_y = MAX(max_y, jy);
9482 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9485 static boolean AllPlayersInVisibleScreen()
9489 for (i = 0; i < MAX_PLAYERS; i++)
9491 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9493 if (!stored_player[i].active)
9496 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9503 void ScrollLevel(int dx, int dy)
9505 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9508 BlitBitmap(drawto_field, drawto_field,
9509 FX + TILEX * (dx == -1) - softscroll_offset,
9510 FY + TILEY * (dy == -1) - softscroll_offset,
9511 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9512 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9513 FX + TILEX * (dx == 1) - softscroll_offset,
9514 FY + TILEY * (dy == 1) - softscroll_offset);
9518 x = (dx == 1 ? BX1 : BX2);
9519 for (y = BY1; y <= BY2; y++)
9520 DrawScreenField(x, y);
9525 y = (dy == 1 ? BY1 : BY2);
9526 for (x = BX1; x <= BX2; x++)
9527 DrawScreenField(x, y);
9530 redraw_mask |= REDRAW_FIELD;
9534 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9536 int nextx = x + dx, nexty = y + dy;
9537 int element = Feld[x][y];
9540 element != EL_SP_PORT_LEFT &&
9541 element != EL_SP_GRAVITY_PORT_LEFT &&
9542 element != EL_SP_PORT_HORIZONTAL &&
9543 element != EL_SP_PORT_ANY) ||
9545 element != EL_SP_PORT_RIGHT &&
9546 element != EL_SP_GRAVITY_PORT_RIGHT &&
9547 element != EL_SP_PORT_HORIZONTAL &&
9548 element != EL_SP_PORT_ANY) ||
9550 element != EL_SP_PORT_UP &&
9551 element != EL_SP_GRAVITY_PORT_UP &&
9552 element != EL_SP_PORT_VERTICAL &&
9553 element != EL_SP_PORT_ANY) ||
9555 element != EL_SP_PORT_DOWN &&
9556 element != EL_SP_GRAVITY_PORT_DOWN &&
9557 element != EL_SP_PORT_VERTICAL &&
9558 element != EL_SP_PORT_ANY) ||
9559 !IN_LEV_FIELD(nextx, nexty) ||
9560 !IS_FREE(nextx, nexty))
9567 static boolean canFallDown(struct PlayerInfo *player)
9569 int jx = player->jx, jy = player->jy;
9571 return (IN_LEV_FIELD(jx, jy + 1) &&
9572 (IS_FREE(jx, jy + 1) ||
9573 #if USE_NEW_BLOCK_STYLE
9574 #if USE_GRAVITY_BUGFIX_OLD
9575 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9578 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9579 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9580 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9583 static boolean canPassField(int x, int y, int move_dir)
9585 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9586 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9587 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9590 int element = Feld[x][y];
9592 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9593 !CAN_MOVE(element) &&
9594 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9595 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9596 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9599 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9601 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9602 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9603 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9607 int nextx = newx + dx;
9608 int nexty = newy + dy;
9612 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9613 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9615 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9617 (IS_DIGGABLE(Feld[newx][newy]) ||
9618 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9619 canPassField(newx, newy, move_dir)));
9622 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9623 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9624 (IS_DIGGABLE(Feld[newx][newy]) ||
9625 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9626 canPassField(newx, newy, move_dir)));
9629 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9630 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9631 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9632 canPassField(newx, newy, move_dir)));
9634 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9635 (IS_DIGGABLE(Feld[newx][newy]) ||
9636 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9637 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9638 !CAN_MOVE(Feld[newx][newy]) &&
9639 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9640 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9641 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9647 static void CheckGravityMovement(struct PlayerInfo *player)
9649 if (game.gravity && !player->programmed_action)
9652 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9653 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9655 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9656 int move_dir_vertical = player->action & MV_VERTICAL;
9660 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9662 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9665 int jx = player->jx, jy = player->jy;
9667 boolean player_is_moving_to_valid_field =
9668 (!player_is_snapping &&
9669 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9670 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9674 (player->last_move_dir & MV_HORIZONTAL ?
9675 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9676 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9680 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9681 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9682 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9683 int new_jx = jx + dx, new_jy = jy + dy;
9684 int nextx = new_jx + dx, nexty = new_jy + dy;
9690 boolean player_can_fall_down = canFallDown(player);
9692 boolean player_can_fall_down =
9693 (IN_LEV_FIELD(jx, jy + 1) &&
9694 (IS_FREE(jx, jy + 1) ||
9695 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9699 boolean player_can_fall_down =
9700 (IN_LEV_FIELD(jx, jy + 1) &&
9701 (IS_FREE(jx, jy + 1)));
9705 boolean player_is_moving_to_valid_field =
9708 !player_is_snapping &&
9712 IN_LEV_FIELD(new_jx, new_jy) &&
9713 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9714 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9715 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9716 IN_LEV_FIELD(nextx, nexty) &&
9717 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9719 IN_LEV_FIELD(new_jx, new_jy) &&
9720 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9721 Feld[new_jx][new_jy] == EL_SAND ||
9722 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9723 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9724 /* !!! extend EL_SAND to anything diggable !!! */
9730 boolean player_is_standing_on_valid_field =
9731 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9732 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9736 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9737 player_can_fall_down,
9738 player_is_standing_on_valid_field,
9739 player_is_moving_to_valid_field,
9740 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9741 player->effective_action,
9742 player->can_fall_into_acid);
9745 if (player_can_fall_down &&
9747 !player_is_standing_on_valid_field &&
9749 !player_is_moving_to_valid_field)
9752 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9753 jx, jy, FrameCounter);
9756 player->programmed_action = MV_DOWN;
9761 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9764 return CheckGravityMovement(player);
9767 if (game.gravity && !player->programmed_action)
9769 int jx = player->jx, jy = player->jy;
9770 boolean field_under_player_is_free =
9771 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9772 boolean player_is_standing_on_valid_field =
9773 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9774 (IS_WALKABLE(Feld[jx][jy]) &&
9775 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9777 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9778 player->programmed_action = MV_DOWN;
9784 -----------------------------------------------------------------------------
9785 dx, dy: direction (non-diagonal) to try to move the player to
9786 real_dx, real_dy: direction as read from input device (can be diagonal)
9789 boolean MovePlayerOneStep(struct PlayerInfo *player,
9790 int dx, int dy, int real_dx, int real_dy)
9793 static int trigger_sides[4][2] =
9795 /* enter side leave side */
9796 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9797 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9798 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9799 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9801 int move_direction = (dx == -1 ? MV_LEFT :
9802 dx == +1 ? MV_RIGHT :
9804 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9805 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9806 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9808 int jx = player->jx, jy = player->jy;
9809 int new_jx = jx + dx, new_jy = jy + dy;
9813 if (!player->active || (!dx && !dy))
9814 return MF_NO_ACTION;
9816 player->MovDir = (dx < 0 ? MV_LEFT :
9819 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9821 if (!IN_LEV_FIELD(new_jx, new_jy))
9822 return MF_NO_ACTION;
9824 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9825 return MF_NO_ACTION;
9828 element = MovingOrBlocked2Element(new_jx, new_jy);
9830 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9833 if (DONT_RUN_INTO(element))
9835 if (element == EL_ACID && dx == 0 && dy == 1)
9837 SplashAcid(new_jx, new_jy);
9838 Feld[jx][jy] = EL_PLAYER_1;
9839 InitMovingField(jx, jy, MV_DOWN);
9840 Store[jx][jy] = EL_ACID;
9841 ContinueMoving(jx, jy);
9845 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9850 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9851 if (can_move != MF_MOVING)
9854 /* check if DigField() has caused relocation of the player */
9855 if (player->jx != jx || player->jy != jy)
9856 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9858 StorePlayer[jx][jy] = 0;
9859 player->last_jx = jx;
9860 player->last_jy = jy;
9861 player->jx = new_jx;
9862 player->jy = new_jy;
9863 StorePlayer[new_jx][new_jy] = player->element_nr;
9866 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9868 player->step_counter++;
9871 player->drop_delay = 0;
9874 PlayerVisit[jx][jy] = FrameCounter;
9876 ScrollPlayer(player, SCROLL_INIT);
9879 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9881 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9883 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9886 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9888 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9889 CE_OTHER_GETS_ENTERED, enter_side);
9890 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9891 CE_ENTERED_BY_PLAYER, enter_side);
9898 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9900 int jx = player->jx, jy = player->jy;
9901 int old_jx = jx, old_jy = jy;
9902 int moved = MF_NO_ACTION;
9905 if (!player->active)
9910 if (player->MovPos == 0)
9912 player->is_moving = FALSE;
9913 player->is_digging = FALSE;
9914 player->is_collecting = FALSE;
9915 player->is_snapping = FALSE;
9916 player->is_pushing = FALSE;
9922 if (!player->active || (!dx && !dy))
9927 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9935 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9936 player->move_delay + player->move_delay_value);
9939 #if USE_NEW_MOVE_DELAY
9940 if (player->move_delay > 0)
9942 if (!FrameReached(&player->move_delay, player->move_delay_value))
9946 printf("::: can NOT move\n");
9952 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9953 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9960 printf("::: COULD move now\n");
9963 #if USE_NEW_MOVE_DELAY
9964 player->move_delay = -1; /* set to "uninitialized" value */
9967 /* store if player is automatically moved to next field */
9968 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9970 /* remove the last programmed player action */
9971 player->programmed_action = 0;
9975 /* should only happen if pre-1.2 tape recordings are played */
9976 /* this is only for backward compatibility */
9978 int original_move_delay_value = player->move_delay_value;
9981 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9985 /* scroll remaining steps with finest movement resolution */
9986 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9988 while (player->MovPos)
9990 ScrollPlayer(player, SCROLL_GO_ON);
9991 ScrollScreen(NULL, SCROLL_GO_ON);
9993 #if USE_NEW_MOVE_DELAY
9994 AdvanceFrameAndPlayerCounters(player->index_nr);
10003 player->move_delay_value = original_move_delay_value;
10006 if (player->last_move_dir & MV_HORIZONTAL)
10008 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10009 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10013 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10014 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10020 if (moved & MF_MOVING && !ScreenMovPos &&
10021 (player == local_player || !options.network))
10023 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10024 int offset = (setup.scroll_delay ? 3 : 0);
10026 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10028 /* actual player has left the screen -- scroll in that direction */
10029 if (jx != old_jx) /* player has moved horizontally */
10030 scroll_x += (jx - old_jx);
10031 else /* player has moved vertically */
10032 scroll_y += (jy - old_jy);
10036 if (jx != old_jx) /* player has moved horizontally */
10038 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10039 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10040 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10042 /* don't scroll over playfield boundaries */
10043 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10044 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10046 /* don't scroll more than one field at a time */
10047 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10049 /* don't scroll against the player's moving direction */
10050 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10051 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10052 scroll_x = old_scroll_x;
10054 else /* player has moved vertically */
10056 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10057 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10058 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10060 /* don't scroll over playfield boundaries */
10061 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10062 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10064 /* don't scroll more than one field at a time */
10065 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10067 /* don't scroll against the player's moving direction */
10068 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10069 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10070 scroll_y = old_scroll_y;
10074 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10076 if (!options.network && !AllPlayersInVisibleScreen())
10078 scroll_x = old_scroll_x;
10079 scroll_y = old_scroll_y;
10083 ScrollScreen(player, SCROLL_INIT);
10084 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10091 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10093 if (!(moved & MF_MOVING) && !player->is_pushing)
10098 player->StepFrame = 0;
10100 if (moved & MF_MOVING)
10103 printf("::: REALLY moves now\n");
10106 if (old_jx != jx && old_jy == jy)
10107 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10108 else if (old_jx == jx && old_jy != jy)
10109 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10111 DrawLevelField(jx, jy); /* for "crumbled sand" */
10113 player->last_move_dir = player->MovDir;
10114 player->is_moving = TRUE;
10116 player->is_snapping = FALSE;
10120 player->is_switching = FALSE;
10123 player->is_dropping = FALSE;
10127 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10130 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10133 int move_direction = player->MovDir;
10135 int enter_side = MV_DIR_OPPOSITE(move_direction);
10136 int leave_side = move_direction;
10138 static int trigger_sides[4][2] =
10140 /* enter side leave side */
10141 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10142 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10143 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10144 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10146 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10147 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10149 int old_element = Feld[old_jx][old_jy];
10150 int new_element = Feld[jx][jy];
10153 /* !!! TEST ONLY !!! */
10154 if (IS_CUSTOM_ELEMENT(old_element))
10155 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10157 player->index_bit, leave_side);
10159 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10160 CE_OTHER_GETS_LEFT,
10161 player->index_bit, leave_side);
10163 if (IS_CUSTOM_ELEMENT(new_element))
10164 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10165 player->index_bit, enter_side);
10167 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10168 CE_OTHER_GETS_ENTERED,
10169 player->index_bit, enter_side);
10179 CheckGravityMovementWhenNotMoving(player);
10182 player->last_move_dir = MV_NO_MOVING;
10184 player->is_moving = FALSE;
10186 #if USE_NEW_MOVE_STYLE
10187 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10188 /* ensure that the player is also allowed to move in the next frame */
10189 /* (currently, the player is forced to wait eight frames before he can try
10192 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10193 player->move_delay = 0; /* allow direct movement in the next frame */
10197 #if USE_NEW_MOVE_DELAY
10198 if (player->move_delay == -1) /* not yet initialized by DigField() */
10199 player->move_delay = player->move_delay_value;
10202 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10204 TestIfHeroTouchesBadThing(jx, jy);
10205 TestIfPlayerTouchesCustomElement(jx, jy);
10208 if (!player->active)
10209 RemoveHero(player);
10214 void ScrollPlayer(struct PlayerInfo *player, int mode)
10216 int jx = player->jx, jy = player->jy;
10217 int last_jx = player->last_jx, last_jy = player->last_jy;
10218 int move_stepsize = TILEX / player->move_delay_value;
10220 if (!player->active || !player->MovPos)
10223 if (mode == SCROLL_INIT)
10225 player->actual_frame_counter = FrameCounter;
10226 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10229 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10231 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10232 player->block_delay);
10235 #if USE_NEW_BLOCK_STYLE
10238 if (player->block_delay <= 0)
10239 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10242 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10243 Feld[last_jx][last_jy] == EL_EMPTY)
10245 int last_field_block_delay = 0; /* start with no blocking at all */
10246 int block_delay_adjustment = player->block_delay_adjustment;
10248 /* if player blocks last field, add delay for exactly one move */
10249 if (player->block_last_field)
10251 last_field_block_delay += player->move_delay_value;
10253 #if USE_GRAVITY_BUGFIX_NEW
10254 /* when blocking enabled, prevent moving up despite gravity */
10255 if (game.gravity && player->MovDir == MV_UP)
10256 block_delay_adjustment = -1;
10260 /* add block delay adjustment (also possible when not blocking) */
10261 last_field_block_delay += block_delay_adjustment;
10264 #if USE_BLOCK_DELAY_BUGFIX
10265 /* when blocking enabled, correct block delay for fast movement */
10266 if (player->block_last_field &&
10267 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10268 last_field_block_delay =
10269 player->move_delay_value + player->block_delay_adjustment;
10274 #if USE_GRAVITY_BUGFIX_NEW
10275 /* when blocking enabled, correct block delay for gravity movement */
10276 if (player->block_last_field &&
10277 game.gravity && player->MovDir == MV_UP)
10278 last_field_block_delay = player->move_delay_value - 1;
10282 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10283 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10286 #if USE_NEW_MOVE_STYLE
10287 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10288 player->block_last_field) &&
10289 Feld[last_jx][last_jy] == EL_EMPTY)
10290 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10292 if (Feld[last_jx][last_jy] == EL_EMPTY)
10293 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10298 DrawPlayer(player);
10303 else if (!FrameReached(&player->actual_frame_counter, 1))
10306 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10307 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10309 #if USE_NEW_BLOCK_STYLE
10311 if (!player->block_last_field &&
10312 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10314 RemoveField(last_jx, last_jy);
10316 Feld[last_jx][last_jy] = EL_EMPTY;
10320 /* before DrawPlayer() to draw correct player graphic for this case */
10321 if (player->MovPos == 0)
10322 CheckGravityMovement(player);
10325 DrawPlayer(player); /* needed here only to cleanup last field */
10328 if (player->MovPos == 0) /* player reached destination field */
10331 if (player->move_delay_reset_counter > 0)
10333 player->move_delay_reset_counter--;
10335 if (player->move_delay_reset_counter == 0)
10337 /* continue with normal speed after quickly moving through gate */
10338 HALVE_PLAYER_SPEED(player);
10340 /* be able to make the next move without delay */
10341 player->move_delay = 0;
10345 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10347 /* continue with normal speed after quickly moving through gate */
10348 HALVE_PLAYER_SPEED(player);
10350 /* be able to make the next move without delay */
10351 player->move_delay = 0;
10355 #if USE_NEW_BLOCK_STYLE
10357 if (player->block_last_field &&
10358 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10360 RemoveField(last_jx, last_jy);
10362 Feld[last_jx][last_jy] = EL_EMPTY;
10366 player->last_jx = jx;
10367 player->last_jy = jy;
10369 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10370 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10371 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10373 DrawPlayer(player); /* needed here only to cleanup last field */
10374 RemoveHero(player);
10376 if (local_player->friends_still_needed == 0 ||
10377 IS_SP_ELEMENT(Feld[jx][jy]))
10378 player->LevelSolved = player->GameOver = TRUE;
10382 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10383 /* this breaks one level: "machine", level 000 */
10385 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10388 int move_direction = player->MovDir;
10390 int enter_side = MV_DIR_OPPOSITE(move_direction);
10391 int leave_side = move_direction;
10393 static int trigger_sides[4][2] =
10395 /* enter side leave side */
10396 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10397 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10398 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10399 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10401 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10402 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10404 int old_jx = last_jx;
10405 int old_jy = last_jy;
10406 int old_element = Feld[old_jx][old_jy];
10407 int new_element = Feld[jx][jy];
10410 /* !!! TEST ONLY !!! */
10411 if (IS_CUSTOM_ELEMENT(old_element))
10412 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10414 player->index_bit, leave_side);
10416 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10417 CE_OTHER_GETS_LEFT,
10418 player->index_bit, leave_side);
10420 if (IS_CUSTOM_ELEMENT(new_element))
10421 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10422 player->index_bit, enter_side);
10424 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10425 CE_OTHER_GETS_ENTERED,
10426 player->index_bit, enter_side);
10432 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10434 TestIfHeroTouchesBadThing(jx, jy);
10435 TestIfPlayerTouchesCustomElement(jx, jy);
10438 /* needed because pushed element has not yet reached its destination,
10439 so it would trigger a change event at its previous field location */
10440 if (!player->is_pushing)
10442 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10445 if (!player->active)
10446 RemoveHero(player);
10449 if (level.use_step_counter)
10459 if (TimeLeft <= 10 && setup.time_limit)
10460 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10462 DrawGameValue_Time(TimeLeft);
10464 if (!TimeLeft && setup.time_limit)
10465 for (i = 0; i < MAX_PLAYERS; i++)
10466 KillHero(&stored_player[i]);
10468 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10469 DrawGameValue_Time(TimePlayed);
10472 if (tape.single_step && tape.recording && !tape.pausing &&
10473 !player->programmed_action)
10474 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10478 void ScrollScreen(struct PlayerInfo *player, int mode)
10480 static unsigned long screen_frame_counter = 0;
10482 if (mode == SCROLL_INIT)
10484 /* set scrolling step size according to actual player's moving speed */
10485 ScrollStepSize = TILEX / player->move_delay_value;
10487 screen_frame_counter = FrameCounter;
10488 ScreenMovDir = player->MovDir;
10489 ScreenMovPos = player->MovPos;
10490 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10493 else if (!FrameReached(&screen_frame_counter, 1))
10498 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10499 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10500 redraw_mask |= REDRAW_FIELD;
10503 ScreenMovDir = MV_NO_MOVING;
10506 void TestIfPlayerTouchesCustomElement(int x, int y)
10508 static int xy[4][2] =
10515 static int trigger_sides[4][2] =
10517 /* center side border side */
10518 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10519 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10520 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10521 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10523 static int touch_dir[4] =
10525 MV_LEFT | MV_RIGHT,
10530 int center_element = Feld[x][y]; /* should always be non-moving! */
10533 for (i = 0; i < NUM_DIRECTIONS; i++)
10535 int xx = x + xy[i][0];
10536 int yy = y + xy[i][1];
10537 int center_side = trigger_sides[i][0];
10538 int border_side = trigger_sides[i][1];
10539 int border_element;
10541 if (!IN_LEV_FIELD(xx, yy))
10544 if (IS_PLAYER(x, y))
10546 struct PlayerInfo *player = PLAYERINFO(x, y);
10548 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10549 border_element = Feld[xx][yy]; /* may be moving! */
10550 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10551 border_element = Feld[xx][yy];
10552 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10553 border_element = MovingOrBlocked2Element(xx, yy);
10555 continue; /* center and border element do not touch */
10558 /* !!! TEST ONLY !!! */
10559 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10560 player->index_bit, border_side);
10561 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10562 CE_OTHER_GETS_TOUCHED,
10563 player->index_bit, border_side);
10565 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10566 CE_OTHER_GETS_TOUCHED,
10567 player->index_bit, border_side);
10568 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10569 player->index_bit, border_side);
10572 else if (IS_PLAYER(xx, yy))
10574 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10576 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10578 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10579 continue; /* center and border element do not touch */
10583 /* !!! TEST ONLY !!! */
10584 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10585 player->index_bit, center_side);
10586 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10587 CE_OTHER_GETS_TOUCHED,
10588 player->index_bit, center_side);
10590 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10591 CE_OTHER_GETS_TOUCHED,
10592 player->index_bit, center_side);
10593 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10594 player->index_bit, center_side);
10602 void TestIfElementTouchesCustomElement(int x, int y)
10604 static int xy[4][2] =
10611 static int trigger_sides[4][2] =
10613 /* center side border side */
10614 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10615 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10616 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10617 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10619 static int touch_dir[4] =
10621 MV_LEFT | MV_RIGHT,
10626 boolean change_center_element = FALSE;
10627 int center_element_change_page = 0;
10628 int center_element = Feld[x][y]; /* should always be non-moving! */
10629 int border_trigger_element = EL_UNDEFINED;
10632 for (i = 0; i < NUM_DIRECTIONS; i++)
10634 int xx = x + xy[i][0];
10635 int yy = y + xy[i][1];
10636 int center_side = trigger_sides[i][0];
10637 int border_side = trigger_sides[i][1];
10638 int border_element;
10640 if (!IN_LEV_FIELD(xx, yy))
10643 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10644 border_element = Feld[xx][yy]; /* may be moving! */
10645 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10646 border_element = Feld[xx][yy];
10647 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10648 border_element = MovingOrBlocked2Element(xx, yy);
10650 continue; /* center and border element do not touch */
10652 /* check for change of center element (but change it only once) */
10653 if (IS_CUSTOM_ELEMENT(center_element) &&
10654 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10655 !change_center_element)
10657 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10659 struct ElementChangeInfo *change =
10660 &element_info[center_element].change_page[j];
10662 if (change->can_change &&
10663 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10664 change->trigger_side & border_side &&
10666 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10668 change->trigger_element == border_element
10672 change_center_element = TRUE;
10673 center_element_change_page = j;
10674 border_trigger_element = border_element;
10681 /* check for change of border element */
10682 if (IS_CUSTOM_ELEMENT(border_element) &&
10683 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10685 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10687 struct ElementChangeInfo *change =
10688 &element_info[border_element].change_page[j];
10690 if (change->can_change &&
10691 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10692 change->trigger_side & center_side &&
10694 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10696 change->trigger_element == center_element
10701 printf("::: border_element %d, %d\n", x, y);
10704 CheckElementChangeByPage(xx, yy, border_element, center_element,
10705 CE_OTHER_IS_TOUCHING, j);
10712 if (change_center_element)
10715 printf("::: center_element %d, %d\n", x, y);
10718 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10719 CE_OTHER_IS_TOUCHING, center_element_change_page);
10723 void TestIfElementHitsCustomElement(int x, int y, int direction)
10725 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10726 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10727 int hitx = x + dx, hity = y + dy;
10728 int hitting_element = Feld[x][y];
10729 int touched_element;
10731 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10732 !IS_FREE(hitx, hity) &&
10733 (!IS_MOVING(hitx, hity) ||
10734 MovDir[hitx][hity] != direction ||
10735 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10738 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10742 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10746 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10747 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10749 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10750 CE_HITTING_SOMETHING, direction);
10752 if (IN_LEV_FIELD(hitx, hity))
10754 int opposite_direction = MV_DIR_OPPOSITE(direction);
10755 int hitting_side = direction;
10756 int touched_side = opposite_direction;
10758 int touched_element = MovingOrBlocked2Element(hitx, hity);
10761 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10762 MovDir[hitx][hity] != direction ||
10763 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10772 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10773 CE_HIT_BY_SOMETHING, opposite_direction);
10775 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10776 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10778 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10780 struct ElementChangeInfo *change =
10781 &element_info[hitting_element].change_page[i];
10783 if (change->can_change &&
10784 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10785 change->trigger_side & touched_side &&
10788 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10790 change->trigger_element == touched_element
10794 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10795 CE_OTHER_IS_HITTING, i);
10801 if (IS_CUSTOM_ELEMENT(touched_element) &&
10802 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10804 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10806 struct ElementChangeInfo *change =
10807 &element_info[touched_element].change_page[i];
10809 if (change->can_change &&
10810 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10811 change->trigger_side & hitting_side &&
10813 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10815 change->trigger_element == hitting_element
10819 CheckElementChangeByPage(hitx, hity, touched_element,
10820 hitting_element, CE_OTHER_GETS_HIT, i);
10830 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10832 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10833 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10834 int hitx = x + dx, hity = y + dy;
10835 int hitting_element = Feld[x][y];
10836 int touched_element;
10838 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10839 !IS_FREE(hitx, hity) &&
10840 (!IS_MOVING(hitx, hity) ||
10841 MovDir[hitx][hity] != direction ||
10842 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10845 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10849 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10853 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10854 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10856 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10857 EP_CAN_SMASH_EVERYTHING, direction);
10859 if (IN_LEV_FIELD(hitx, hity))
10861 int opposite_direction = MV_DIR_OPPOSITE(direction);
10862 int hitting_side = direction;
10863 int touched_side = opposite_direction;
10865 int touched_element = MovingOrBlocked2Element(hitx, hity);
10868 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10869 MovDir[hitx][hity] != direction ||
10870 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10879 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10880 CE_SMASHED_BY_SOMETHING, opposite_direction);
10882 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10883 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10885 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10887 struct ElementChangeInfo *change =
10888 &element_info[hitting_element].change_page[i];
10890 if (change->can_change &&
10891 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10892 change->trigger_side & touched_side &&
10895 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10897 change->trigger_element == touched_element
10901 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10902 CE_OTHER_IS_SMASHING, i);
10908 if (IS_CUSTOM_ELEMENT(touched_element) &&
10909 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10911 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10913 struct ElementChangeInfo *change =
10914 &element_info[touched_element].change_page[i];
10916 if (change->can_change &&
10917 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10918 change->trigger_side & hitting_side &&
10920 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10922 change->trigger_element == hitting_element
10926 CheckElementChangeByPage(hitx, hity, touched_element,
10927 hitting_element, CE_OTHER_GETS_SMASHED,i);
10937 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10939 int i, kill_x = -1, kill_y = -1;
10940 int bad_element = -1;
10941 static int test_xy[4][2] =
10948 static int test_dir[4] =
10956 for (i = 0; i < NUM_DIRECTIONS; i++)
10958 int test_x, test_y, test_move_dir, test_element;
10960 test_x = good_x + test_xy[i][0];
10961 test_y = good_y + test_xy[i][1];
10963 if (!IN_LEV_FIELD(test_x, test_y))
10967 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10970 test_element = Feld[test_x][test_y];
10972 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10975 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10976 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10978 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10979 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10983 bad_element = test_element;
10989 if (kill_x != -1 || kill_y != -1)
10991 if (IS_PLAYER(good_x, good_y))
10993 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10996 if (player->shield_deadly_time_left > 0 &&
10997 !IS_INDESTRUCTIBLE(bad_element))
10998 Bang(kill_x, kill_y);
10999 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11002 if (player->shield_deadly_time_left > 0)
11003 Bang(kill_x, kill_y);
11004 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11009 Bang(good_x, good_y);
11013 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11015 int i, kill_x = -1, kill_y = -1;
11016 int bad_element = Feld[bad_x][bad_y];
11017 static int test_xy[4][2] =
11024 static int touch_dir[4] =
11026 MV_LEFT | MV_RIGHT,
11031 static int test_dir[4] =
11039 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11042 for (i = 0; i < NUM_DIRECTIONS; i++)
11044 int test_x, test_y, test_move_dir, test_element;
11046 test_x = bad_x + test_xy[i][0];
11047 test_y = bad_y + test_xy[i][1];
11048 if (!IN_LEV_FIELD(test_x, test_y))
11052 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11054 test_element = Feld[test_x][test_y];
11056 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11057 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11059 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11060 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11062 /* good thing is player or penguin that does not move away */
11063 if (IS_PLAYER(test_x, test_y))
11065 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11067 if (bad_element == EL_ROBOT && player->is_moving)
11068 continue; /* robot does not kill player if he is moving */
11070 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11072 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11073 continue; /* center and border element do not touch */
11080 else if (test_element == EL_PENGUIN)
11089 if (kill_x != -1 || kill_y != -1)
11091 if (IS_PLAYER(kill_x, kill_y))
11093 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11096 if (player->shield_deadly_time_left > 0 &&
11097 !IS_INDESTRUCTIBLE(bad_element))
11098 Bang(bad_x, bad_y);
11099 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11102 if (player->shield_deadly_time_left > 0)
11103 Bang(bad_x, bad_y);
11104 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11109 Bang(kill_x, kill_y);
11113 void TestIfHeroTouchesBadThing(int x, int y)
11115 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11118 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11120 TestIfGoodThingHitsBadThing(x, y, move_dir);
11123 void TestIfBadThingTouchesHero(int x, int y)
11125 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11128 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11130 TestIfBadThingHitsGoodThing(x, y, move_dir);
11133 void TestIfFriendTouchesBadThing(int x, int y)
11135 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11138 void TestIfBadThingTouchesFriend(int x, int y)
11140 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11143 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11145 int i, kill_x = bad_x, kill_y = bad_y;
11146 static int xy[4][2] =
11154 for (i = 0; i < NUM_DIRECTIONS; i++)
11158 x = bad_x + xy[i][0];
11159 y = bad_y + xy[i][1];
11160 if (!IN_LEV_FIELD(x, y))
11163 element = Feld[x][y];
11164 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11165 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11173 if (kill_x != bad_x || kill_y != bad_y)
11174 Bang(bad_x, bad_y);
11177 void KillHero(struct PlayerInfo *player)
11179 int jx = player->jx, jy = player->jy;
11181 if (!player->active)
11184 /* remove accessible field at the player's position */
11185 Feld[jx][jy] = EL_EMPTY;
11187 /* deactivate shield (else Bang()/Explode() would not work right) */
11188 player->shield_normal_time_left = 0;
11189 player->shield_deadly_time_left = 0;
11195 static void KillHeroUnlessEnemyProtected(int x, int y)
11197 if (!PLAYER_ENEMY_PROTECTED(x, y))
11198 KillHero(PLAYERINFO(x, y));
11201 static void KillHeroUnlessExplosionProtected(int x, int y)
11203 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11204 KillHero(PLAYERINFO(x, y));
11207 void BuryHero(struct PlayerInfo *player)
11209 int jx = player->jx, jy = player->jy;
11211 if (!player->active)
11215 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11217 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11219 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11221 player->GameOver = TRUE;
11222 RemoveHero(player);
11225 void RemoveHero(struct PlayerInfo *player)
11227 int jx = player->jx, jy = player->jy;
11228 int i, found = FALSE;
11230 player->present = FALSE;
11231 player->active = FALSE;
11233 if (!ExplodeField[jx][jy])
11234 StorePlayer[jx][jy] = 0;
11236 for (i = 0; i < MAX_PLAYERS; i++)
11237 if (stored_player[i].active)
11241 AllPlayersGone = TRUE;
11248 =============================================================================
11249 checkDiagonalPushing()
11250 -----------------------------------------------------------------------------
11251 check if diagonal input device direction results in pushing of object
11252 (by checking if the alternative direction is walkable, diggable, ...)
11253 =============================================================================
11256 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11257 int x, int y, int real_dx, int real_dy)
11259 int jx, jy, dx, dy, xx, yy;
11261 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11264 /* diagonal direction: check alternative direction */
11269 xx = jx + (dx == 0 ? real_dx : 0);
11270 yy = jy + (dy == 0 ? real_dy : 0);
11272 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11276 =============================================================================
11278 -----------------------------------------------------------------------------
11279 x, y: field next to player (non-diagonal) to try to dig to
11280 real_dx, real_dy: direction as read from input device (can be diagonal)
11281 =============================================================================
11284 int DigField(struct PlayerInfo *player,
11285 int oldx, int oldy, int x, int y,
11286 int real_dx, int real_dy, int mode)
11289 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11291 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11292 boolean player_was_pushing = player->is_pushing;
11293 int jx = oldx, jy = oldy;
11294 int dx = x - jx, dy = y - jy;
11295 int nextx = x + dx, nexty = y + dy;
11296 int move_direction = (dx == -1 ? MV_LEFT :
11297 dx == +1 ? MV_RIGHT :
11299 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11300 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11302 int dig_side = MV_DIR_OPPOSITE(move_direction);
11304 static int trigger_sides[4] =
11306 CH_SIDE_RIGHT, /* moving left */
11307 CH_SIDE_LEFT, /* moving right */
11308 CH_SIDE_BOTTOM, /* moving up */
11309 CH_SIDE_TOP, /* moving down */
11311 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11313 int old_element = Feld[jx][jy];
11316 if (is_player) /* function can also be called by EL_PENGUIN */
11318 if (player->MovPos == 0)
11320 player->is_digging = FALSE;
11321 player->is_collecting = FALSE;
11324 if (player->MovPos == 0) /* last pushing move finished */
11325 player->is_pushing = FALSE;
11327 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11329 player->is_switching = FALSE;
11330 #if USE_NEW_PUSH_DELAY
11331 player->push_delay = -1;
11333 player->push_delay = 0;
11336 return MF_NO_ACTION;
11340 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11341 return MF_NO_ACTION;
11346 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11348 if (IS_TUBE(Feld[jx][jy]) ||
11349 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11353 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11354 int tube_leave_directions[][2] =
11356 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11357 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11358 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11359 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11360 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11361 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11362 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11363 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11364 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11365 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11366 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11367 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11370 while (tube_leave_directions[i][0] != tube_element)
11373 if (tube_leave_directions[i][0] == -1) /* should not happen */
11377 if (!(tube_leave_directions[i][1] & move_direction))
11378 return MF_NO_ACTION; /* tube has no opening in this direction */
11383 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11384 old_element = Back[jx][jy];
11388 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11389 return MF_NO_ACTION; /* field has no opening in this direction */
11391 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11392 return MF_NO_ACTION; /* field has no opening in this direction */
11394 element = Feld[x][y];
11396 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11397 return MF_NO_ACTION;
11399 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11400 game.engine_version >= VERSION_IDENT(2,2,0,0))
11401 return MF_NO_ACTION;
11404 if (game.gravity && is_player && !player->is_auto_moving &&
11405 canFallDown(player) && move_direction != MV_DOWN &&
11406 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11407 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11411 if (element == EL_EMPTY_SPACE &&
11412 game.gravity && !player->is_auto_moving &&
11413 canFallDown(player) && move_direction != MV_DOWN)
11414 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11420 case EL_SP_PORT_LEFT:
11421 case EL_SP_PORT_RIGHT:
11422 case EL_SP_PORT_UP:
11423 case EL_SP_PORT_DOWN:
11424 case EL_SP_PORT_HORIZONTAL:
11425 case EL_SP_PORT_VERTICAL:
11426 case EL_SP_PORT_ANY:
11427 case EL_SP_GRAVITY_PORT_LEFT:
11428 case EL_SP_GRAVITY_PORT_RIGHT:
11429 case EL_SP_GRAVITY_PORT_UP:
11430 case EL_SP_GRAVITY_PORT_DOWN:
11432 if (!canEnterSupaplexPort(x, y, dx, dy))
11433 return MF_NO_ACTION;
11436 element != EL_SP_PORT_LEFT &&
11437 element != EL_SP_GRAVITY_PORT_LEFT &&
11438 element != EL_SP_PORT_HORIZONTAL &&
11439 element != EL_SP_PORT_ANY) ||
11441 element != EL_SP_PORT_RIGHT &&
11442 element != EL_SP_GRAVITY_PORT_RIGHT &&
11443 element != EL_SP_PORT_HORIZONTAL &&
11444 element != EL_SP_PORT_ANY) ||
11446 element != EL_SP_PORT_UP &&
11447 element != EL_SP_GRAVITY_PORT_UP &&
11448 element != EL_SP_PORT_VERTICAL &&
11449 element != EL_SP_PORT_ANY) ||
11451 element != EL_SP_PORT_DOWN &&
11452 element != EL_SP_GRAVITY_PORT_DOWN &&
11453 element != EL_SP_PORT_VERTICAL &&
11454 element != EL_SP_PORT_ANY) ||
11455 !IN_LEV_FIELD(nextx, nexty) ||
11456 !IS_FREE(nextx, nexty))
11457 return MF_NO_ACTION;
11460 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11461 element == EL_SP_GRAVITY_PORT_RIGHT ||
11462 element == EL_SP_GRAVITY_PORT_UP ||
11463 element == EL_SP_GRAVITY_PORT_DOWN)
11464 game.gravity = !game.gravity;
11466 /* automatically move to the next field with double speed */
11467 player->programmed_action = move_direction;
11469 if (player->move_delay_reset_counter == 0)
11471 player->move_delay_reset_counter = 2; /* two double speed steps */
11473 DOUBLE_PLAYER_SPEED(player);
11476 player->move_delay_reset_counter = 2;
11478 DOUBLE_PLAYER_SPEED(player);
11482 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11485 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11491 case EL_TUBE_VERTICAL:
11492 case EL_TUBE_HORIZONTAL:
11493 case EL_TUBE_VERTICAL_LEFT:
11494 case EL_TUBE_VERTICAL_RIGHT:
11495 case EL_TUBE_HORIZONTAL_UP:
11496 case EL_TUBE_HORIZONTAL_DOWN:
11497 case EL_TUBE_LEFT_UP:
11498 case EL_TUBE_LEFT_DOWN:
11499 case EL_TUBE_RIGHT_UP:
11500 case EL_TUBE_RIGHT_DOWN:
11503 int tube_enter_directions[][2] =
11505 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11506 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11507 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11508 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11509 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11510 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11511 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11512 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11513 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11514 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11515 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11516 { -1, MV_NO_MOVING }
11519 while (tube_enter_directions[i][0] != element)
11522 if (tube_enter_directions[i][0] == -1) /* should not happen */
11526 if (!(tube_enter_directions[i][1] & move_direction))
11527 return MF_NO_ACTION; /* tube has no opening in this direction */
11529 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11537 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11539 if (IS_WALKABLE(element))
11542 int sound_element = SND_ELEMENT(element);
11543 int sound_action = ACTION_WALKING;
11546 if (!ACCESS_FROM(element, opposite_direction))
11547 return MF_NO_ACTION; /* field not accessible from this direction */
11551 if (element == EL_EMPTY_SPACE &&
11552 game.gravity && !player->is_auto_moving &&
11553 canFallDown(player) && move_direction != MV_DOWN)
11554 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11557 if (IS_RND_GATE(element))
11559 if (!player->key[RND_GATE_NR(element)])
11560 return MF_NO_ACTION;
11562 else if (IS_RND_GATE_GRAY(element))
11564 if (!player->key[RND_GATE_GRAY_NR(element)])
11565 return MF_NO_ACTION;
11567 else if (element == EL_EXIT_OPEN ||
11568 element == EL_SP_EXIT_OPEN ||
11569 element == EL_SP_EXIT_OPENING)
11571 sound_action = ACTION_PASSING; /* player is passing exit */
11573 else if (element == EL_EMPTY)
11575 sound_action = ACTION_MOVING; /* nothing to walk on */
11578 /* play sound from background or player, whatever is available */
11579 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11580 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11582 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11587 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11589 else if (IS_PASSABLE(element))
11593 if (!canPassField(x, y, move_direction))
11594 return MF_NO_ACTION;
11599 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11600 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11601 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11602 return MF_NO_ACTION;
11604 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11605 return MF_NO_ACTION;
11610 if (!ACCESS_FROM(element, opposite_direction))
11611 return MF_NO_ACTION; /* field not accessible from this direction */
11613 if (IS_CUSTOM_ELEMENT(element) &&
11614 !ACCESS_FROM(element, opposite_direction))
11615 return MF_NO_ACTION; /* field not accessible from this direction */
11619 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11620 return MF_NO_ACTION;
11625 if (IS_EM_GATE(element))
11627 if (!player->key[EM_GATE_NR(element)])
11628 return MF_NO_ACTION;
11630 else if (IS_EM_GATE_GRAY(element))
11632 if (!player->key[EM_GATE_GRAY_NR(element)])
11633 return MF_NO_ACTION;
11635 else if (IS_SP_PORT(element))
11637 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11638 element == EL_SP_GRAVITY_PORT_RIGHT ||
11639 element == EL_SP_GRAVITY_PORT_UP ||
11640 element == EL_SP_GRAVITY_PORT_DOWN)
11641 game.gravity = !game.gravity;
11642 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11643 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11644 element == EL_SP_GRAVITY_ON_PORT_UP ||
11645 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11646 game.gravity = TRUE;
11647 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11648 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11649 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11650 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11651 game.gravity = FALSE;
11654 /* automatically move to the next field with double speed */
11655 player->programmed_action = move_direction;
11657 if (player->move_delay_reset_counter == 0)
11659 player->move_delay_reset_counter = 2; /* two double speed steps */
11661 DOUBLE_PLAYER_SPEED(player);
11664 player->move_delay_reset_counter = 2;
11666 DOUBLE_PLAYER_SPEED(player);
11669 PlayLevelSoundAction(x, y, ACTION_PASSING);
11673 else if (IS_DIGGABLE(element))
11677 if (mode != DF_SNAP)
11680 GfxElement[x][y] = GFX_ELEMENT(element);
11683 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11685 player->is_digging = TRUE;
11688 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11690 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11691 player->index_bit, dig_side);
11694 if (mode == DF_SNAP)
11695 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11700 else if (IS_COLLECTIBLE(element))
11704 if (is_player && mode != DF_SNAP)
11706 GfxElement[x][y] = element;
11707 player->is_collecting = TRUE;
11710 if (element == EL_SPEED_PILL)
11711 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11712 else if (element == EL_EXTRA_TIME && level.time > 0)
11715 DrawGameValue_Time(TimeLeft);
11717 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11719 player->shield_normal_time_left += 10;
11720 if (element == EL_SHIELD_DEADLY)
11721 player->shield_deadly_time_left += 10;
11723 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11725 if (player->inventory_size < MAX_INVENTORY_SIZE)
11726 player->inventory_element[player->inventory_size++] = element;
11728 DrawGameValue_Dynamite(local_player->inventory_size);
11730 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11732 player->dynabomb_count++;
11733 player->dynabombs_left++;
11735 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11737 player->dynabomb_size++;
11739 else if (element == EL_DYNABOMB_INCREASE_POWER)
11741 player->dynabomb_xl = TRUE;
11743 else if (IS_KEY(element))
11745 player->key[KEY_NR(element)] = TRUE;
11747 DrawGameValue_Keys(player->key);
11749 redraw_mask |= REDRAW_DOOR_1;
11751 else if (IS_ENVELOPE(element))
11754 player->show_envelope = element;
11756 ShowEnvelope(element - EL_ENVELOPE_1);
11759 else if (IS_DROPPABLE(element) ||
11760 IS_THROWABLE(element)) /* can be collected and dropped */
11764 if (element_info[element].collect_count == 0)
11765 player->inventory_infinite_element = element;
11767 for (i = 0; i < element_info[element].collect_count; i++)
11768 if (player->inventory_size < MAX_INVENTORY_SIZE)
11769 player->inventory_element[player->inventory_size++] = element;
11771 DrawGameValue_Dynamite(local_player->inventory_size);
11773 else if (element_info[element].collect_count > 0)
11775 local_player->gems_still_needed -=
11776 element_info[element].collect_count;
11777 if (local_player->gems_still_needed < 0)
11778 local_player->gems_still_needed = 0;
11780 DrawGameValue_Emeralds(local_player->gems_still_needed);
11783 RaiseScoreElement(element);
11784 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11787 CheckTriggeredElementChangeByPlayer(x, y, element,
11788 CE_OTHER_GETS_COLLECTED,
11789 player->index_bit, dig_side);
11792 if (mode == DF_SNAP)
11793 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11798 else if (IS_PUSHABLE(element))
11800 if (mode == DF_SNAP && element != EL_BD_ROCK)
11801 return MF_NO_ACTION;
11803 if (CAN_FALL(element) && dy)
11804 return MF_NO_ACTION;
11806 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11807 !(element == EL_SPRING && level.use_spring_bug))
11808 return MF_NO_ACTION;
11811 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11812 ((move_direction & MV_VERTICAL &&
11813 ((element_info[element].move_pattern & MV_LEFT &&
11814 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11815 (element_info[element].move_pattern & MV_RIGHT &&
11816 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11817 (move_direction & MV_HORIZONTAL &&
11818 ((element_info[element].move_pattern & MV_UP &&
11819 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11820 (element_info[element].move_pattern & MV_DOWN &&
11821 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11822 return MF_NO_ACTION;
11826 /* do not push elements already moving away faster than player */
11827 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11828 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11829 return MF_NO_ACTION;
11831 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11832 return MF_NO_ACTION;
11838 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11840 if (player->push_delay_value == -1 || !player_was_pushing)
11841 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11843 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11845 if (player->push_delay_value == -1)
11846 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11849 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11851 if (player->push_delay_value == -1 || !player_was_pushing)
11852 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11855 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11857 if (!player->is_pushing)
11858 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11862 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11863 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11864 !player_is_pushing))
11865 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11868 if (!player->is_pushing &&
11869 game.engine_version >= VERSION_IDENT(2,2,0,7))
11870 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11874 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11875 player->push_delay, player->push_delay_value,
11876 FrameCounter, game.engine_version,
11877 player_was_pushing, player->is_pushing,
11878 element, element_info[element].token_name,
11879 GET_NEW_PUSH_DELAY(element));
11882 player->is_pushing = TRUE;
11884 if (!(IN_LEV_FIELD(nextx, nexty) &&
11885 (IS_FREE(nextx, nexty) ||
11886 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11887 IS_SB_ELEMENT(element)))))
11888 return MF_NO_ACTION;
11890 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11891 return MF_NO_ACTION;
11893 #if USE_NEW_PUSH_DELAY
11896 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11897 printf("::: ALERT: %d, %d [%d / %d]\n",
11898 player->push_delay, player->push_delay2,
11899 FrameCounter, FrameCounter / 50);
11902 if (player->push_delay == -1) /* new pushing; restart delay */
11903 player->push_delay = 0;
11905 if (player->push_delay == 0) /* new pushing; restart delay */
11906 player->push_delay = FrameCounter;
11909 #if USE_NEW_PUSH_DELAY
11911 if ( (player->push_delay > 0) != (!xxx_fr) )
11912 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11913 player->push_delay,
11914 xxx_pdv2, player->push_delay2, player->push_delay_value,
11915 FrameCounter, FrameCounter / 50);
11919 if (player->push_delay > 0 &&
11920 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11921 element != EL_SPRING && element != EL_BALLOON)
11924 if (player->push_delay < player->push_delay_value &&
11925 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11926 element != EL_SPRING && element != EL_BALLOON)
11930 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11931 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11932 element != EL_SPRING && element != EL_BALLOON)
11935 /* make sure that there is no move delay before next try to push */
11936 #if USE_NEW_MOVE_DELAY
11937 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11938 player->move_delay = 0;
11940 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11941 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11944 return MF_NO_ACTION;
11948 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11951 if (IS_SB_ELEMENT(element))
11953 if (element == EL_SOKOBAN_FIELD_FULL)
11955 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11956 local_player->sokobanfields_still_needed++;
11959 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11961 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11962 local_player->sokobanfields_still_needed--;
11965 Feld[x][y] = EL_SOKOBAN_OBJECT;
11967 if (Back[x][y] == Back[nextx][nexty])
11968 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11969 else if (Back[x][y] != 0)
11970 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11973 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11976 if (local_player->sokobanfields_still_needed == 0 &&
11977 game.emulation == EMU_SOKOBAN)
11979 player->LevelSolved = player->GameOver = TRUE;
11980 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11984 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11986 InitMovingField(x, y, move_direction);
11987 GfxAction[x][y] = ACTION_PUSHING;
11989 if (mode == DF_SNAP)
11990 ContinueMoving(x, y);
11992 MovPos[x][y] = (dx != 0 ? dx : dy);
11994 Pushed[x][y] = TRUE;
11995 Pushed[nextx][nexty] = TRUE;
11997 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11998 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12000 player->push_delay_value = -1; /* get new value later */
12002 #if USE_PUSH_BUGFIX
12003 /* now: check for element change _after_ element has been pushed! */
12005 if (game.use_change_when_pushing_bug)
12007 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12010 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12011 player->index_bit, dig_side);
12012 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
12013 player->index_bit, dig_side);
12019 /* check for element change _after_ element has been pushed! */
12023 /* !!! TEST ONLY !!! */
12024 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12025 player->index_bit, dig_side);
12026 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12027 player->index_bit, dig_side);
12029 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12030 player->index_bit, dig_side);
12031 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12032 player->index_bit, dig_side);
12040 else if (IS_SWITCHABLE(element))
12042 if (PLAYER_SWITCHING(player, x, y))
12044 CheckTriggeredElementChangeByPlayer(x,y, element,
12045 CE_OTHER_GETS_PRESSED,
12046 player->index_bit, dig_side);
12051 player->is_switching = TRUE;
12052 player->switch_x = x;
12053 player->switch_y = y;
12055 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12057 if (element == EL_ROBOT_WHEEL)
12059 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12063 DrawLevelField(x, y);
12065 else if (element == EL_SP_TERMINAL)
12069 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12071 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12073 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12074 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12077 else if (IS_BELT_SWITCH(element))
12079 ToggleBeltSwitch(x, y);
12081 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12082 element == EL_SWITCHGATE_SWITCH_DOWN)
12084 ToggleSwitchgateSwitch(x, y);
12086 else if (element == EL_LIGHT_SWITCH ||
12087 element == EL_LIGHT_SWITCH_ACTIVE)
12089 ToggleLightSwitch(x, y);
12092 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12093 SND_LIGHT_SWITCH_ACTIVATING :
12094 SND_LIGHT_SWITCH_DEACTIVATING);
12097 else if (element == EL_TIMEGATE_SWITCH)
12099 ActivateTimegateSwitch(x, y);
12101 else if (element == EL_BALLOON_SWITCH_LEFT ||
12102 element == EL_BALLOON_SWITCH_RIGHT ||
12103 element == EL_BALLOON_SWITCH_UP ||
12104 element == EL_BALLOON_SWITCH_DOWN ||
12105 element == EL_BALLOON_SWITCH_ANY)
12107 if (element == EL_BALLOON_SWITCH_ANY)
12108 game.balloon_dir = move_direction;
12110 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12111 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12112 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12113 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12116 else if (element == EL_LAMP)
12118 Feld[x][y] = EL_LAMP_ACTIVE;
12119 local_player->lights_still_needed--;
12121 ResetGfxAnimation(x, y);
12122 DrawLevelField(x, y);
12124 else if (element == EL_TIME_ORB_FULL)
12126 Feld[x][y] = EL_TIME_ORB_EMPTY;
12128 DrawGameValue_Time(TimeLeft);
12130 ResetGfxAnimation(x, y);
12131 DrawLevelField(x, y);
12134 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12138 CheckTriggeredElementChangeByPlayer(x, y, element,
12139 CE_OTHER_IS_SWITCHING,
12140 player->index_bit, dig_side);
12142 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12143 player->index_bit, dig_side);
12149 if (!PLAYER_SWITCHING(player, x, y))
12151 player->is_switching = TRUE;
12152 player->switch_x = x;
12153 player->switch_y = y;
12156 /* !!! TEST ONLY !!! */
12157 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12158 player->index_bit, dig_side);
12159 CheckTriggeredElementChangeByPlayer(x, y, element,
12160 CE_OTHER_IS_SWITCHING,
12161 player->index_bit, dig_side);
12163 CheckTriggeredElementChangeByPlayer(x, y, element,
12164 CE_OTHER_IS_SWITCHING,
12165 player->index_bit, dig_side);
12166 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12167 player->index_bit, dig_side);
12172 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12173 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12174 player->index_bit, dig_side);
12175 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12176 player->index_bit, dig_side);
12178 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12179 player->index_bit, dig_side);
12180 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12181 player->index_bit, dig_side);
12185 return MF_NO_ACTION;
12188 #if USE_NEW_PUSH_DELAY
12189 player->push_delay = -1;
12191 player->push_delay = 0;
12194 #if USE_PENGUIN_COLLECT_BUG
12195 if (is_player) /* function can also be called by EL_PENGUIN */
12198 if (Feld[x][y] != element) /* really digged/collected something */
12199 player->is_collecting = !player->is_digging;
12205 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12207 int jx = player->jx, jy = player->jy;
12208 int x = jx + dx, y = jy + dy;
12209 int snap_direction = (dx == -1 ? MV_LEFT :
12210 dx == +1 ? MV_RIGHT :
12212 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12215 if (player->MovPos != 0)
12218 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12222 if (!player->active || !IN_LEV_FIELD(x, y))
12230 if (player->MovPos == 0)
12231 player->is_pushing = FALSE;
12233 player->is_snapping = FALSE;
12235 if (player->MovPos == 0)
12237 player->is_moving = FALSE;
12238 player->is_digging = FALSE;
12239 player->is_collecting = FALSE;
12245 if (player->is_snapping)
12248 player->MovDir = snap_direction;
12251 if (player->MovPos == 0)
12254 player->is_moving = FALSE;
12255 player->is_digging = FALSE;
12256 player->is_collecting = FALSE;
12259 player->is_dropping = FALSE;
12261 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12264 player->is_snapping = TRUE;
12267 if (player->MovPos == 0)
12270 player->is_moving = FALSE;
12271 player->is_digging = FALSE;
12272 player->is_collecting = FALSE;
12276 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12277 DrawLevelField(player->last_jx, player->last_jy);
12280 DrawLevelField(x, y);
12289 boolean DropElement(struct PlayerInfo *player)
12291 int old_element, new_element;
12292 int dropx = player->jx, dropy = player->jy;
12293 int drop_direction = player->MovDir;
12295 int drop_side = drop_direction;
12297 static int trigger_sides[4] =
12299 CH_SIDE_LEFT, /* dropping left */
12300 CH_SIDE_RIGHT, /* dropping right */
12301 CH_SIDE_TOP, /* dropping up */
12302 CH_SIDE_BOTTOM, /* dropping down */
12304 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12306 int drop_element = (player->inventory_size > 0 ?
12307 player->inventory_element[player->inventory_size - 1] :
12308 player->inventory_infinite_element != EL_UNDEFINED ?
12309 player->inventory_infinite_element :
12310 player->dynabombs_left > 0 ?
12311 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12314 if (IS_THROWABLE(drop_element))
12316 dropx += GET_DX_FROM_DIR(drop_direction);
12317 dropy += GET_DY_FROM_DIR(drop_direction);
12319 if (!IN_LEV_FIELD(dropx, dropy))
12323 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12324 new_element = drop_element; /* default: no change when dropping */
12326 /* check if player is active, not moving and ready to drop */
12327 if (!player->active || player->MovPos || player->drop_delay > 0)
12330 /* check if player has anything that can be dropped */
12332 if (new_element == EL_UNDEFINED)
12335 if (player->inventory_size == 0 &&
12336 player->inventory_infinite_element == EL_UNDEFINED &&
12337 player->dynabombs_left == 0)
12341 /* check if anything can be dropped at the current position */
12342 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12345 /* collected custom elements can only be dropped on empty fields */
12347 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12350 if (player->inventory_size > 0 &&
12351 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12352 && old_element != EL_EMPTY)
12356 if (old_element != EL_EMPTY)
12357 Back[dropx][dropy] = old_element; /* store old element on this field */
12359 ResetGfxAnimation(dropx, dropy);
12360 ResetRandomAnimationValue(dropx, dropy);
12362 if (player->inventory_size > 0 ||
12363 player->inventory_infinite_element != EL_UNDEFINED)
12365 if (player->inventory_size > 0)
12367 player->inventory_size--;
12370 new_element = player->inventory_element[player->inventory_size];
12373 DrawGameValue_Dynamite(local_player->inventory_size);
12375 if (new_element == EL_DYNAMITE)
12376 new_element = EL_DYNAMITE_ACTIVE;
12377 else if (new_element == EL_SP_DISK_RED)
12378 new_element = EL_SP_DISK_RED_ACTIVE;
12381 Feld[dropx][dropy] = new_element;
12383 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12384 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12385 el2img(Feld[dropx][dropy]), 0);
12387 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12390 /* needed if previous element just changed to "empty" in the last frame */
12391 Changed[dropx][dropy] = 0; /* allow another change */
12395 /* !!! TEST ONLY !!! */
12396 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12397 player->index_bit, drop_side);
12398 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12399 CE_OTHER_GETS_DROPPED,
12400 player->index_bit, drop_side);
12402 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12403 CE_OTHER_GETS_DROPPED,
12404 player->index_bit, drop_side);
12405 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12406 player->index_bit, drop_side);
12409 TestIfElementTouchesCustomElement(dropx, dropy);
12411 else /* player is dropping a dyna bomb */
12413 player->dynabombs_left--;
12416 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12419 Feld[dropx][dropy] = new_element;
12421 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12422 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12423 el2img(Feld[dropx][dropy]), 0);
12425 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12432 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12435 InitField_WithBug1(dropx, dropy, FALSE);
12437 InitField(dropx, dropy, FALSE);
12438 if (CAN_MOVE(Feld[dropx][dropy]))
12439 InitMovDir(dropx, dropy);
12443 new_element = Feld[dropx][dropy]; /* element might have changed */
12445 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12446 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12449 int move_stepsize = element_info[new_element].move_stepsize;
12451 int move_direction, nextx, nexty;
12453 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12454 MovDir[dropx][dropy] = drop_direction;
12456 move_direction = MovDir[dropx][dropy];
12457 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12458 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12461 Changed[dropx][dropy] = 0; /* allow another change */
12462 CheckCollision[dropx][dropy] = 2;
12465 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12468 WasJustMoving[dropx][dropy] = 3;
12471 InitMovingField(dropx, dropy, move_direction);
12472 ContinueMoving(dropx, dropy);
12477 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12480 Changed[dropx][dropy] = 0; /* allow another change */
12483 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12485 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12486 CE_HITTING_SOMETHING, move_direction);
12494 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12499 player->drop_delay = 8 + 8 + 8;
12503 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12508 player->is_dropping = TRUE;
12514 /* ------------------------------------------------------------------------- */
12515 /* game sound playing functions */
12516 /* ------------------------------------------------------------------------- */
12518 static int *loop_sound_frame = NULL;
12519 static int *loop_sound_volume = NULL;
12521 void InitPlayLevelSound()
12523 int num_sounds = getSoundListSize();
12525 checked_free(loop_sound_frame);
12526 checked_free(loop_sound_volume);
12528 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12529 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12532 static void PlayLevelSound(int x, int y, int nr)
12534 int sx = SCREENX(x), sy = SCREENY(y);
12535 int volume, stereo_position;
12536 int max_distance = 8;
12537 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12539 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12540 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12543 if (!IN_LEV_FIELD(x, y) ||
12544 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12545 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12548 volume = SOUND_MAX_VOLUME;
12550 if (!IN_SCR_FIELD(sx, sy))
12552 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12553 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12555 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12558 stereo_position = (SOUND_MAX_LEFT +
12559 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12560 (SCR_FIELDX + 2 * max_distance));
12562 if (IS_LOOP_SOUND(nr))
12564 /* This assures that quieter loop sounds do not overwrite louder ones,
12565 while restarting sound volume comparison with each new game frame. */
12567 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12570 loop_sound_volume[nr] = volume;
12571 loop_sound_frame[nr] = FrameCounter;
12574 PlaySoundExt(nr, volume, stereo_position, type);
12577 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12579 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12580 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12581 y < LEVELY(BY1) ? LEVELY(BY1) :
12582 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12586 static void PlayLevelSoundAction(int x, int y, int action)
12588 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12591 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12593 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12595 if (sound_effect != SND_UNDEFINED)
12596 PlayLevelSound(x, y, sound_effect);
12599 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12602 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12604 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12605 PlayLevelSound(x, y, sound_effect);
12608 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12610 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12612 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12613 PlayLevelSound(x, y, sound_effect);
12616 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12618 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12620 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12621 StopSound(sound_effect);
12624 static void PlayLevelMusic()
12626 if (levelset.music[level_nr] != MUS_UNDEFINED)
12627 PlayMusic(levelset.music[level_nr]); /* from config file */
12629 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12632 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12634 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12637 if (sample == SAMPLE_bug)
12638 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12644 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12648 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12652 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12656 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12660 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12664 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12668 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12671 case SAMPLE_android_clone:
12672 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12675 case SAMPLE_android_move:
12676 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12679 case SAMPLE_spring:
12680 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12684 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12688 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12691 case SAMPLE_eater_eat:
12692 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12696 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12699 case SAMPLE_collect:
12700 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12703 case SAMPLE_diamond:
12704 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12707 case SAMPLE_squash:
12708 /* !!! CHECK THIS !!! */
12710 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12712 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12716 case SAMPLE_wonderfall:
12717 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12721 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12725 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12729 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12733 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12737 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12741 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12744 case SAMPLE_wonder:
12745 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12749 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12752 case SAMPLE_exit_open:
12753 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12756 case SAMPLE_exit_leave:
12757 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12760 case SAMPLE_dynamite:
12761 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12765 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12769 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12773 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12777 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12781 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12785 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12789 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12794 void RaiseScore(int value)
12796 local_player->score += value;
12798 DrawGameValue_Score(local_player->score);
12801 void RaiseScoreElement(int element)
12806 case EL_BD_DIAMOND:
12807 case EL_EMERALD_YELLOW:
12808 case EL_EMERALD_RED:
12809 case EL_EMERALD_PURPLE:
12810 case EL_SP_INFOTRON:
12811 RaiseScore(level.score[SC_EMERALD]);
12814 RaiseScore(level.score[SC_DIAMOND]);
12817 RaiseScore(level.score[SC_CRYSTAL]);
12820 RaiseScore(level.score[SC_PEARL]);
12823 case EL_BD_BUTTERFLY:
12824 case EL_SP_ELECTRON:
12825 RaiseScore(level.score[SC_BUG]);
12828 case EL_BD_FIREFLY:
12829 case EL_SP_SNIKSNAK:
12830 RaiseScore(level.score[SC_SPACESHIP]);
12833 case EL_DARK_YAMYAM:
12834 RaiseScore(level.score[SC_YAMYAM]);
12837 RaiseScore(level.score[SC_ROBOT]);
12840 RaiseScore(level.score[SC_PACMAN]);
12843 RaiseScore(level.score[SC_NUT]);
12846 case EL_SP_DISK_RED:
12847 case EL_DYNABOMB_INCREASE_NUMBER:
12848 case EL_DYNABOMB_INCREASE_SIZE:
12849 case EL_DYNABOMB_INCREASE_POWER:
12850 RaiseScore(level.score[SC_DYNAMITE]);
12852 case EL_SHIELD_NORMAL:
12853 case EL_SHIELD_DEADLY:
12854 RaiseScore(level.score[SC_SHIELD]);
12856 case EL_EXTRA_TIME:
12857 RaiseScore(level.score[SC_TIME_BONUS]);
12871 RaiseScore(level.score[SC_KEY]);
12874 RaiseScore(element_info[element].collect_score);
12879 void RequestQuitGame(boolean ask_if_really_quit)
12881 if (AllPlayersGone ||
12882 !ask_if_really_quit ||
12883 level_editor_test_game ||
12884 Request("Do you really want to quit the game ?",
12885 REQ_ASK | REQ_STAY_CLOSED))
12887 #if defined(NETWORK_AVALIABLE)
12888 if (options.network)
12889 SendToServer_StopPlaying();
12893 game_status = GAME_MODE_MAIN;
12901 if (tape.playing && tape.deactivate_display)
12902 TapeDeactivateDisplayOff(TRUE);
12905 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12908 if (tape.playing && tape.deactivate_display)
12909 TapeDeactivateDisplayOn();
12916 /* ---------- new game button stuff ---------------------------------------- */
12918 /* graphic position values for game buttons */
12919 #define GAME_BUTTON_XSIZE 30
12920 #define GAME_BUTTON_YSIZE 30
12921 #define GAME_BUTTON_XPOS 5
12922 #define GAME_BUTTON_YPOS 215
12923 #define SOUND_BUTTON_XPOS 5
12924 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12926 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12927 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12928 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12929 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12930 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12931 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12938 } gamebutton_info[NUM_GAME_BUTTONS] =
12941 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12946 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12947 GAME_CTRL_ID_PAUSE,
12951 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12956 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12957 SOUND_CTRL_ID_MUSIC,
12958 "background music on/off"
12961 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12962 SOUND_CTRL_ID_LOOPS,
12963 "sound loops on/off"
12966 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12967 SOUND_CTRL_ID_SIMPLE,
12968 "normal sounds on/off"
12972 void CreateGameButtons()
12976 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12978 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12979 struct GadgetInfo *gi;
12982 unsigned long event_mask;
12983 int gd_xoffset, gd_yoffset;
12984 int gd_x1, gd_x2, gd_y1, gd_y2;
12987 gd_xoffset = gamebutton_info[i].x;
12988 gd_yoffset = gamebutton_info[i].y;
12989 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12990 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12992 if (id == GAME_CTRL_ID_STOP ||
12993 id == GAME_CTRL_ID_PAUSE ||
12994 id == GAME_CTRL_ID_PLAY)
12996 button_type = GD_TYPE_NORMAL_BUTTON;
12998 event_mask = GD_EVENT_RELEASED;
12999 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13000 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13004 button_type = GD_TYPE_CHECK_BUTTON;
13006 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13007 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13008 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13009 event_mask = GD_EVENT_PRESSED;
13010 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13011 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13014 gi = CreateGadget(GDI_CUSTOM_ID, id,
13015 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13016 GDI_X, DX + gd_xoffset,
13017 GDI_Y, DY + gd_yoffset,
13018 GDI_WIDTH, GAME_BUTTON_XSIZE,
13019 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13020 GDI_TYPE, button_type,
13021 GDI_STATE, GD_BUTTON_UNPRESSED,
13022 GDI_CHECKED, checked,
13023 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13024 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13025 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13026 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13027 GDI_EVENT_MASK, event_mask,
13028 GDI_CALLBACK_ACTION, HandleGameButtons,
13032 Error(ERR_EXIT, "cannot create gadget");
13034 game_gadget[id] = gi;
13038 void FreeGameButtons()
13042 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13043 FreeGadget(game_gadget[i]);
13046 static void MapGameButtons()
13050 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13051 MapGadget(game_gadget[i]);
13054 void UnmapGameButtons()
13058 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13059 UnmapGadget(game_gadget[i]);
13062 static void HandleGameButtons(struct GadgetInfo *gi)
13064 int id = gi->custom_id;
13066 if (game_status != GAME_MODE_PLAYING)
13071 case GAME_CTRL_ID_STOP:
13072 RequestQuitGame(TRUE);
13075 case GAME_CTRL_ID_PAUSE:
13076 if (options.network)
13078 #if defined(NETWORK_AVALIABLE)
13080 SendToServer_ContinuePlaying();
13082 SendToServer_PausePlaying();
13086 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13089 case GAME_CTRL_ID_PLAY:
13092 #if defined(NETWORK_AVALIABLE)
13093 if (options.network)
13094 SendToServer_ContinuePlaying();
13098 tape.pausing = FALSE;
13099 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13104 case SOUND_CTRL_ID_MUSIC:
13105 if (setup.sound_music)
13107 setup.sound_music = FALSE;
13110 else if (audio.music_available)
13112 setup.sound = setup.sound_music = TRUE;
13114 SetAudioMode(setup.sound);
13120 case SOUND_CTRL_ID_LOOPS:
13121 if (setup.sound_loops)
13122 setup.sound_loops = FALSE;
13123 else if (audio.loops_available)
13125 setup.sound = setup.sound_loops = TRUE;
13126 SetAudioMode(setup.sound);
13130 case SOUND_CTRL_ID_SIMPLE:
13131 if (setup.sound_simple)
13132 setup.sound_simple = FALSE;
13133 else if (audio.sound_available)
13135 setup.sound = setup.sound_simple = TRUE;
13136 SetAudioMode(setup.sound);