1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF (TRUE * 1)
33 #define USE_NEW_MOVE_STYLE (TRUE * USE_NEW_STUFF * 1)
34 #define USE_NEW_MOVE_DELAY (TRUE * USE_NEW_STUFF * 1)
35 #define USE_NEW_PUSH_DELAY (TRUE * USE_NEW_STUFF * 1)
36 #define USE_NEW_BLOCK_STYLE (TRUE * USE_NEW_STUFF * 1)
37 #define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
38 #define USE_NEW_RANDOMIZE (TRUE * USE_NEW_STUFF * 1)
40 #define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
41 #define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
43 #define USE_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
45 #define USE_BLOCK_DELAY_BUGFIX (TRUE * USE_NEW_STUFF * 1)
47 #define USE_GRAVITY_BUGFIX_NEW (TRUE * USE_NEW_STUFF * 1)
48 #define USE_GRAVITY_BUGFIX_OLD (TRUE * USE_NEW_STUFF * 0)
50 #define USE_PENGUIN_COLLECT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
52 #define USE_IMPACT_BUGFIX (TRUE * USE_NEW_STUFF * 1)
54 #define USE_HITTING_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
55 #define USE_HIT_BY_SOMETHING_BUGFIX (TRUE * USE_NEW_STUFF * 1)
63 /* for MovePlayer() */
64 #define MF_NO_ACTION 0
68 /* for ScrollPlayer() */
70 #define SCROLL_GO_ON 1
73 #define EX_PHASE_START 0
74 #define EX_TYPE_NONE 0
75 #define EX_TYPE_NORMAL (1 << 0)
76 #define EX_TYPE_CENTER (1 << 1)
77 #define EX_TYPE_BORDER (1 << 2)
78 #define EX_TYPE_CROSS (1 << 3)
79 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
81 /* special positions in the game control window (relative to control window) */
84 #define XX_EMERALDS 29
85 #define YY_EMERALDS 54
86 #define XX_DYNAMITE 29
87 #define YY_DYNAMITE 89
96 /* special positions in the game control window (relative to main window) */
97 #define DX_LEVEL (DX + XX_LEVEL)
98 #define DY_LEVEL (DY + YY_LEVEL)
99 #define DX_EMERALDS (DX + XX_EMERALDS)
100 #define DY_EMERALDS (DY + YY_EMERALDS)
101 #define DX_DYNAMITE (DX + XX_DYNAMITE)
102 #define DY_DYNAMITE (DY + YY_DYNAMITE)
103 #define DX_KEYS (DX + XX_KEYS)
104 #define DY_KEYS (DY + YY_KEYS)
105 #define DX_SCORE (DX + XX_SCORE)
106 #define DY_SCORE (DY + YY_SCORE)
107 #define DX_TIME1 (DX + XX_TIME1)
108 #define DX_TIME2 (DX + XX_TIME2)
109 #define DY_TIME (DY + YY_TIME)
111 /* values for initial player move delay (initial delay counter value) */
112 #define INITIAL_MOVE_DELAY_OFF -1
113 #define INITIAL_MOVE_DELAY_ON 0
115 /* values for player movement speed (which is in fact a delay value) */
116 #define MOVE_DELAY_NORMAL_SPEED 8
117 #define MOVE_DELAY_HIGH_SPEED 4
119 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
120 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
121 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
122 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
124 /* values for other actions */
125 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
127 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
128 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
130 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
132 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
133 RND(element_info[e].push_delay_random))
134 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
135 RND(element_info[e].drop_delay_random))
136 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
137 RND(element_info[e].move_delay_random))
138 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
139 (element_info[e].move_delay_random))
141 #define GET_TARGET_ELEMENT(e, ch) \
142 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
143 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
145 #define GET_VALID_PLAYER_ELEMENT(e) \
146 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
148 #define CAN_GROW_INTO(e) \
149 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
155 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
156 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
157 (CAN_MOVE_INTO_ACID(e) && \
158 Feld[x][y] == EL_ACID) || \
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
163 (CAN_MOVE_INTO_ACID(e) && \
164 Feld[x][y] == EL_ACID) || \
167 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
168 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
170 (CAN_MOVE_INTO_ACID(e) && \
171 Feld[x][y] == EL_ACID) || \
172 (DONT_COLLIDE_WITH(e) && \
174 !PLAYER_ENEMY_PROTECTED(x, y))))
177 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
178 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
180 (DONT_COLLIDE_WITH(e) && \
182 !PLAYER_ENEMY_PROTECTED(x, y))))
185 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
186 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
189 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
192 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
197 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
200 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
205 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
206 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
208 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
209 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
211 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
214 #define PIG_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
217 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
219 IS_FOOD_PENGUIN(Feld[x][y])))
220 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
223 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
224 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
226 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
227 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
231 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
232 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
233 (CAN_MOVE_INTO_ACID(e) && \
234 Feld[x][y] == EL_ACID) || \
235 Feld[x][y] == EL_DIAMOND))
237 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
238 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
239 (CAN_MOVE_INTO_ACID(e) && \
240 Feld[x][y] == EL_ACID) || \
241 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
243 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
244 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
245 (CAN_MOVE_INTO_ACID(e) && \
246 Feld[x][y] == EL_ACID) || \
247 IS_AMOEBOID(Feld[x][y])))
249 #define PIG_CAN_ENTER_FIELD(e, x, y) \
250 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
251 (CAN_MOVE_INTO_ACID(e) && \
252 Feld[x][y] == EL_ACID) || \
253 IS_FOOD_PIG(Feld[x][y])))
255 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
256 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
257 (CAN_MOVE_INTO_ACID(e) && \
258 Feld[x][y] == EL_ACID) || \
259 IS_FOOD_PENGUIN(Feld[x][y]) || \
260 Feld[x][y] == EL_EXIT_OPEN))
262 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
263 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
264 (CAN_MOVE_INTO_ACID(e) && \
265 Feld[x][y] == EL_ACID)))
267 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
268 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
269 (CAN_MOVE_INTO_ACID(e) && \
270 Feld[x][y] == EL_ACID) || \
273 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
274 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
275 (CAN_MOVE_INTO_ACID(e) && \
276 Feld[x][y] == EL_ACID)))
280 #define GROUP_NR(e) ((e) - EL_GROUP_START)
281 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
282 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
283 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
285 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
286 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
289 #define CE_ENTER_FIELD_COND(e, x, y) \
290 (!IS_PLAYER(x, y) && \
291 (Feld[x][y] == EL_ACID || \
292 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
294 #define CE_ENTER_FIELD_COND(e, x, y) \
295 (!IS_PLAYER(x, y) && \
296 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
299 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
300 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
302 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
303 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
305 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
306 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
307 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
308 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
310 /* game button identifiers */
311 #define GAME_CTRL_ID_STOP 0
312 #define GAME_CTRL_ID_PAUSE 1
313 #define GAME_CTRL_ID_PLAY 2
314 #define SOUND_CTRL_ID_MUSIC 3
315 #define SOUND_CTRL_ID_LOOPS 4
316 #define SOUND_CTRL_ID_SIMPLE 5
318 #define NUM_GAME_BUTTONS 6
321 /* forward declaration for internal use */
323 static void AdvanceFrameAndPlayerCounters(int);
325 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
326 static boolean MovePlayer(struct PlayerInfo *, int, int);
327 static void ScrollPlayer(struct PlayerInfo *, int);
328 static void ScrollScreen(struct PlayerInfo *, int);
330 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
332 static void InitBeltMovement(void);
333 static void CloseAllOpenTimegates(void);
334 static void CheckGravityMovement(struct PlayerInfo *);
335 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
336 static void KillHeroUnlessEnemyProtected(int, int);
337 static void KillHeroUnlessExplosionProtected(int, int);
339 static void TestIfPlayerTouchesCustomElement(int, int);
340 static void TestIfElementTouchesCustomElement(int, int);
341 static void TestIfElementHitsCustomElement(int, int, int);
343 static void TestIfElementSmashesCustomElement(int, int, int);
346 static void ChangeElement(int, int, int);
348 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
349 #define CheckTriggeredElementChange(x, y, e, ev) \
350 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
352 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
353 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
354 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
355 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
356 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
357 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
360 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
361 #define CheckElementChange(x, y, e, te, ev) \
362 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
363 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
364 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
365 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
366 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
367 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
368 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
370 static void PlayLevelSound(int, int, int);
371 static void PlayLevelSoundNearest(int, int, int);
372 static void PlayLevelSoundAction(int, int, int);
373 static void PlayLevelSoundElementAction(int, int, int, int);
374 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
375 static void PlayLevelSoundActionIfLoop(int, int, int);
376 static void StopLevelSoundActionIfLoop(int, int, int);
377 static void PlayLevelMusic();
379 static void MapGameButtons();
380 static void HandleGameButtons(struct GadgetInfo *);
382 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
385 /* ------------------------------------------------------------------------- */
386 /* definition of elements that automatically change to other elements after */
387 /* a specified time, eventually calling a function when changing */
388 /* ------------------------------------------------------------------------- */
390 /* forward declaration for changer functions */
391 static void InitBuggyBase(int x, int y);
392 static void WarnBuggyBase(int x, int y);
394 static void InitTrap(int x, int y);
395 static void ActivateTrap(int x, int y);
396 static void ChangeActiveTrap(int x, int y);
398 static void InitRobotWheel(int x, int y);
399 static void RunRobotWheel(int x, int y);
400 static void StopRobotWheel(int x, int y);
402 static void InitTimegateWheel(int x, int y);
403 static void RunTimegateWheel(int x, int y);
405 struct ChangingElementInfo
410 void (*pre_change_function)(int x, int y);
411 void (*change_function)(int x, int y);
412 void (*post_change_function)(int x, int y);
415 static struct ChangingElementInfo change_delay_list[] =
466 EL_SWITCHGATE_OPENING,
474 EL_SWITCHGATE_CLOSING,
475 EL_SWITCHGATE_CLOSED,
507 EL_ACID_SPLASH_RIGHT,
516 EL_SP_BUGGY_BASE_ACTIVATING,
523 EL_SP_BUGGY_BASE_ACTIVATING,
524 EL_SP_BUGGY_BASE_ACTIVE,
531 EL_SP_BUGGY_BASE_ACTIVE,
555 EL_ROBOT_WHEEL_ACTIVE,
563 EL_TIMEGATE_SWITCH_ACTIVE,
584 int push_delay_fixed, push_delay_random;
589 { EL_BALLOON, 0, 0 },
591 { EL_SOKOBAN_OBJECT, 2, 0 },
592 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
593 { EL_SATELLITE, 2, 0 },
594 { EL_SP_DISK_YELLOW, 2, 0 },
596 { EL_UNDEFINED, 0, 0 },
604 move_stepsize_list[] =
606 { EL_AMOEBA_DROP, 2 },
607 { EL_AMOEBA_DROPPING, 2 },
608 { EL_QUICKSAND_FILLING, 1 },
609 { EL_QUICKSAND_EMPTYING, 1 },
610 { EL_MAGIC_WALL_FILLING, 2 },
611 { EL_BD_MAGIC_WALL_FILLING, 2 },
612 { EL_MAGIC_WALL_EMPTYING, 2 },
613 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
623 collect_count_list[] =
626 { EL_BD_DIAMOND, 1 },
627 { EL_EMERALD_YELLOW, 1 },
628 { EL_EMERALD_RED, 1 },
629 { EL_EMERALD_PURPLE, 1 },
631 { EL_SP_INFOTRON, 1 },
643 access_direction_list[] =
645 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
646 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
647 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
648 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
649 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
650 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
651 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
652 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
653 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
654 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
655 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
657 { EL_SP_PORT_LEFT, MV_RIGHT },
658 { EL_SP_PORT_RIGHT, MV_LEFT },
659 { EL_SP_PORT_UP, MV_DOWN },
660 { EL_SP_PORT_DOWN, MV_UP },
661 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
662 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
663 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
664 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
665 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
666 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
667 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
668 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
669 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
670 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
671 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
672 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
673 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
674 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
675 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
677 { EL_UNDEFINED, MV_NO_MOVING }
680 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
682 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
683 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
684 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
685 IS_JUST_CHANGING(x, y))
687 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
690 void GetPlayerConfig()
692 if (!audio.sound_available)
693 setup.sound_simple = FALSE;
695 if (!audio.loops_available)
696 setup.sound_loops = FALSE;
698 if (!audio.music_available)
699 setup.sound_music = FALSE;
701 if (!video.fullscreen_available)
702 setup.fullscreen = FALSE;
704 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
706 SetAudioMode(setup.sound);
710 static int getBeltNrFromBeltElement(int element)
712 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
713 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
714 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
717 static int getBeltNrFromBeltActiveElement(int element)
719 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
720 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
721 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
724 static int getBeltNrFromBeltSwitchElement(int element)
726 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
727 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
728 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
731 static int getBeltDirNrFromBeltSwitchElement(int element)
733 static int belt_base_element[4] =
735 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
736 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
737 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
738 EL_CONVEYOR_BELT_4_SWITCH_LEFT
741 int belt_nr = getBeltNrFromBeltSwitchElement(element);
742 int belt_dir_nr = element - belt_base_element[belt_nr];
744 return (belt_dir_nr % 3);
747 static int getBeltDirFromBeltSwitchElement(int element)
749 static int belt_move_dir[3] =
756 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
758 return belt_move_dir[belt_dir_nr];
761 static void InitPlayerField(int x, int y, int element, boolean init_game)
763 if (element == EL_SP_MURPHY)
767 if (stored_player[0].present)
769 Feld[x][y] = EL_SP_MURPHY_CLONE;
775 stored_player[0].use_murphy_graphic = TRUE;
778 Feld[x][y] = EL_PLAYER_1;
784 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
785 int jx = player->jx, jy = player->jy;
787 player->present = TRUE;
789 player->block_last_field = (element == EL_SP_MURPHY ?
790 level.sp_block_last_field :
791 level.block_last_field);
793 #if USE_NEW_BLOCK_STYLE
796 /* ---------- initialize player's last field block delay --------------- */
798 /* always start with reliable default value (no adjustment needed) */
799 player->block_delay_adjustment = 0;
801 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
802 if (player->block_last_field && element == EL_SP_MURPHY)
803 player->block_delay_adjustment = 1;
805 /* special case 2: in game engines before 3.1.1, blocking was different */
806 if (game.use_block_last_field_bug)
807 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
810 /* blocking the last field when moving was corrected in version 3.1.1 */
811 if (game.use_block_last_field_bug)
813 /* even "not blocking" was blocking the last field for one frame */
814 level.block_delay = (level.block_last_field ? 7 : 1);
815 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
817 level.block_last_field = TRUE;
818 level.sp_block_last_field = TRUE;
822 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
823 level.block_delay = 8; /* when blocking, block 8 frames */
824 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
828 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
834 player->block_delay = (player->block_last_field ?
835 (element == EL_SP_MURPHY ?
836 level.sp_block_delay :
837 level.block_delay) : 0);
839 player->block_delay = (element == EL_SP_MURPHY ?
840 (player->block_last_field ? 7 : 1) :
841 (player->block_last_field ? 7 : 1));
847 printf("::: block_last_field == %d, block_delay = %d\n",
848 player->block_last_field, player->block_delay);
852 if (!options.network || player->connected)
854 player->active = TRUE;
856 /* remove potentially duplicate players */
857 if (StorePlayer[jx][jy] == Feld[x][y])
858 StorePlayer[jx][jy] = 0;
860 StorePlayer[x][y] = Feld[x][y];
864 printf("Player %d activated.\n", player->element_nr);
865 printf("[Local player is %d and currently %s.]\n",
866 local_player->element_nr,
867 local_player->active ? "active" : "not active");
871 Feld[x][y] = EL_EMPTY;
873 player->jx = player->last_jx = x;
874 player->jy = player->last_jy = y;
878 static void InitField(int x, int y, boolean init_game)
880 int element = Feld[x][y];
889 InitPlayerField(x, y, element, init_game);
892 case EL_SOKOBAN_FIELD_PLAYER:
893 element = Feld[x][y] = EL_PLAYER_1;
894 InitField(x, y, init_game);
896 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
897 InitField(x, y, init_game);
900 case EL_SOKOBAN_FIELD_EMPTY:
901 local_player->sokobanfields_still_needed++;
905 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
906 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
907 else if (x > 0 && Feld[x-1][y] == EL_ACID)
908 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
909 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
910 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
911 else if (y > 0 && Feld[x][y-1] == EL_ACID)
912 Feld[x][y] = EL_ACID_POOL_BOTTOM;
913 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
914 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
922 case EL_SPACESHIP_RIGHT:
923 case EL_SPACESHIP_UP:
924 case EL_SPACESHIP_LEFT:
925 case EL_SPACESHIP_DOWN:
927 case EL_BD_BUTTERFLY_RIGHT:
928 case EL_BD_BUTTERFLY_UP:
929 case EL_BD_BUTTERFLY_LEFT:
930 case EL_BD_BUTTERFLY_DOWN:
931 case EL_BD_BUTTERFLY:
932 case EL_BD_FIREFLY_RIGHT:
933 case EL_BD_FIREFLY_UP:
934 case EL_BD_FIREFLY_LEFT:
935 case EL_BD_FIREFLY_DOWN:
937 case EL_PACMAN_RIGHT:
961 if (y == lev_fieldy - 1)
963 Feld[x][y] = EL_AMOEBA_GROWING;
964 Store[x][y] = EL_AMOEBA_WET;
968 case EL_DYNAMITE_ACTIVE:
969 case EL_SP_DISK_RED_ACTIVE:
970 case EL_DYNABOMB_PLAYER_1_ACTIVE:
971 case EL_DYNABOMB_PLAYER_2_ACTIVE:
972 case EL_DYNABOMB_PLAYER_3_ACTIVE:
973 case EL_DYNABOMB_PLAYER_4_ACTIVE:
978 local_player->lights_still_needed++;
982 local_player->friends_still_needed++;
987 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
992 Feld[x][y] = EL_EMPTY;
997 case EL_EM_KEY_1_FILE:
998 Feld[x][y] = EL_EM_KEY_1;
1000 case EL_EM_KEY_2_FILE:
1001 Feld[x][y] = EL_EM_KEY_2;
1003 case EL_EM_KEY_3_FILE:
1004 Feld[x][y] = EL_EM_KEY_3;
1006 case EL_EM_KEY_4_FILE:
1007 Feld[x][y] = EL_EM_KEY_4;
1011 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1012 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1013 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1014 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1015 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1016 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1017 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1018 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1019 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1020 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1021 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1022 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1025 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1026 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1027 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1029 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1031 game.belt_dir[belt_nr] = belt_dir;
1032 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1034 else /* more than one switch -- set it like the first switch */
1036 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1041 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1043 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1046 case EL_LIGHT_SWITCH_ACTIVE:
1048 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1052 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1054 else if (IS_GROUP_ELEMENT(element))
1056 struct ElementGroupInfo *group = element_info[element].group;
1057 int last_anim_random_frame = gfx.anim_random_frame;
1060 if (group->choice_mode == ANIM_RANDOM)
1061 gfx.anim_random_frame = RND(group->num_elements_resolved);
1063 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1064 group->choice_mode, 0,
1067 if (group->choice_mode == ANIM_RANDOM)
1068 gfx.anim_random_frame = last_anim_random_frame;
1070 group->choice_pos++;
1072 Feld[x][y] = group->element_resolved[element_pos];
1074 InitField(x, y, init_game);
1080 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1082 InitField(x, y, init_game);
1084 /* not needed to call InitMovDir() -- already done by InitField()! */
1085 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1086 CAN_MOVE(Feld[x][y]))
1090 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1092 int old_element = Feld[x][y];
1094 InitField(x, y, init_game);
1096 /* not needed to call InitMovDir() -- already done by InitField()! */
1097 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1098 CAN_MOVE(old_element) &&
1099 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1102 /* this case is in fact a combination of not less than three bugs:
1103 first, it calls InitMovDir() for elements that can move, although this is
1104 already done by InitField(); then, it checks the element that was at this
1105 field _before_ the call to InitField() (which can change it); lastly, it
1106 was not called for "mole with direction" elements, which were treated as
1107 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1111 inline void DrawGameValue_Emeralds(int value)
1113 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1116 inline void DrawGameValue_Dynamite(int value)
1118 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1121 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1125 /* currently only 4 of 8 possible keys are displayed */
1126 for (i = 0; i < STD_NUM_KEYS; i++)
1128 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1129 el2edimg(EL_KEY_1 + i));
1132 inline void DrawGameValue_Score(int value)
1134 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1137 inline void DrawGameValue_Time(int value)
1140 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1142 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1145 inline void DrawGameValue_Level(int value)
1148 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1151 /* misuse area for displaying emeralds to draw bigger level number */
1152 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1153 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1155 /* now copy it to the area for displaying level number */
1156 BlitBitmap(drawto, drawto,
1157 DX_EMERALDS, DY_EMERALDS + 1,
1158 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1159 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1160 DX_LEVEL - 1, DY_LEVEL + 1);
1162 /* restore the area for displaying emeralds */
1163 DrawGameValue_Emeralds(local_player->gems_still_needed);
1165 /* yes, this is all really ugly :-) */
1169 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1172 int key[MAX_NUM_KEYS];
1175 for (i = 0; i < MAX_NUM_KEYS; i++)
1176 key[i] = key_bits & (1 << i);
1178 DrawGameValue_Level(level_nr);
1180 DrawGameValue_Emeralds(emeralds);
1181 DrawGameValue_Dynamite(dynamite);
1182 DrawGameValue_Score(score);
1183 DrawGameValue_Time(time);
1185 DrawGameValue_Keys(key);
1188 void DrawGameDoorValues()
1192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1194 DrawGameDoorValues_EM();
1199 DrawGameValue_Level(level_nr);
1201 DrawGameValue_Emeralds(local_player->gems_still_needed);
1202 DrawGameValue_Dynamite(local_player->inventory_size);
1203 DrawGameValue_Score(local_player->score);
1204 DrawGameValue_Time(TimeLeft);
1206 for (i = 0; i < MAX_PLAYERS; i++)
1207 DrawGameValue_Keys(stored_player[i].key);
1210 static void resolve_group_element(int group_element, int recursion_depth)
1212 static int group_nr;
1213 static struct ElementGroupInfo *group;
1214 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1217 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1219 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1220 group_element - EL_GROUP_START + 1);
1222 /* replace element which caused too deep recursion by question mark */
1223 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1228 if (recursion_depth == 0) /* initialization */
1230 group = element_info[group_element].group;
1231 group_nr = group_element - EL_GROUP_START;
1233 group->num_elements_resolved = 0;
1234 group->choice_pos = 0;
1237 for (i = 0; i < actual_group->num_elements; i++)
1239 int element = actual_group->element[i];
1241 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1244 if (IS_GROUP_ELEMENT(element))
1245 resolve_group_element(element, recursion_depth + 1);
1248 group->element_resolved[group->num_elements_resolved++] = element;
1249 element_info[element].in_group[group_nr] = TRUE;
1254 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1256 printf("::: group %d: %d resolved elements\n",
1257 group_element - EL_GROUP_START, group->num_elements_resolved);
1258 for (i = 0; i < group->num_elements_resolved; i++)
1259 printf("::: - %d ['%s']\n", group->element_resolved[i],
1260 element_info[group->element_resolved[i]].token_name);
1267 =============================================================================
1269 -----------------------------------------------------------------------------
1270 initialize game engine due to level / tape version number
1271 =============================================================================
1274 static void InitGameEngine()
1278 /* set game engine from tape file when re-playing, else from level file */
1279 game.engine_version = (tape.playing ? tape.engine_version :
1280 level.game_version);
1282 /* ---------------------------------------------------------------------- */
1283 /* set flags for bugs and changes according to active game engine version */
1284 /* ---------------------------------------------------------------------- */
1287 Summary of bugfix/change:
1288 Fixed handling for custom elements that change when pushed by the player.
1290 Fixed/changed in version:
1294 Before 3.1.0, custom elements that "change when pushing" changed directly
1295 after the player started pushing them (until then handled in "DigField()").
1296 Since 3.1.0, these custom elements are not changed until the "pushing"
1297 move of the element is finished (now handled in "ContinueMoving()").
1299 Affected levels/tapes:
1300 The first condition is generally needed for all levels/tapes before version
1301 3.1.0, which might use the old behaviour before it was changed; known tapes
1302 that are affected are some tapes from the level set "Walpurgis Gardens" by
1304 The second condition is an exception from the above case and is needed for
1305 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1306 above (including some development versions of 3.1.0), but before it was
1307 known that this change would break tapes like the above and was fixed in
1308 3.1.1, so that the changed behaviour was active although the engine version
1309 while recording maybe was before 3.1.0. There is at least one tape that is
1310 affected by this exception, which is the tape for the one-level set "Bug
1311 Machine" by Juergen Bonhagen.
1314 game.use_change_when_pushing_bug =
1315 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1317 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1318 tape.game_version < VERSION_IDENT(3,1,1,0)));
1321 Summary of bugfix/change:
1322 Fixed handling for blocking the field the player leaves when moving.
1324 Fixed/changed in version:
1328 Before 3.1.1, when "block last field when moving" was enabled, the field
1329 the player is leaving when moving was blocked for the time of the move,
1330 and was directly unblocked afterwards. This resulted in the last field
1331 being blocked for exactly one less than the number of frames of one player
1332 move. Additionally, even when blocking was disabled, the last field was
1333 blocked for exactly one frame.
1334 Since 3.1.1, due to changes in player movement handling, the last field
1335 is not blocked at all when blocking is disabled. When blocking is enabled,
1336 the last field is blocked for exactly the number of frames of one player
1337 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1338 last field is blocked for exactly one more than the number of frames of
1341 Affected levels/tapes:
1342 (!!! yet to be determined -- probably many !!!)
1345 game.use_block_last_field_bug =
1346 (game.engine_version < VERSION_IDENT(3,1,1,0));
1348 /* ---------------------------------------------------------------------- */
1350 /* dynamically adjust element properties according to game engine version */
1351 InitElementPropertiesEngine(game.engine_version);
1354 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1355 printf(" tape version == %06d [%s] [file: %06d]\n",
1356 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1358 printf(" => game.engine_version == %06d\n", game.engine_version);
1361 /* ---------- recursively resolve group elements ------------------------- */
1363 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1364 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1365 element_info[i].in_group[j] = FALSE;
1367 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1368 resolve_group_element(EL_GROUP_START + i, 0);
1370 /* ---------- initialize player's initial move delay --------------------- */
1372 #if USE_NEW_MOVE_DELAY
1373 /* dynamically adjust player properties according to level information */
1374 game.initial_move_delay_value =
1375 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1377 /* dynamically adjust player properties according to game engine version */
1378 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1379 game.initial_move_delay_value : 0);
1381 /* dynamically adjust player properties according to game engine version */
1382 game.initial_move_delay =
1383 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1384 INITIAL_MOVE_DELAY_OFF);
1386 /* dynamically adjust player properties according to level information */
1387 game.initial_move_delay_value =
1388 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1391 /* ---------- initialize player's initial push delay --------------------- */
1393 /* dynamically adjust player properties according to game engine version */
1394 game.initial_push_delay_value =
1395 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1397 /* ---------- initialize changing elements ------------------------------- */
1399 /* initialize changing elements information */
1400 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1402 struct ElementInfo *ei = &element_info[i];
1404 /* this pointer might have been changed in the level editor */
1405 ei->change = &ei->change_page[0];
1407 if (!IS_CUSTOM_ELEMENT(i))
1409 ei->change->target_element = EL_EMPTY_SPACE;
1410 ei->change->delay_fixed = 0;
1411 ei->change->delay_random = 0;
1412 ei->change->delay_frames = 1;
1415 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1417 ei->has_change_event[j] = FALSE;
1419 ei->event_page_nr[j] = 0;
1420 ei->event_page[j] = &ei->change_page[0];
1424 /* add changing elements from pre-defined list */
1425 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1427 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1428 struct ElementInfo *ei = &element_info[ch_delay->element];
1430 ei->change->target_element = ch_delay->target_element;
1431 ei->change->delay_fixed = ch_delay->change_delay;
1433 ei->change->pre_change_function = ch_delay->pre_change_function;
1434 ei->change->change_function = ch_delay->change_function;
1435 ei->change->post_change_function = ch_delay->post_change_function;
1437 ei->has_change_event[CE_DELAY] = TRUE;
1440 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1445 /* add change events from custom element configuration */
1446 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1448 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1450 for (j = 0; j < ei->num_change_pages; j++)
1452 if (!ei->change_page[j].can_change)
1455 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1457 /* only add event page for the first page found with this event */
1458 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1460 ei->has_change_event[k] = TRUE;
1462 ei->event_page_nr[k] = j;
1463 ei->event_page[k] = &ei->change_page[j];
1471 /* add change events from custom element configuration */
1472 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1474 int element = EL_CUSTOM_START + i;
1476 /* only add custom elements that change after fixed/random frame delay */
1477 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1478 element_info[element].has_change_event[CE_DELAY] = TRUE;
1482 /* ---------- initialize run-time trigger player and element ------------- */
1484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1486 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1488 for (j = 0; j < ei->num_change_pages; j++)
1490 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1491 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1495 /* ---------- initialize trigger events ---------------------------------- */
1497 /* initialize trigger events information */
1498 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1499 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1500 trigger_events[i][j] = FALSE;
1503 /* add trigger events from element change event properties */
1504 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1506 struct ElementInfo *ei = &element_info[i];
1508 for (j = 0; j < ei->num_change_pages; j++)
1510 if (!ei->change_page[j].can_change)
1513 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1515 int trigger_element = ei->change_page[j].trigger_element;
1517 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1519 if (ei->change_page[j].has_event[k])
1521 if (IS_GROUP_ELEMENT(trigger_element))
1523 struct ElementGroupInfo *group =
1524 element_info[trigger_element].group;
1526 for (l = 0; l < group->num_elements_resolved; l++)
1527 trigger_events[group->element_resolved[l]][k] = TRUE;
1530 trigger_events[trigger_element][k] = TRUE;
1537 /* add trigger events from element change event properties */
1538 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1539 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1540 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1541 if (element_info[i].change->has_event[j])
1542 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1545 /* ---------- initialize push delay -------------------------------------- */
1547 /* initialize push delay values to default */
1548 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1550 if (!IS_CUSTOM_ELEMENT(i))
1552 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1553 element_info[i].push_delay_random = game.default_push_delay_random;
1557 /* set push delay value for certain elements from pre-defined list */
1558 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1560 int e = push_delay_list[i].element;
1562 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1563 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1566 /* set push delay value for Supaplex elements for newer engine versions */
1567 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1571 if (IS_SP_ELEMENT(i))
1573 #if USE_NEW_MOVE_STYLE
1574 /* set SP push delay to just enough to push under a falling zonk */
1575 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1577 element_info[i].push_delay_fixed = delay;
1578 element_info[i].push_delay_random = 0;
1580 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1581 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1587 /* ---------- initialize move stepsize ----------------------------------- */
1589 /* initialize move stepsize values to default */
1590 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1591 if (!IS_CUSTOM_ELEMENT(i))
1592 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1594 /* set move stepsize value for certain elements from pre-defined list */
1595 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1597 int e = move_stepsize_list[i].element;
1599 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1603 /* ---------- initialize move dig/leave ---------------------------------- */
1605 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1607 element_info[i].can_leave_element = FALSE;
1608 element_info[i].can_leave_element_last = FALSE;
1612 /* ---------- initialize gem count --------------------------------------- */
1614 /* initialize gem count values for each element */
1615 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1616 if (!IS_CUSTOM_ELEMENT(i))
1617 element_info[i].collect_count = 0;
1619 /* add gem count values for all elements from pre-defined list */
1620 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1621 element_info[collect_count_list[i].element].collect_count =
1622 collect_count_list[i].count;
1624 /* ---------- initialize access direction -------------------------------- */
1626 /* initialize access direction values to default (access from every side) */
1627 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1628 if (!IS_CUSTOM_ELEMENT(i))
1629 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1631 /* set access direction value for certain elements from pre-defined list */
1632 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1633 element_info[access_direction_list[i].element].access_direction =
1634 access_direction_list[i].direction;
1639 =============================================================================
1641 -----------------------------------------------------------------------------
1642 initialize and start new game
1643 =============================================================================
1648 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1649 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1650 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1657 #if USE_NEW_AMOEBA_CODE
1658 printf("Using new amoeba code.\n");
1660 printf("Using old amoeba code.\n");
1665 /* don't play tapes over network */
1666 network_playing = (options.network && !tape.playing);
1668 for (i = 0; i < MAX_PLAYERS; i++)
1670 struct PlayerInfo *player = &stored_player[i];
1672 player->index_nr = i;
1673 player->index_bit = (1 << i);
1674 player->element_nr = EL_PLAYER_1 + i;
1676 player->present = FALSE;
1677 player->active = FALSE;
1680 player->effective_action = 0;
1681 player->programmed_action = 0;
1684 player->gems_still_needed = level.gems_needed;
1685 player->sokobanfields_still_needed = 0;
1686 player->lights_still_needed = 0;
1687 player->friends_still_needed = 0;
1689 for (j = 0; j < MAX_NUM_KEYS; j++)
1690 player->key[j] = FALSE;
1692 player->dynabomb_count = 0;
1693 player->dynabomb_size = 1;
1694 player->dynabombs_left = 0;
1695 player->dynabomb_xl = FALSE;
1697 player->MovDir = MV_NO_MOVING;
1700 player->GfxDir = MV_NO_MOVING;
1701 player->GfxAction = ACTION_DEFAULT;
1703 player->StepFrame = 0;
1705 player->use_murphy_graphic = FALSE;
1707 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1708 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1710 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1712 player->actual_frame_counter = 0;
1714 player->step_counter = 0;
1716 player->last_move_dir = MV_NO_MOVING;
1718 player->is_waiting = FALSE;
1719 player->is_moving = FALSE;
1720 player->is_auto_moving = FALSE;
1721 player->is_digging = FALSE;
1722 player->is_snapping = FALSE;
1723 player->is_collecting = FALSE;
1724 player->is_pushing = FALSE;
1725 player->is_switching = FALSE;
1726 player->is_dropping = FALSE;
1728 player->is_bored = FALSE;
1729 player->is_sleeping = FALSE;
1731 player->frame_counter_bored = -1;
1732 player->frame_counter_sleeping = -1;
1734 player->anim_delay_counter = 0;
1735 player->post_delay_counter = 0;
1737 player->action_waiting = ACTION_DEFAULT;
1738 player->last_action_waiting = ACTION_DEFAULT;
1739 player->special_action_bored = ACTION_DEFAULT;
1740 player->special_action_sleeping = ACTION_DEFAULT;
1742 player->num_special_action_bored = 0;
1743 player->num_special_action_sleeping = 0;
1745 /* determine number of special actions for bored and sleeping animation */
1746 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1748 boolean found = FALSE;
1750 for (k = 0; k < NUM_DIRECTIONS; k++)
1751 if (el_act_dir2img(player->element_nr, j, k) !=
1752 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1756 player->num_special_action_bored++;
1760 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1762 boolean found = FALSE;
1764 for (k = 0; k < NUM_DIRECTIONS; k++)
1765 if (el_act_dir2img(player->element_nr, j, k) !=
1766 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1770 player->num_special_action_sleeping++;
1775 player->switch_x = -1;
1776 player->switch_y = -1;
1778 player->show_envelope = 0;
1780 player->move_delay = game.initial_move_delay;
1781 player->move_delay_value = game.initial_move_delay_value;
1783 player->move_delay_reset_counter = 0;
1785 #if USE_NEW_PUSH_DELAY
1786 player->push_delay = -1; /* initialized when pushing starts */
1787 player->push_delay_value = game.initial_push_delay_value;
1789 player->push_delay = 0;
1790 player->push_delay_value = game.initial_push_delay_value;
1793 player->drop_delay = 0;
1795 player->last_jx = player->last_jy = 0;
1796 player->jx = player->jy = 0;
1798 player->shield_normal_time_left = 0;
1799 player->shield_deadly_time_left = 0;
1801 player->inventory_infinite_element = EL_UNDEFINED;
1802 player->inventory_size = 0;
1804 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1805 SnapField(player, 0, 0);
1807 player->LevelSolved = FALSE;
1808 player->GameOver = FALSE;
1811 network_player_action_received = FALSE;
1813 #if defined(NETWORK_AVALIABLE)
1814 /* initial null action */
1815 if (network_playing)
1816 SendToServer_MovePlayer(MV_NO_MOVING);
1825 TimeLeft = level.time;
1828 ScreenMovDir = MV_NO_MOVING;
1832 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1834 AllPlayersGone = FALSE;
1836 game.yamyam_content_nr = 0;
1837 game.magic_wall_active = FALSE;
1838 game.magic_wall_time_left = 0;
1839 game.light_time_left = 0;
1840 game.timegate_time_left = 0;
1841 game.switchgate_pos = 0;
1842 game.balloon_dir = MV_NO_MOVING;
1843 game.gravity = level.initial_gravity;
1844 game.explosions_delayed = TRUE;
1846 game.envelope_active = FALSE;
1848 for (i = 0; i < NUM_BELTS; i++)
1850 game.belt_dir[i] = MV_NO_MOVING;
1851 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1854 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1855 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1857 for (x = 0; x < lev_fieldx; x++)
1859 for (y = 0; y < lev_fieldy; y++)
1861 Feld[x][y] = level.field[x][y];
1862 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1863 ChangeDelay[x][y] = 0;
1864 ChangePage[x][y] = -1;
1865 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1867 WasJustMoving[x][y] = 0;
1868 WasJustFalling[x][y] = 0;
1869 CheckCollision[x][y] = 0;
1871 Pushed[x][y] = FALSE;
1873 Changed[x][y] = FALSE;
1874 ChangeEvent[x][y] = -1;
1876 ExplodePhase[x][y] = 0;
1877 ExplodeDelay[x][y] = 0;
1878 ExplodeField[x][y] = EX_TYPE_NONE;
1880 RunnerVisit[x][y] = 0;
1881 PlayerVisit[x][y] = 0;
1884 GfxRandom[x][y] = INIT_GFX_RANDOM();
1885 GfxElement[x][y] = EL_UNDEFINED;
1886 GfxAction[x][y] = ACTION_DEFAULT;
1887 GfxDir[x][y] = MV_NO_MOVING;
1891 for (y = 0; y < lev_fieldy; y++)
1893 for (x = 0; x < lev_fieldx; x++)
1895 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1897 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1899 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1902 InitField(x, y, TRUE);
1908 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1909 emulate_sb ? EMU_SOKOBAN :
1910 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1912 /* initialize explosion and ignition delay */
1913 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1915 if (!IS_CUSTOM_ELEMENT(i))
1918 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1919 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1920 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1921 int last_phase = (num_phase + 1) * delay;
1922 int half_phase = (num_phase / 2) * delay;
1924 element_info[i].explosion_delay = last_phase - 1;
1925 element_info[i].ignition_delay = half_phase;
1928 if (i == EL_BLACK_ORB)
1929 element_info[i].ignition_delay = 0;
1931 if (i == EL_BLACK_ORB)
1932 element_info[i].ignition_delay = 1;
1937 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1938 element_info[i].explosion_delay = 1;
1940 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1941 element_info[i].ignition_delay = 1;
1945 /* correct non-moving belts to start moving left */
1946 for (i = 0; i < NUM_BELTS; i++)
1947 if (game.belt_dir[i] == MV_NO_MOVING)
1948 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1950 /* check if any connected player was not found in playfield */
1951 for (i = 0; i < MAX_PLAYERS; i++)
1953 struct PlayerInfo *player = &stored_player[i];
1955 if (player->connected && !player->present)
1957 for (j = 0; j < MAX_PLAYERS; j++)
1959 struct PlayerInfo *some_player = &stored_player[j];
1960 int jx = some_player->jx, jy = some_player->jy;
1962 /* assign first free player found that is present in the playfield */
1963 if (some_player->present && !some_player->connected)
1965 player->present = TRUE;
1966 player->active = TRUE;
1968 some_player->present = FALSE;
1969 some_player->active = FALSE;
1972 player->element_nr = some_player->element_nr;
1975 #if USE_NEW_BLOCK_STYLE
1976 player->block_last_field = some_player->block_last_field;
1977 player->block_delay_adjustment = some_player->block_delay_adjustment;
1980 StorePlayer[jx][jy] = player->element_nr;
1981 player->jx = player->last_jx = jx;
1982 player->jy = player->last_jy = jy;
1992 /* when playing a tape, eliminate all players which do not participate */
1994 for (i = 0; i < MAX_PLAYERS; i++)
1996 if (stored_player[i].active && !tape.player_participates[i])
1998 struct PlayerInfo *player = &stored_player[i];
1999 int jx = player->jx, jy = player->jy;
2001 player->active = FALSE;
2002 StorePlayer[jx][jy] = 0;
2003 Feld[jx][jy] = EL_EMPTY;
2007 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2009 /* when in single player mode, eliminate all but the first active player */
2011 for (i = 0; i < MAX_PLAYERS; i++)
2013 if (stored_player[i].active)
2015 for (j = i + 1; j < MAX_PLAYERS; j++)
2017 if (stored_player[j].active)
2019 struct PlayerInfo *player = &stored_player[j];
2020 int jx = player->jx, jy = player->jy;
2022 player->active = FALSE;
2023 player->present = FALSE;
2025 StorePlayer[jx][jy] = 0;
2026 Feld[jx][jy] = EL_EMPTY;
2033 /* when recording the game, store which players take part in the game */
2036 for (i = 0; i < MAX_PLAYERS; i++)
2037 if (stored_player[i].active)
2038 tape.player_participates[i] = TRUE;
2043 for (i = 0; i < MAX_PLAYERS; i++)
2045 struct PlayerInfo *player = &stored_player[i];
2047 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2052 if (local_player == player)
2053 printf("Player %d is local player.\n", i+1);
2057 if (BorderElement == EL_EMPTY)
2060 SBX_Right = lev_fieldx - SCR_FIELDX;
2062 SBY_Lower = lev_fieldy - SCR_FIELDY;
2067 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2069 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2072 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2073 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2075 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2076 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2078 /* if local player not found, look for custom element that might create
2079 the player (make some assumptions about the right custom element) */
2080 if (!local_player->present)
2082 int start_x = 0, start_y = 0;
2083 int found_rating = 0;
2084 int found_element = EL_UNDEFINED;
2086 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2088 int element = Feld[x][y];
2093 if (!IS_CUSTOM_ELEMENT(element))
2096 if (CAN_CHANGE(element))
2098 for (i = 0; i < element_info[element].num_change_pages; i++)
2100 content = element_info[element].change_page[i].target_element;
2101 is_player = ELEM_IS_PLAYER(content);
2103 if (is_player && (found_rating < 3 || element < found_element))
2109 found_element = element;
2114 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2116 content = element_info[element].content[xx][yy];
2117 is_player = ELEM_IS_PLAYER(content);
2119 if (is_player && (found_rating < 2 || element < found_element))
2121 start_x = x + xx - 1;
2122 start_y = y + yy - 1;
2125 found_element = element;
2128 if (!CAN_CHANGE(element))
2131 for (i = 0; i < element_info[element].num_change_pages; i++)
2133 content= element_info[element].change_page[i].target_content[xx][yy];
2134 is_player = ELEM_IS_PLAYER(content);
2136 if (is_player && (found_rating < 1 || element < found_element))
2138 start_x = x + xx - 1;
2139 start_y = y + yy - 1;
2142 found_element = element;
2148 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2149 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2152 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2153 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2159 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2160 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2161 local_player->jx - MIDPOSX);
2163 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2164 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2165 local_player->jy - MIDPOSY);
2167 scroll_x = SBX_Left;
2168 scroll_y = SBY_Upper;
2169 if (local_player->jx >= SBX_Left + MIDPOSX)
2170 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2171 local_player->jx - MIDPOSX :
2173 if (local_player->jy >= SBY_Upper + MIDPOSY)
2174 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2175 local_player->jy - MIDPOSY :
2180 CloseDoor(DOOR_CLOSE_1);
2182 /* !!! FIX THIS (START) !!! */
2183 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2185 InitGameEngine_EM();
2192 /* after drawing the level, correct some elements */
2193 if (game.timegate_time_left == 0)
2194 CloseAllOpenTimegates();
2196 if (setup.soft_scrolling)
2197 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2199 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2202 /* !!! FIX THIS (END) !!! */
2204 /* copy default game door content to main double buffer */
2205 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2206 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2208 DrawGameDoorValues();
2212 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2213 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2214 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2218 /* copy actual game door content to door double buffer for OpenDoor() */
2219 BlitBitmap(drawto, bitmap_db_door,
2220 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2222 OpenDoor(DOOR_OPEN_ALL);
2224 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2226 if (setup.sound_music)
2229 KeyboardAutoRepeatOffUnlessAutoplay();
2233 for (i = 0; i < MAX_PLAYERS; i++)
2234 printf("Player %d %sactive.\n",
2235 i + 1, (stored_player[i].active ? "" : "not "));
2239 printf("::: starting game [%d]\n", FrameCounter);
2243 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2245 /* this is used for non-R'n'D game engines to update certain engine values */
2247 /* needed to determine if sounds are played within the visible screen area */
2248 scroll_x = actual_scroll_x;
2249 scroll_y = actual_scroll_y;
2252 void InitMovDir(int x, int y)
2254 int i, element = Feld[x][y];
2255 static int xy[4][2] =
2262 static int direction[3][4] =
2264 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2265 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2266 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2275 Feld[x][y] = EL_BUG;
2276 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2279 case EL_SPACESHIP_RIGHT:
2280 case EL_SPACESHIP_UP:
2281 case EL_SPACESHIP_LEFT:
2282 case EL_SPACESHIP_DOWN:
2283 Feld[x][y] = EL_SPACESHIP;
2284 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2287 case EL_BD_BUTTERFLY_RIGHT:
2288 case EL_BD_BUTTERFLY_UP:
2289 case EL_BD_BUTTERFLY_LEFT:
2290 case EL_BD_BUTTERFLY_DOWN:
2291 Feld[x][y] = EL_BD_BUTTERFLY;
2292 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2295 case EL_BD_FIREFLY_RIGHT:
2296 case EL_BD_FIREFLY_UP:
2297 case EL_BD_FIREFLY_LEFT:
2298 case EL_BD_FIREFLY_DOWN:
2299 Feld[x][y] = EL_BD_FIREFLY;
2300 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2303 case EL_PACMAN_RIGHT:
2305 case EL_PACMAN_LEFT:
2306 case EL_PACMAN_DOWN:
2307 Feld[x][y] = EL_PACMAN;
2308 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2311 case EL_SP_SNIKSNAK:
2312 MovDir[x][y] = MV_UP;
2315 case EL_SP_ELECTRON:
2316 MovDir[x][y] = MV_LEFT;
2323 Feld[x][y] = EL_MOLE;
2324 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2328 if (IS_CUSTOM_ELEMENT(element))
2330 struct ElementInfo *ei = &element_info[element];
2331 int move_direction_initial = ei->move_direction_initial;
2332 int move_pattern = ei->move_pattern;
2334 if (move_direction_initial == MV_START_PREVIOUS)
2336 if (MovDir[x][y] != MV_NO_MOVING)
2339 move_direction_initial = MV_START_AUTOMATIC;
2342 if (move_direction_initial == MV_START_RANDOM)
2343 MovDir[x][y] = 1 << RND(4);
2344 else if (move_direction_initial & MV_ANY_DIRECTION)
2345 MovDir[x][y] = move_direction_initial;
2346 else if (move_pattern == MV_ALL_DIRECTIONS ||
2347 move_pattern == MV_TURNING_LEFT ||
2348 move_pattern == MV_TURNING_RIGHT ||
2349 move_pattern == MV_TURNING_LEFT_RIGHT ||
2350 move_pattern == MV_TURNING_RIGHT_LEFT ||
2351 move_pattern == MV_TURNING_RANDOM)
2352 MovDir[x][y] = 1 << RND(4);
2353 else if (move_pattern == MV_HORIZONTAL)
2354 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2355 else if (move_pattern == MV_VERTICAL)
2356 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2357 else if (move_pattern & MV_ANY_DIRECTION)
2358 MovDir[x][y] = element_info[element].move_pattern;
2359 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2360 move_pattern == MV_ALONG_RIGHT_SIDE)
2363 /* use random direction as default start direction */
2364 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2365 MovDir[x][y] = 1 << RND(4);
2368 for (i = 0; i < NUM_DIRECTIONS; i++)
2370 int x1 = x + xy[i][0];
2371 int y1 = y + xy[i][1];
2373 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2375 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2376 MovDir[x][y] = direction[0][i];
2378 MovDir[x][y] = direction[1][i];
2387 MovDir[x][y] = 1 << RND(4);
2389 if (element != EL_BUG &&
2390 element != EL_SPACESHIP &&
2391 element != EL_BD_BUTTERFLY &&
2392 element != EL_BD_FIREFLY)
2395 for (i = 0; i < NUM_DIRECTIONS; i++)
2397 int x1 = x + xy[i][0];
2398 int y1 = y + xy[i][1];
2400 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2402 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2404 MovDir[x][y] = direction[0][i];
2407 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2408 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2410 MovDir[x][y] = direction[1][i];
2419 GfxDir[x][y] = MovDir[x][y];
2422 void InitAmoebaNr(int x, int y)
2425 int group_nr = AmoebeNachbarNr(x, y);
2429 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2431 if (AmoebaCnt[i] == 0)
2439 AmoebaNr[x][y] = group_nr;
2440 AmoebaCnt[group_nr]++;
2441 AmoebaCnt2[group_nr]++;
2447 boolean raise_level = FALSE;
2449 if (local_player->MovPos)
2453 if (tape.auto_play) /* tape might already be stopped here */
2454 tape.auto_play_level_solved = TRUE;
2456 if (tape.playing && tape.auto_play)
2457 tape.auto_play_level_solved = TRUE;
2460 local_player->LevelSolved = FALSE;
2462 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2466 if (!tape.playing && setup.sound_loops)
2467 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2468 SND_CTRL_PLAY_LOOP);
2470 while (TimeLeft > 0)
2472 if (!tape.playing && !setup.sound_loops)
2473 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2474 if (TimeLeft > 0 && !(TimeLeft % 10))
2475 RaiseScore(level.score[SC_TIME_BONUS]);
2476 if (TimeLeft > 100 && !(TimeLeft % 10))
2481 DrawGameValue_Time(TimeLeft);
2489 if (!tape.playing && setup.sound_loops)
2490 StopSound(SND_GAME_LEVELTIME_BONUS);
2492 else if (level.time == 0) /* level without time limit */
2494 if (!tape.playing && setup.sound_loops)
2495 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2496 SND_CTRL_PLAY_LOOP);
2498 while (TimePlayed < 999)
2500 if (!tape.playing && !setup.sound_loops)
2501 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2502 if (TimePlayed < 999 && !(TimePlayed % 10))
2503 RaiseScore(level.score[SC_TIME_BONUS]);
2504 if (TimePlayed < 900 && !(TimePlayed % 10))
2509 DrawGameValue_Time(TimePlayed);
2517 if (!tape.playing && setup.sound_loops)
2518 StopSound(SND_GAME_LEVELTIME_BONUS);
2521 /* close exit door after last player */
2522 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2523 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2524 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2526 int element = Feld[ExitX][ExitY];
2528 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2529 EL_SP_EXIT_CLOSING);
2531 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2534 /* Hero disappears */
2535 if (ExitX >= 0 && ExitY >= 0)
2536 DrawLevelField(ExitX, ExitY);
2543 CloseDoor(DOOR_CLOSE_1);
2548 SaveTape(tape.level_nr); /* Ask to save tape */
2551 if (level_nr == leveldir_current->handicap_level)
2553 leveldir_current->handicap_level++;
2554 SaveLevelSetup_SeriesInfo();
2557 if (level_editor_test_game)
2558 local_player->score = -1; /* no highscore when playing from editor */
2559 else if (level_nr < leveldir_current->last_level)
2560 raise_level = TRUE; /* advance to next level */
2562 if ((hi_pos = NewHiScore()) >= 0)
2564 game_status = GAME_MODE_SCORES;
2565 DrawHallOfFame(hi_pos);
2574 game_status = GAME_MODE_MAIN;
2591 LoadScore(level_nr);
2593 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2594 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2597 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2599 if (local_player->score > highscore[k].Score)
2601 /* player has made it to the hall of fame */
2603 if (k < MAX_SCORE_ENTRIES - 1)
2605 int m = MAX_SCORE_ENTRIES - 1;
2608 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2609 if (!strcmp(setup.player_name, highscore[l].Name))
2611 if (m == k) /* player's new highscore overwrites his old one */
2615 for (l = m; l > k; l--)
2617 strcpy(highscore[l].Name, highscore[l - 1].Name);
2618 highscore[l].Score = highscore[l - 1].Score;
2625 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2626 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2627 highscore[k].Score = local_player->score;
2633 else if (!strncmp(setup.player_name, highscore[k].Name,
2634 MAX_PLAYER_NAME_LEN))
2635 break; /* player already there with a higher score */
2641 SaveScore(level_nr);
2646 inline static int getElementMoveStepsize(int x, int y)
2648 int element = Feld[x][y];
2649 int direction = MovDir[x][y];
2650 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2651 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2652 int horiz_move = (dx != 0);
2653 int sign = (horiz_move ? dx : dy);
2654 int step = sign * element_info[element].move_stepsize;
2656 /* special values for move stepsize for spring and things on conveyor belt */
2660 if (element == EL_SPRING)
2661 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2662 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2663 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2664 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2666 if (CAN_FALL(element) &&
2667 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2668 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2669 else if (element == EL_SPRING)
2670 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2677 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2679 if (player->GfxAction != action || player->GfxDir != dir)
2682 printf("Player frame reset! (%d => %d, %d => %d)\n",
2683 player->GfxAction, action, player->GfxDir, dir);
2686 player->GfxAction = action;
2687 player->GfxDir = dir;
2689 player->StepFrame = 0;
2693 static void ResetRandomAnimationValue(int x, int y)
2695 GfxRandom[x][y] = INIT_GFX_RANDOM();
2698 static void ResetGfxAnimation(int x, int y)
2701 GfxAction[x][y] = ACTION_DEFAULT;
2702 GfxDir[x][y] = MovDir[x][y];
2705 void InitMovingField(int x, int y, int direction)
2707 int element = Feld[x][y];
2708 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2709 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2713 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2714 ResetGfxAnimation(x, y);
2716 #if USE_CAN_MOVE_NOT_MOVING
2718 MovDir[x][y] = direction;
2719 GfxDir[x][y] = direction;
2720 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2721 ACTION_FALLING : ACTION_MOVING);
2723 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2725 if (Feld[newx][newy] == EL_EMPTY)
2726 Feld[newx][newy] = EL_BLOCKED;
2728 MovDir[newx][newy] = MovDir[x][y];
2729 GfxFrame[newx][newy] = GfxFrame[x][y];
2730 GfxRandom[newx][newy] = GfxRandom[x][y];
2731 GfxAction[newx][newy] = GfxAction[x][y];
2732 GfxDir[newx][newy] = GfxDir[x][y];
2737 MovDir[newx][newy] = MovDir[x][y] = direction;
2738 GfxDir[x][y] = direction;
2740 if (Feld[newx][newy] == EL_EMPTY)
2741 Feld[newx][newy] = EL_BLOCKED;
2743 if (direction == MV_DOWN && CAN_FALL(element))
2744 GfxAction[x][y] = ACTION_FALLING;
2746 GfxAction[x][y] = ACTION_MOVING;
2748 GfxFrame[newx][newy] = GfxFrame[x][y];
2749 GfxRandom[newx][newy] = GfxRandom[x][y];
2750 GfxAction[newx][newy] = GfxAction[x][y];
2751 GfxDir[newx][newy] = GfxDir[x][y];
2755 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2757 int direction = MovDir[x][y];
2758 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2759 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2765 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2767 int oldx = x, oldy = y;
2768 int direction = MovDir[x][y];
2770 if (direction == MV_LEFT)
2772 else if (direction == MV_RIGHT)
2774 else if (direction == MV_UP)
2776 else if (direction == MV_DOWN)
2779 *comes_from_x = oldx;
2780 *comes_from_y = oldy;
2783 int MovingOrBlocked2Element(int x, int y)
2785 int element = Feld[x][y];
2787 if (element == EL_BLOCKED)
2791 Blocked2Moving(x, y, &oldx, &oldy);
2792 return Feld[oldx][oldy];
2798 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2800 /* like MovingOrBlocked2Element(), but if element is moving
2801 and (x,y) is the field the moving element is just leaving,
2802 return EL_BLOCKED instead of the element value */
2803 int element = Feld[x][y];
2805 if (IS_MOVING(x, y))
2807 if (element == EL_BLOCKED)
2811 Blocked2Moving(x, y, &oldx, &oldy);
2812 return Feld[oldx][oldy];
2821 static void RemoveField(int x, int y)
2823 Feld[x][y] = EL_EMPTY;
2830 ChangeDelay[x][y] = 0;
2831 ChangePage[x][y] = -1;
2832 Pushed[x][y] = FALSE;
2835 ExplodeField[x][y] = EX_TYPE_NONE;
2838 GfxElement[x][y] = EL_UNDEFINED;
2839 GfxAction[x][y] = ACTION_DEFAULT;
2840 GfxDir[x][y] = MV_NO_MOVING;
2843 void RemoveMovingField(int x, int y)
2845 int oldx = x, oldy = y, newx = x, newy = y;
2846 int element = Feld[x][y];
2847 int next_element = EL_UNDEFINED;
2849 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2852 if (IS_MOVING(x, y))
2854 Moving2Blocked(x, y, &newx, &newy);
2856 if (Feld[newx][newy] != EL_BLOCKED)
2859 if (Feld[newx][newy] != EL_BLOCKED)
2861 /* element is moving, but target field is not free (blocked), but
2862 already occupied by something different (example: acid pool);
2863 in this case, only remove the moving field, but not the target */
2865 RemoveField(oldx, oldy);
2867 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2869 DrawLevelField(oldx, oldy);
2875 else if (element == EL_BLOCKED)
2877 Blocked2Moving(x, y, &oldx, &oldy);
2878 if (!IS_MOVING(oldx, oldy))
2882 if (element == EL_BLOCKED &&
2883 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2884 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2885 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2886 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2887 next_element = get_next_element(Feld[oldx][oldy]);
2889 RemoveField(oldx, oldy);
2890 RemoveField(newx, newy);
2892 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2894 if (next_element != EL_UNDEFINED)
2895 Feld[oldx][oldy] = next_element;
2897 DrawLevelField(oldx, oldy);
2898 DrawLevelField(newx, newy);
2901 void DrawDynamite(int x, int y)
2903 int sx = SCREENX(x), sy = SCREENY(y);
2904 int graphic = el2img(Feld[x][y]);
2907 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2910 if (IS_WALKABLE_INSIDE(Back[x][y]))
2914 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2915 else if (Store[x][y])
2916 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2918 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2921 if (Back[x][y] || Store[x][y])
2922 DrawGraphicThruMask(sx, sy, graphic, frame);
2924 DrawGraphic(sx, sy, graphic, frame);
2926 if (game.emulation == EMU_SUPAPLEX)
2927 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2928 else if (Store[x][y])
2929 DrawGraphicThruMask(sx, sy, graphic, frame);
2931 DrawGraphic(sx, sy, graphic, frame);
2935 void CheckDynamite(int x, int y)
2937 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2941 if (MovDelay[x][y] != 0)
2944 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2951 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2953 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2954 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2955 StopSound(SND_DYNAMITE_ACTIVE);
2957 StopSound(SND_DYNABOMB_ACTIVE);
2963 void DrawRelocatePlayer(struct PlayerInfo *player)
2965 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2966 boolean no_delay = (tape.warp_forward);
2967 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2968 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2969 int jx = player->jx;
2970 int jy = player->jy;
2972 if (level.instant_relocation)
2975 int offset = (setup.scroll_delay ? 3 : 0);
2977 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2979 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2980 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2981 local_player->jx - MIDPOSX);
2983 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2984 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2985 local_player->jy - MIDPOSY);
2989 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2990 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2991 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2993 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2994 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2995 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2997 /* don't scroll over playfield boundaries */
2998 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2999 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3001 /* don't scroll over playfield boundaries */
3002 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3003 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3006 scroll_x += (local_player->jx - old_jx);
3007 scroll_y += (local_player->jy - old_jy);
3009 /* don't scroll over playfield boundaries */
3010 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3011 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3013 /* don't scroll over playfield boundaries */
3014 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3015 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3018 RedrawPlayfield(TRUE, 0,0,0,0);
3024 int offset = (setup.scroll_delay ? 3 : 0);
3026 int scroll_xx = -999, scroll_yy = -999;
3028 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3030 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3033 int fx = FX, fy = FY;
3035 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3036 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3037 local_player->jx - MIDPOSX);
3039 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3040 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3041 local_player->jy - MIDPOSY);
3043 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3044 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3047 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3050 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3057 fx += dx * TILEX / 2;
3058 fy += dy * TILEY / 2;
3060 ScrollLevel(dx, dy);
3063 /* scroll in two steps of half tile size to make things smoother */
3064 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3066 Delay(wait_delay_value);
3068 /* scroll second step to align at full tile size */
3070 Delay(wait_delay_value);
3073 int scroll_xx = -999, scroll_yy = -999;
3075 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3077 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3080 int fx = FX, fy = FY;
3082 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3083 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3084 local_player->jx - MIDPOSX);
3086 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3087 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3088 local_player->jy - MIDPOSY);
3090 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3091 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3094 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3097 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3104 fx += dx * TILEX / 2;
3105 fy += dy * TILEY / 2;
3107 ScrollLevel(dx, dy);
3110 /* scroll in two steps of half tile size to make things smoother */
3111 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3113 Delay(wait_delay_value);
3115 /* scroll second step to align at full tile size */
3117 Delay(wait_delay_value);
3123 Delay(wait_delay_value);
3127 void RelocatePlayer(int jx, int jy, int el_player_raw)
3130 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3132 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3134 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3135 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3136 boolean no_delay = (tape.warp_forward);
3137 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3138 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3139 int old_jx = player->jx;
3140 int old_jy = player->jy;
3141 int old_element = Feld[old_jx][old_jy];
3142 int element = Feld[jx][jy];
3143 boolean player_relocated = (old_jx != jx || old_jy != jy);
3145 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3146 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3148 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3149 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3150 int leave_side_horiz = move_dir_horiz;
3151 int leave_side_vert = move_dir_vert;
3153 static int trigger_sides[4][2] =
3155 /* enter side leave side */
3156 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3157 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3158 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3159 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3161 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3162 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3163 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3164 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3166 int enter_side = enter_side_horiz | enter_side_vert;
3167 int leave_side = leave_side_horiz | leave_side_vert;
3169 if (player->GameOver) /* do not reanimate dead player */
3172 if (!player_relocated) /* no need to relocate the player */
3175 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3177 RemoveField(jx, jy); /* temporarily remove newly placed player */
3178 DrawLevelField(jx, jy);
3181 if (player->present)
3183 while (player->MovPos)
3185 ScrollPlayer(player, SCROLL_GO_ON);
3186 ScrollScreen(NULL, SCROLL_GO_ON);
3188 #if USE_NEW_MOVE_DELAY
3189 AdvanceFrameAndPlayerCounters(player->index_nr);
3197 Delay(wait_delay_value);
3200 DrawPlayer(player); /* needed here only to cleanup last field */
3201 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3203 player->is_moving = FALSE;
3207 if (IS_CUSTOM_ELEMENT(old_element))
3208 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3210 player->index_bit, leave_side);
3212 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3214 player->index_bit, leave_side);
3217 Feld[jx][jy] = el_player;
3218 InitPlayerField(jx, jy, el_player, TRUE);
3220 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3222 Feld[jx][jy] = element;
3223 InitField(jx, jy, FALSE);
3227 if (player == local_player) /* only visually relocate local player */
3228 DrawRelocatePlayer(player);
3232 TestIfHeroTouchesBadThing(jx, jy);
3233 TestIfPlayerTouchesCustomElement(jx, jy);
3237 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3242 /* needed to allow change of walkable custom element by entering player */
3243 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3244 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3246 /* needed to allow change of walkable custom element by entering player */
3247 Changed[jx][jy] = 0; /* allow another change */
3252 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3253 enter_side == MV_LEFT ? "left" :
3254 enter_side == MV_RIGHT ? "right" :
3255 enter_side == MV_UP ? "top" :
3256 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3260 if (IS_CUSTOM_ELEMENT(element))
3261 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3262 player->index_bit, enter_side);
3264 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3266 player->index_bit, enter_side);
3270 void Explode(int ex, int ey, int phase, int mode)
3277 /* !!! eliminate this variable !!! */
3278 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3283 int last_phase = num_phase * delay;
3284 int half_phase = (num_phase / 2) * delay;
3285 int first_phase_after_start = EX_PHASE_START + 1;
3289 if (game.explosions_delayed)
3291 ExplodeField[ex][ey] = mode;
3295 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3297 int center_element = Feld[ex][ey];
3300 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3304 /* --- This is only really needed (and now handled) in "Impact()". --- */
3305 /* do not explode moving elements that left the explode field in time */
3306 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3307 center_element == EL_EMPTY &&
3308 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3313 if (mode == EX_TYPE_NORMAL ||
3314 mode == EX_TYPE_CENTER ||
3315 mode == EX_TYPE_CROSS)
3316 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3318 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3319 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3322 /* remove things displayed in background while burning dynamite */
3323 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3326 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3328 /* put moving element to center field (and let it explode there) */
3329 center_element = MovingOrBlocked2Element(ex, ey);
3330 RemoveMovingField(ex, ey);
3331 Feld[ex][ey] = center_element;
3337 last_phase = element_info[center_element].explosion_delay + 1;
3339 last_phase = element_info[center_element].explosion_delay;
3343 printf("::: %d -> %d\n", center_element, last_phase);
3347 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3349 int xx = x - ex + 1;
3350 int yy = y - ey + 1;
3355 if (!IN_LEV_FIELD(x, y) ||
3356 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3357 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3360 if (!IN_LEV_FIELD(x, y) ||
3361 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3365 if (!IN_LEV_FIELD(x, y) ||
3366 ((mode != EX_TYPE_NORMAL ||
3367 center_element == EL_AMOEBA_TO_DIAMOND) &&
3368 (x != ex || y != ey)))
3372 element = Feld[x][y];
3374 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3376 element = MovingOrBlocked2Element(x, y);
3378 if (!IS_EXPLOSION_PROOF(element))
3379 RemoveMovingField(x, y);
3385 if (IS_EXPLOSION_PROOF(element))
3388 /* indestructible elements can only explode in center (but not flames) */
3390 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3391 mode == EX_TYPE_BORDER)) ||
3392 element == EL_FLAMES)
3395 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3396 element == EL_FLAMES)
3402 if ((IS_INDESTRUCTIBLE(element) &&
3403 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3404 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3405 element == EL_FLAMES)
3409 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3410 behaviour, for example when touching a yamyam that explodes to rocks
3411 with active deadly shield, a rock is created under the player !!! */
3412 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3414 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3415 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3416 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3418 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3421 if (IS_ACTIVE_BOMB(element))
3423 /* re-activate things under the bomb like gate or penguin */
3425 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3428 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3433 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3434 element_info[Feld[x][y]].token_name,
3435 Store[x][y], Store2[x][y]);
3442 /* save walkable background elements while explosion on same tile */
3444 if (IS_INDESTRUCTIBLE(element))
3445 Back[x][y] = element;
3449 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3450 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3451 Back[x][y] = element;
3453 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3454 (x != ex || y != ey))
3455 Back[x][y] = element;
3458 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3459 Back[x][y] = element;
3463 /* ignite explodable elements reached by other explosion */
3464 if (element == EL_EXPLOSION)
3465 element = Store2[x][y];
3468 if (AmoebaNr[x][y] &&
3469 (element == EL_AMOEBA_FULL ||
3470 element == EL_BD_AMOEBA ||
3471 element == EL_AMOEBA_GROWING))
3473 AmoebaCnt[AmoebaNr[x][y]]--;
3474 AmoebaCnt2[AmoebaNr[x][y]]--;
3480 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3482 switch(StorePlayer[ex][ey])
3485 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3488 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3491 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3495 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3500 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3501 Store[x][y] = EL_EMPTY;
3503 if (game.emulation == EMU_SUPAPLEX)
3504 Store[x][y] = EL_EMPTY;
3507 else if (center_element == EL_MOLE)
3508 Store[x][y] = EL_EMERALD_RED;
3509 else if (center_element == EL_PENGUIN)
3510 Store[x][y] = EL_EMERALD_PURPLE;
3511 else if (center_element == EL_BUG)
3512 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3513 else if (center_element == EL_BD_BUTTERFLY)
3514 Store[x][y] = EL_BD_DIAMOND;
3515 else if (center_element == EL_SP_ELECTRON)
3516 Store[x][y] = EL_SP_INFOTRON;
3517 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3518 Store[x][y] = level.amoeba_content;
3519 else if (center_element == EL_YAMYAM)
3520 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3521 else if (IS_CUSTOM_ELEMENT(center_element) &&
3522 element_info[center_element].content[xx][yy] != EL_EMPTY)
3523 Store[x][y] = element_info[center_element].content[xx][yy];
3524 else if (element == EL_WALL_EMERALD)
3525 Store[x][y] = EL_EMERALD;
3526 else if (element == EL_WALL_DIAMOND)
3527 Store[x][y] = EL_DIAMOND;
3528 else if (element == EL_WALL_BD_DIAMOND)
3529 Store[x][y] = EL_BD_DIAMOND;
3530 else if (element == EL_WALL_EMERALD_YELLOW)
3531 Store[x][y] = EL_EMERALD_YELLOW;
3532 else if (element == EL_WALL_EMERALD_RED)
3533 Store[x][y] = EL_EMERALD_RED;
3534 else if (element == EL_WALL_EMERALD_PURPLE)
3535 Store[x][y] = EL_EMERALD_PURPLE;
3536 else if (element == EL_WALL_PEARL)
3537 Store[x][y] = EL_PEARL;
3538 else if (element == EL_WALL_CRYSTAL)
3539 Store[x][y] = EL_CRYSTAL;
3540 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3541 Store[x][y] = element_info[element].content[1][1];
3543 Store[x][y] = EL_EMPTY;
3545 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3546 center_element == EL_AMOEBA_TO_DIAMOND)
3547 Store2[x][y] = element;
3550 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3551 element_info[Store2[x][y]].token_name);
3555 if (AmoebaNr[x][y] &&
3556 (element == EL_AMOEBA_FULL ||
3557 element == EL_BD_AMOEBA ||
3558 element == EL_AMOEBA_GROWING))
3560 AmoebaCnt[AmoebaNr[x][y]]--;
3561 AmoebaCnt2[AmoebaNr[x][y]]--;
3567 MovDir[x][y] = MovPos[x][y] = 0;
3568 GfxDir[x][y] = MovDir[x][y];
3573 Feld[x][y] = EL_EXPLOSION;
3575 GfxElement[x][y] = center_element;
3577 GfxElement[x][y] = EL_UNDEFINED;
3580 ExplodePhase[x][y] = 1;
3582 ExplodeDelay[x][y] = last_phase;
3587 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3589 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3596 if (center_element == EL_YAMYAM)
3597 game.yamyam_content_nr =
3598 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3601 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3602 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3616 GfxFrame[x][y] = 0; /* restart explosion animation */
3620 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3624 last_phase = ExplodeDelay[x][y];
3627 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3631 /* activate this even in non-DEBUG version until cause for crash in
3632 getGraphicAnimationFrame() (see below) is found and eliminated */
3636 if (GfxElement[x][y] == EL_UNDEFINED)
3639 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3640 printf("Explode(): This should never happen!\n");
3643 GfxElement[x][y] = EL_EMPTY;
3649 border_element = Store2[x][y];
3651 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3652 border_element = StorePlayer[x][y];
3654 if (IS_PLAYER(x, y))
3655 border_element = StorePlayer[x][y];
3659 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3660 element_info[border_element].token_name, Store2[x][y]);
3664 printf("::: phase == %d\n", phase);
3667 if (phase == element_info[border_element].ignition_delay ||
3668 phase == last_phase)
3670 boolean border_explosion = FALSE;
3674 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3675 !PLAYER_EXPLOSION_PROTECTED(x, y))
3677 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3680 if (IS_PLAYER(x, y))
3683 KillHeroUnlessExplosionProtected(x, y);
3684 border_explosion = TRUE;
3687 if (phase == last_phase)
3688 printf("::: IS_PLAYER\n");
3691 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3694 printf("::: %d,%d: %d %s\n", x, y, border_element,
3695 element_info[border_element].token_name);
3698 Feld[x][y] = Store2[x][y];
3701 border_explosion = TRUE;
3704 if (phase == last_phase)
3705 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3708 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3710 AmoebeUmwandeln(x, y);
3712 border_explosion = TRUE;
3715 if (phase == last_phase)
3716 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3717 element_info[border_element].explosion_delay,
3718 element_info[border_element].ignition_delay,
3724 /* if an element just explodes due to another explosion (chain-reaction),
3725 do not immediately end the new explosion when it was the last frame of
3726 the explosion (as it would be done in the following "if"-statement!) */
3727 if (border_explosion && phase == last_phase)
3734 if (phase == first_phase_after_start)
3736 int element = Store2[x][y];
3738 if (element == EL_BLACK_ORB)
3740 Feld[x][y] = Store2[x][y];
3745 else if (phase == half_phase)
3747 int element = Store2[x][y];
3749 if (IS_PLAYER(x, y))
3750 KillHeroUnlessExplosionProtected(x, y);
3751 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3753 Feld[x][y] = Store2[x][y];
3757 else if (element == EL_AMOEBA_TO_DIAMOND)
3758 AmoebeUmwandeln(x, y);
3762 if (phase == last_phase)
3767 printf("::: done: phase == %d\n", phase);
3771 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3774 element = Feld[x][y] = Store[x][y];
3775 Store[x][y] = Store2[x][y] = 0;
3776 GfxElement[x][y] = EL_UNDEFINED;
3778 /* player can escape from explosions and might therefore be still alive */
3779 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3780 element <= EL_PLAYER_IS_EXPLODING_4)
3781 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3783 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3784 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3785 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3788 /* restore probably existing indestructible background element */
3789 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3790 element = Feld[x][y] = Back[x][y];
3793 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3794 GfxDir[x][y] = MV_NO_MOVING;
3795 ChangeDelay[x][y] = 0;
3796 ChangePage[x][y] = -1;
3799 InitField_WithBug2(x, y, FALSE);
3801 InitField(x, y, FALSE);
3803 /* !!! not needed !!! */
3805 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3806 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3809 if (CAN_MOVE(element))
3814 DrawLevelField(x, y);
3816 TestIfElementTouchesCustomElement(x, y);
3818 if (GFX_CRUMBLED(element))
3819 DrawLevelFieldCrumbledSandNeighbours(x, y);
3821 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3822 StorePlayer[x][y] = 0;
3824 if (ELEM_IS_PLAYER(element))
3825 RelocatePlayer(x, y, element);
3828 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3830 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3834 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3836 int stored = Store[x][y];
3837 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3838 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3842 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3844 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3848 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3852 printf("::: %d / %d [%d - %d]\n",
3853 GfxFrame[x][y], phase - delay, phase, delay);
3857 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3858 element_info[GfxElement[x][y]].token_name,
3863 DrawLevelFieldCrumbledSand(x, y);
3865 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3867 DrawLevelElement(x, y, Back[x][y]);
3868 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3870 else if (IS_WALKABLE_UNDER(Back[x][y]))
3872 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3873 DrawLevelElementThruMask(x, y, Back[x][y]);
3875 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3876 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3880 void DynaExplode(int ex, int ey)
3883 int dynabomb_element = Feld[ex][ey];
3884 int dynabomb_size = 1;
3885 boolean dynabomb_xl = FALSE;
3886 struct PlayerInfo *player;
3887 static int xy[4][2] =
3895 if (IS_ACTIVE_BOMB(dynabomb_element))
3897 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3898 dynabomb_size = player->dynabomb_size;
3899 dynabomb_xl = player->dynabomb_xl;
3900 player->dynabombs_left++;
3903 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3905 for (i = 0; i < NUM_DIRECTIONS; i++)
3907 for (j = 1; j <= dynabomb_size; j++)
3909 int x = ex + j * xy[i][0];
3910 int y = ey + j * xy[i][1];
3913 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3916 element = Feld[x][y];
3918 /* do not restart explosions of fields with active bombs */
3919 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3922 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3926 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3927 !IS_DIGGABLE(element) && !dynabomb_xl)
3930 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3931 !CAN_GROW_INTO(element) && !dynabomb_xl)
3935 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3936 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3937 element != EL_SAND && !dynabomb_xl)
3944 void Bang(int x, int y)
3947 int element = MovingOrBlocked2Element(x, y);
3949 int element = Feld[x][y];
3953 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3955 if (IS_PLAYER(x, y))
3958 struct PlayerInfo *player = PLAYERINFO(x, y);
3960 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3961 player->element_nr);
3966 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3968 if (game.emulation == EMU_SUPAPLEX)
3969 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3971 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3976 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3984 case EL_BD_BUTTERFLY:
3987 case EL_DARK_YAMYAM:
3991 RaiseScoreElement(element);
3992 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3994 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3995 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3996 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3997 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3998 case EL_DYNABOMB_INCREASE_NUMBER:
3999 case EL_DYNABOMB_INCREASE_SIZE:
4000 case EL_DYNABOMB_INCREASE_POWER:
4005 case EL_LAMP_ACTIVE:
4007 case EL_AMOEBA_TO_DIAMOND:
4009 if (IS_PLAYER(x, y))
4010 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4012 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4016 if (element_info[element].explosion_type == EXPLODES_CROSS)
4018 if (CAN_EXPLODE_CROSS(element))
4021 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4026 else if (element_info[element].explosion_type == EXPLODES_1X1)
4028 else if (CAN_EXPLODE_1X1(element))
4030 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4032 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4036 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4039 void SplashAcid(int x, int y)
4042 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4043 (!IN_LEV_FIELD(x - 1, y - 2) ||
4044 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4045 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4047 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4048 (!IN_LEV_FIELD(x + 1, y - 2) ||
4049 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4050 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4052 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4054 /* input: position of element entering acid (obsolete) */
4056 int element = Feld[x][y];
4058 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4061 if (element != EL_ACID_SPLASH_LEFT &&
4062 element != EL_ACID_SPLASH_RIGHT)
4064 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4066 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4067 (!IN_LEV_FIELD(x - 1, y - 1) ||
4068 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4069 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4071 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4072 (!IN_LEV_FIELD(x + 1, y - 1) ||
4073 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4074 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4079 static void InitBeltMovement()
4081 static int belt_base_element[4] =
4083 EL_CONVEYOR_BELT_1_LEFT,
4084 EL_CONVEYOR_BELT_2_LEFT,
4085 EL_CONVEYOR_BELT_3_LEFT,
4086 EL_CONVEYOR_BELT_4_LEFT
4088 static int belt_base_active_element[4] =
4090 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4091 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4092 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4093 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4098 /* set frame order for belt animation graphic according to belt direction */
4099 for (i = 0; i < NUM_BELTS; i++)
4103 for (j = 0; j < NUM_BELT_PARTS; j++)
4105 int element = belt_base_active_element[belt_nr] + j;
4106 int graphic = el2img(element);
4108 if (game.belt_dir[i] == MV_LEFT)
4109 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4111 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4115 for (y = 0; y < lev_fieldy; y++)
4117 for (x = 0; x < lev_fieldx; x++)
4119 int element = Feld[x][y];
4121 for (i = 0; i < NUM_BELTS; i++)
4123 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4125 int e_belt_nr = getBeltNrFromBeltElement(element);
4128 if (e_belt_nr == belt_nr)
4130 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4132 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4140 static void ToggleBeltSwitch(int x, int y)
4142 static int belt_base_element[4] =
4144 EL_CONVEYOR_BELT_1_LEFT,
4145 EL_CONVEYOR_BELT_2_LEFT,
4146 EL_CONVEYOR_BELT_3_LEFT,
4147 EL_CONVEYOR_BELT_4_LEFT
4149 static int belt_base_active_element[4] =
4151 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4152 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4153 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4154 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4156 static int belt_base_switch_element[4] =
4158 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4159 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4160 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4161 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4163 static int belt_move_dir[4] =
4171 int element = Feld[x][y];
4172 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4173 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4174 int belt_dir = belt_move_dir[belt_dir_nr];
4177 if (!IS_BELT_SWITCH(element))
4180 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4181 game.belt_dir[belt_nr] = belt_dir;
4183 if (belt_dir_nr == 3)
4186 /* set frame order for belt animation graphic according to belt direction */
4187 for (i = 0; i < NUM_BELT_PARTS; i++)
4189 int element = belt_base_active_element[belt_nr] + i;
4190 int graphic = el2img(element);
4192 if (belt_dir == MV_LEFT)
4193 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4195 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4198 for (yy = 0; yy < lev_fieldy; yy++)
4200 for (xx = 0; xx < lev_fieldx; xx++)
4202 int element = Feld[xx][yy];
4204 if (IS_BELT_SWITCH(element))
4206 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4208 if (e_belt_nr == belt_nr)
4210 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4211 DrawLevelField(xx, yy);
4214 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4216 int e_belt_nr = getBeltNrFromBeltElement(element);
4218 if (e_belt_nr == belt_nr)
4220 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4222 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4223 DrawLevelField(xx, yy);
4226 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4228 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4230 if (e_belt_nr == belt_nr)
4232 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4234 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4235 DrawLevelField(xx, yy);
4242 static void ToggleSwitchgateSwitch(int x, int y)
4246 game.switchgate_pos = !game.switchgate_pos;
4248 for (yy = 0; yy < lev_fieldy; yy++)
4250 for (xx = 0; xx < lev_fieldx; xx++)
4252 int element = Feld[xx][yy];
4254 if (element == EL_SWITCHGATE_SWITCH_UP ||
4255 element == EL_SWITCHGATE_SWITCH_DOWN)
4257 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4258 DrawLevelField(xx, yy);
4260 else if (element == EL_SWITCHGATE_OPEN ||
4261 element == EL_SWITCHGATE_OPENING)
4263 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4265 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4267 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4270 else if (element == EL_SWITCHGATE_CLOSED ||
4271 element == EL_SWITCHGATE_CLOSING)
4273 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4275 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4277 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4284 static int getInvisibleActiveFromInvisibleElement(int element)
4286 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4287 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4288 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4292 static int getInvisibleFromInvisibleActiveElement(int element)
4294 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4295 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4296 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4300 static void RedrawAllLightSwitchesAndInvisibleElements()
4304 for (y = 0; y < lev_fieldy; y++)
4306 for (x = 0; x < lev_fieldx; x++)
4308 int element = Feld[x][y];
4310 if (element == EL_LIGHT_SWITCH &&
4311 game.light_time_left > 0)
4313 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4314 DrawLevelField(x, y);
4316 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4317 game.light_time_left == 0)
4319 Feld[x][y] = EL_LIGHT_SWITCH;
4320 DrawLevelField(x, y);
4322 else if (element == EL_INVISIBLE_STEELWALL ||
4323 element == EL_INVISIBLE_WALL ||
4324 element == EL_INVISIBLE_SAND)
4326 if (game.light_time_left > 0)
4327 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4329 DrawLevelField(x, y);
4331 /* uncrumble neighbour fields, if needed */
4332 if (element == EL_INVISIBLE_SAND)
4333 DrawLevelFieldCrumbledSandNeighbours(x, y);
4335 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4336 element == EL_INVISIBLE_WALL_ACTIVE ||
4337 element == EL_INVISIBLE_SAND_ACTIVE)
4339 if (game.light_time_left == 0)
4340 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4342 DrawLevelField(x, y);
4344 /* re-crumble neighbour fields, if needed */
4345 if (element == EL_INVISIBLE_SAND)
4346 DrawLevelFieldCrumbledSandNeighbours(x, y);
4352 static void ToggleLightSwitch(int x, int y)
4354 int element = Feld[x][y];
4356 game.light_time_left =
4357 (element == EL_LIGHT_SWITCH ?
4358 level.time_light * FRAMES_PER_SECOND : 0);
4360 RedrawAllLightSwitchesAndInvisibleElements();
4363 static void ActivateTimegateSwitch(int x, int y)
4367 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4369 for (yy = 0; yy < lev_fieldy; yy++)
4371 for (xx = 0; xx < lev_fieldx; xx++)
4373 int element = Feld[xx][yy];
4375 if (element == EL_TIMEGATE_CLOSED ||
4376 element == EL_TIMEGATE_CLOSING)
4378 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4379 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4383 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4385 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4386 DrawLevelField(xx, yy);
4393 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4396 void Impact(int x, int y)
4398 boolean lastline = (y == lev_fieldy-1);
4399 boolean object_hit = FALSE;
4400 boolean impact = (lastline || object_hit);
4401 int element = Feld[x][y];
4402 int smashed = EL_STEELWALL;
4405 printf("IMPACT!\n");
4408 if (!lastline) /* check if element below was hit */
4410 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4413 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4414 MovDir[x][y + 1] != MV_DOWN ||
4415 MovPos[x][y + 1] <= TILEY / 2));
4418 object_hit = !IS_FREE(x, y + 1);
4421 /* do not smash moving elements that left the smashed field in time */
4422 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4423 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4427 smashed = MovingOrBlocked2Element(x, y + 1);
4429 impact = (lastline || object_hit);
4432 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4434 SplashAcid(x, y + 1);
4438 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4439 /* only reset graphic animation if graphic really changes after impact */
4441 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4443 ResetGfxAnimation(x, y);
4444 DrawLevelField(x, y);
4447 if (impact && CAN_EXPLODE_IMPACT(element))
4452 else if (impact && element == EL_PEARL)
4454 ResetGfxAnimation(x, y);
4456 Feld[x][y] = EL_PEARL_BREAKING;
4457 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4460 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4462 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4467 if (impact && element == EL_AMOEBA_DROP)
4469 if (object_hit && IS_PLAYER(x, y + 1))
4470 KillHeroUnlessEnemyProtected(x, y + 1);
4471 else if (object_hit && smashed == EL_PENGUIN)
4475 Feld[x][y] = EL_AMOEBA_GROWING;
4476 Store[x][y] = EL_AMOEBA_WET;
4478 ResetRandomAnimationValue(x, y);
4483 if (object_hit) /* check which object was hit */
4485 if (CAN_PASS_MAGIC_WALL(element) &&
4486 (smashed == EL_MAGIC_WALL ||
4487 smashed == EL_BD_MAGIC_WALL))
4490 int activated_magic_wall =
4491 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4492 EL_BD_MAGIC_WALL_ACTIVE);
4494 /* activate magic wall / mill */
4495 for (yy = 0; yy < lev_fieldy; yy++)
4496 for (xx = 0; xx < lev_fieldx; xx++)
4497 if (Feld[xx][yy] == smashed)
4498 Feld[xx][yy] = activated_magic_wall;
4500 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4501 game.magic_wall_active = TRUE;
4503 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4504 SND_MAGIC_WALL_ACTIVATING :
4505 SND_BD_MAGIC_WALL_ACTIVATING));
4508 if (IS_PLAYER(x, y + 1))
4510 if (CAN_SMASH_PLAYER(element))
4512 KillHeroUnlessEnemyProtected(x, y + 1);
4516 else if (smashed == EL_PENGUIN)
4518 if (CAN_SMASH_PLAYER(element))
4524 else if (element == EL_BD_DIAMOND)
4526 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4532 else if (((element == EL_SP_INFOTRON ||
4533 element == EL_SP_ZONK) &&
4534 (smashed == EL_SP_SNIKSNAK ||
4535 smashed == EL_SP_ELECTRON ||
4536 smashed == EL_SP_DISK_ORANGE)) ||
4537 (element == EL_SP_INFOTRON &&
4538 smashed == EL_SP_DISK_YELLOW))
4544 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4550 else if (CAN_SMASH_EVERYTHING(element))
4552 if (IS_CLASSIC_ENEMY(smashed) ||
4553 CAN_EXPLODE_SMASHED(smashed))
4558 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4560 if (smashed == EL_LAMP ||
4561 smashed == EL_LAMP_ACTIVE)
4566 else if (smashed == EL_NUT)
4568 Feld[x][y + 1] = EL_NUT_BREAKING;
4569 PlayLevelSound(x, y, SND_NUT_BREAKING);
4570 RaiseScoreElement(EL_NUT);
4573 else if (smashed == EL_PEARL)
4575 ResetGfxAnimation(x, y);
4577 Feld[x][y + 1] = EL_PEARL_BREAKING;
4578 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4581 else if (smashed == EL_DIAMOND)
4583 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4584 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4587 else if (IS_BELT_SWITCH(smashed))
4589 ToggleBeltSwitch(x, y + 1);
4591 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4592 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4594 ToggleSwitchgateSwitch(x, y + 1);
4596 else if (smashed == EL_LIGHT_SWITCH ||
4597 smashed == EL_LIGHT_SWITCH_ACTIVE)
4599 ToggleLightSwitch(x, y + 1);
4604 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4607 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4610 /* !!! TEST ONLY !!! */
4611 CheckElementChangeBySide(x, y + 1, smashed, element,
4612 CE_SWITCHED, CH_SIDE_TOP);
4613 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4614 CE_SWITCH_OF_X, CH_SIDE_TOP);
4616 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4617 CE_SWITCH_OF_X, CH_SIDE_TOP);
4618 CheckElementChangeBySide(x, y + 1, smashed, element,
4619 CE_SWITCHED, CH_SIDE_TOP);
4625 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4630 /* play sound of magic wall / mill */
4632 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4633 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4635 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4636 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4637 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4638 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4643 /* play sound of object that hits the ground */
4644 if (lastline || object_hit)
4645 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4648 inline static void TurnRoundExt(int x, int y)
4660 { 0, 0 }, { 0, 0 }, { 0, 0 },
4665 int left, right, back;
4669 { MV_DOWN, MV_UP, MV_RIGHT },
4670 { MV_UP, MV_DOWN, MV_LEFT },
4672 { MV_LEFT, MV_RIGHT, MV_DOWN },
4676 { MV_RIGHT, MV_LEFT, MV_UP }
4679 int element = Feld[x][y];
4680 int move_pattern = element_info[element].move_pattern;
4682 int old_move_dir = MovDir[x][y];
4683 int left_dir = turn[old_move_dir].left;
4684 int right_dir = turn[old_move_dir].right;
4685 int back_dir = turn[old_move_dir].back;
4687 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4688 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4689 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4690 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4692 int left_x = x + left_dx, left_y = y + left_dy;
4693 int right_x = x + right_dx, right_y = y + right_dy;
4694 int move_x = x + move_dx, move_y = y + move_dy;
4698 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4700 TestIfBadThingTouchesOtherBadThing(x, y);
4702 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4703 MovDir[x][y] = right_dir;
4704 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4705 MovDir[x][y] = left_dir;
4707 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4709 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4713 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4714 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4716 TestIfBadThingTouchesOtherBadThing(x, y);
4718 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4719 MovDir[x][y] = left_dir;
4720 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4721 MovDir[x][y] = right_dir;
4723 if ((element == EL_SPACESHIP ||
4724 element == EL_SP_SNIKSNAK ||
4725 element == EL_SP_ELECTRON)
4726 && MovDir[x][y] != old_move_dir)
4728 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4732 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4734 TestIfBadThingTouchesOtherBadThing(x, y);
4736 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4737 MovDir[x][y] = left_dir;
4738 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4739 MovDir[x][y] = right_dir;
4741 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4743 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4746 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4748 TestIfBadThingTouchesOtherBadThing(x, y);
4750 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4751 MovDir[x][y] = left_dir;
4752 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4753 MovDir[x][y] = right_dir;
4755 if (MovDir[x][y] != old_move_dir)
4759 else if (element == EL_YAMYAM)
4761 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4762 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4764 if (can_turn_left && can_turn_right)
4765 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4766 else if (can_turn_left)
4767 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4768 else if (can_turn_right)
4769 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4771 MovDir[x][y] = back_dir;
4773 MovDelay[x][y] = 16 + 16 * RND(3);
4775 else if (element == EL_DARK_YAMYAM)
4777 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4779 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4782 if (can_turn_left && can_turn_right)
4783 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4784 else if (can_turn_left)
4785 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4786 else if (can_turn_right)
4787 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4789 MovDir[x][y] = back_dir;
4791 MovDelay[x][y] = 16 + 16 * RND(3);
4793 else if (element == EL_PACMAN)
4795 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4796 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4798 if (can_turn_left && can_turn_right)
4799 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4800 else if (can_turn_left)
4801 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4802 else if (can_turn_right)
4803 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4805 MovDir[x][y] = back_dir;
4807 MovDelay[x][y] = 6 + RND(40);
4809 else if (element == EL_PIG)
4811 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4812 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4813 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4814 boolean should_turn_left, should_turn_right, should_move_on;
4816 int rnd = RND(rnd_value);
4818 should_turn_left = (can_turn_left &&
4820 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4821 y + back_dy + left_dy)));
4822 should_turn_right = (can_turn_right &&
4824 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4825 y + back_dy + right_dy)));
4826 should_move_on = (can_move_on &&
4829 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4830 y + move_dy + left_dy) ||
4831 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4832 y + move_dy + right_dy)));
4834 if (should_turn_left || should_turn_right || should_move_on)
4836 if (should_turn_left && should_turn_right && should_move_on)
4837 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4838 rnd < 2 * rnd_value / 3 ? right_dir :
4840 else if (should_turn_left && should_turn_right)
4841 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4842 else if (should_turn_left && should_move_on)
4843 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4844 else if (should_turn_right && should_move_on)
4845 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4846 else if (should_turn_left)
4847 MovDir[x][y] = left_dir;
4848 else if (should_turn_right)
4849 MovDir[x][y] = right_dir;
4850 else if (should_move_on)
4851 MovDir[x][y] = old_move_dir;
4853 else if (can_move_on && rnd > rnd_value / 8)
4854 MovDir[x][y] = old_move_dir;
4855 else if (can_turn_left && can_turn_right)
4856 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4857 else if (can_turn_left && rnd > rnd_value / 8)
4858 MovDir[x][y] = left_dir;
4859 else if (can_turn_right && rnd > rnd_value/8)
4860 MovDir[x][y] = right_dir;
4862 MovDir[x][y] = back_dir;
4864 xx = x + move_xy[MovDir[x][y]].x;
4865 yy = y + move_xy[MovDir[x][y]].y;
4868 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4869 if (!IN_LEV_FIELD(xx, yy) ||
4870 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4871 MovDir[x][y] = old_move_dir;
4873 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4874 MovDir[x][y] = old_move_dir;
4879 else if (element == EL_DRAGON)
4881 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4882 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4883 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4885 int rnd = RND(rnd_value);
4888 if (FrameCounter < 1 && x == 0 && y == 29)
4889 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4892 if (can_move_on && rnd > rnd_value / 8)
4893 MovDir[x][y] = old_move_dir;
4894 else if (can_turn_left && can_turn_right)
4895 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4896 else if (can_turn_left && rnd > rnd_value / 8)
4897 MovDir[x][y] = left_dir;
4898 else if (can_turn_right && rnd > rnd_value / 8)
4899 MovDir[x][y] = right_dir;
4901 MovDir[x][y] = back_dir;
4903 xx = x + move_xy[MovDir[x][y]].x;
4904 yy = y + move_xy[MovDir[x][y]].y;
4907 if (FrameCounter < 1 && x == 0 && y == 29)
4908 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4909 xx, yy, Feld[xx][yy],
4914 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4915 MovDir[x][y] = old_move_dir;
4917 if (!IS_FREE(xx, yy))
4918 MovDir[x][y] = old_move_dir;
4922 if (FrameCounter < 1 && x == 0 && y == 29)
4923 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4928 else if (element == EL_MOLE)
4930 boolean can_move_on =
4931 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4932 IS_AMOEBOID(Feld[move_x][move_y]) ||
4933 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4936 boolean can_turn_left =
4937 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4938 IS_AMOEBOID(Feld[left_x][left_y])));
4940 boolean can_turn_right =
4941 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4942 IS_AMOEBOID(Feld[right_x][right_y])));
4944 if (can_turn_left && can_turn_right)
4945 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4946 else if (can_turn_left)
4947 MovDir[x][y] = left_dir;
4949 MovDir[x][y] = right_dir;
4952 if (MovDir[x][y] != old_move_dir)
4955 else if (element == EL_BALLOON)
4957 MovDir[x][y] = game.balloon_dir;
4960 else if (element == EL_SPRING)
4963 if (MovDir[x][y] & MV_HORIZONTAL &&
4964 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4965 MovDir[x][y] = MV_NO_MOVING;
4967 if (MovDir[x][y] & MV_HORIZONTAL &&
4968 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4969 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4970 MovDir[x][y] = MV_NO_MOVING;
4975 else if (element == EL_ROBOT ||
4976 element == EL_SATELLITE ||
4977 element == EL_PENGUIN)
4979 int attr_x = -1, attr_y = -1;
4990 for (i = 0; i < MAX_PLAYERS; i++)
4992 struct PlayerInfo *player = &stored_player[i];
4993 int jx = player->jx, jy = player->jy;
4995 if (!player->active)
4999 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5008 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5009 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5010 game.engine_version < VERSION_IDENT(3,1,0,0)))
5012 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5019 if (element == EL_PENGUIN)
5022 static int xy[4][2] =
5030 for (i = 0; i < NUM_DIRECTIONS; i++)
5032 int ex = x + xy[i][0];
5033 int ey = y + xy[i][1];
5035 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5044 MovDir[x][y] = MV_NO_MOVING;
5046 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5047 else if (attr_x > x)
5048 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5050 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5051 else if (attr_y > y)
5052 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5054 if (element == EL_ROBOT)
5058 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5059 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5060 Moving2Blocked(x, y, &newx, &newy);
5062 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5063 MovDelay[x][y] = 8 + 8 * !RND(3);
5065 MovDelay[x][y] = 16;
5067 else if (element == EL_PENGUIN)
5073 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5075 boolean first_horiz = RND(2);
5076 int new_move_dir = MovDir[x][y];
5079 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5080 Moving2Blocked(x, y, &newx, &newy);
5082 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5086 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5087 Moving2Blocked(x, y, &newx, &newy);
5089 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5092 MovDir[x][y] = old_move_dir;
5096 else /* (element == EL_SATELLITE) */
5102 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5104 boolean first_horiz = RND(2);
5105 int new_move_dir = MovDir[x][y];
5108 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5109 Moving2Blocked(x, y, &newx, &newy);
5111 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5115 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5116 Moving2Blocked(x, y, &newx, &newy);
5118 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5121 MovDir[x][y] = old_move_dir;
5126 else if (move_pattern == MV_TURNING_LEFT ||
5127 move_pattern == MV_TURNING_RIGHT ||
5128 move_pattern == MV_TURNING_LEFT_RIGHT ||
5129 move_pattern == MV_TURNING_RIGHT_LEFT ||
5130 move_pattern == MV_TURNING_RANDOM ||
5131 move_pattern == MV_ALL_DIRECTIONS)
5133 boolean can_turn_left =
5134 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5135 boolean can_turn_right =
5136 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5138 #if USE_CAN_MOVE_NOT_MOVING
5139 if (element_info[element].move_stepsize == 0) /* not moving */
5143 if (move_pattern == MV_TURNING_LEFT)
5144 MovDir[x][y] = left_dir;
5145 else if (move_pattern == MV_TURNING_RIGHT)
5146 MovDir[x][y] = right_dir;
5147 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5148 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5149 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5150 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5151 else if (move_pattern == MV_TURNING_RANDOM)
5152 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5153 can_turn_right && !can_turn_left ? right_dir :
5154 RND(2) ? left_dir : right_dir);
5155 else if (can_turn_left && can_turn_right)
5156 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5157 else if (can_turn_left)
5158 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5159 else if (can_turn_right)
5160 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5162 MovDir[x][y] = back_dir;
5164 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5166 else if (move_pattern == MV_HORIZONTAL ||
5167 move_pattern == MV_VERTICAL)
5169 if (move_pattern & old_move_dir)
5170 MovDir[x][y] = back_dir;
5171 else if (move_pattern == MV_HORIZONTAL)
5172 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5173 else if (move_pattern == MV_VERTICAL)
5174 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5176 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5178 else if (move_pattern & MV_ANY_DIRECTION)
5180 MovDir[x][y] = move_pattern;
5181 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5183 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5185 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5186 MovDir[x][y] = left_dir;
5187 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5188 MovDir[x][y] = right_dir;
5190 if (MovDir[x][y] != old_move_dir)
5191 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5193 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5195 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5196 MovDir[x][y] = right_dir;
5197 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5198 MovDir[x][y] = left_dir;
5200 if (MovDir[x][y] != old_move_dir)
5201 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5203 else if (move_pattern == MV_TOWARDS_PLAYER ||
5204 move_pattern == MV_AWAY_FROM_PLAYER)
5206 int attr_x = -1, attr_y = -1;
5208 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5219 for (i = 0; i < MAX_PLAYERS; i++)
5221 struct PlayerInfo *player = &stored_player[i];
5222 int jx = player->jx, jy = player->jy;
5224 if (!player->active)
5228 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5236 MovDir[x][y] = MV_NO_MOVING;
5238 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5239 else if (attr_x > x)
5240 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5242 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5243 else if (attr_y > y)
5244 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5246 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5248 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5250 boolean first_horiz = RND(2);
5251 int new_move_dir = MovDir[x][y];
5253 #if USE_CAN_MOVE_NOT_MOVING
5254 if (element_info[element].move_stepsize == 0) /* not moving */
5256 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5257 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5264 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5265 Moving2Blocked(x, y, &newx, &newy);
5267 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5271 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5272 Moving2Blocked(x, y, &newx, &newy);
5274 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5277 MovDir[x][y] = old_move_dir;
5280 else if (move_pattern == MV_WHEN_PUSHED ||
5281 move_pattern == MV_WHEN_DROPPED)
5283 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5284 MovDir[x][y] = MV_NO_MOVING;
5288 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5290 static int test_xy[7][2] =
5300 static int test_dir[7] =
5310 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5311 int move_preference = -1000000; /* start with very low preference */
5312 int new_move_dir = MV_NO_MOVING;
5313 int start_test = RND(4);
5316 for (i = 0; i < NUM_DIRECTIONS; i++)
5318 int move_dir = test_dir[start_test + i];
5319 int move_dir_preference;
5321 xx = x + test_xy[start_test + i][0];
5322 yy = y + test_xy[start_test + i][1];
5324 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5325 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5327 new_move_dir = move_dir;
5332 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5335 move_dir_preference = -1 * RunnerVisit[xx][yy];
5336 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5337 move_dir_preference = PlayerVisit[xx][yy];
5339 if (move_dir_preference > move_preference)
5341 /* prefer field that has not been visited for the longest time */
5342 move_preference = move_dir_preference;
5343 new_move_dir = move_dir;
5345 else if (move_dir_preference == move_preference &&
5346 move_dir == old_move_dir)
5348 /* prefer last direction when all directions are preferred equally */
5349 move_preference = move_dir_preference;
5350 new_move_dir = move_dir;
5354 MovDir[x][y] = new_move_dir;
5355 if (old_move_dir != new_move_dir)
5358 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5366 static void TurnRound(int x, int y)
5368 int direction = MovDir[x][y];
5371 GfxDir[x][y] = MovDir[x][y];
5377 GfxDir[x][y] = MovDir[x][y];
5380 if (direction != MovDir[x][y])
5385 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5388 GfxAction[x][y] = ACTION_WAITING;
5392 static boolean JustBeingPushed(int x, int y)
5396 for (i = 0; i < MAX_PLAYERS; i++)
5398 struct PlayerInfo *player = &stored_player[i];
5400 if (player->active && player->is_pushing && player->MovPos)
5402 int next_jx = player->jx + (player->jx - player->last_jx);
5403 int next_jy = player->jy + (player->jy - player->last_jy);
5405 if (x == next_jx && y == next_jy)
5413 void StartMoving(int x, int y)
5416 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5418 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5419 int element = Feld[x][y];
5425 if (MovDelay[x][y] == 0)
5426 GfxAction[x][y] = ACTION_DEFAULT;
5428 /* !!! this should be handled more generic (not only for mole) !!! */
5429 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5430 GfxAction[x][y] = ACTION_DEFAULT;
5433 if (CAN_FALL(element) && y < lev_fieldy - 1)
5435 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5436 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5437 if (JustBeingPushed(x, y))
5440 if (element == EL_QUICKSAND_FULL)
5442 if (IS_FREE(x, y + 1))
5444 InitMovingField(x, y, MV_DOWN);
5445 started_moving = TRUE;
5447 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5448 Store[x][y] = EL_ROCK;
5450 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5452 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5455 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5457 if (!MovDelay[x][y])
5458 MovDelay[x][y] = TILEY + 1;
5467 Feld[x][y] = EL_QUICKSAND_EMPTY;
5468 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5469 Store[x][y + 1] = Store[x][y];
5472 PlayLevelSoundAction(x, y, ACTION_FILLING);
5474 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5478 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5479 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5481 InitMovingField(x, y, MV_DOWN);
5482 started_moving = TRUE;
5484 Feld[x][y] = EL_QUICKSAND_FILLING;
5485 Store[x][y] = element;
5487 PlayLevelSoundAction(x, y, ACTION_FILLING);
5489 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5492 else if (element == EL_MAGIC_WALL_FULL)
5494 if (IS_FREE(x, y + 1))
5496 InitMovingField(x, y, MV_DOWN);
5497 started_moving = TRUE;
5499 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5500 Store[x][y] = EL_CHANGED(Store[x][y]);
5502 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5504 if (!MovDelay[x][y])
5505 MovDelay[x][y] = TILEY/4 + 1;
5514 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5515 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5516 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5520 else if (element == EL_BD_MAGIC_WALL_FULL)
5522 if (IS_FREE(x, y + 1))
5524 InitMovingField(x, y, MV_DOWN);
5525 started_moving = TRUE;
5527 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5528 Store[x][y] = EL_CHANGED2(Store[x][y]);
5530 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5532 if (!MovDelay[x][y])
5533 MovDelay[x][y] = TILEY/4 + 1;
5542 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5543 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5544 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5548 else if (CAN_PASS_MAGIC_WALL(element) &&
5549 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5550 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5552 InitMovingField(x, y, MV_DOWN);
5553 started_moving = TRUE;
5556 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5557 EL_BD_MAGIC_WALL_FILLING);
5558 Store[x][y] = element;
5561 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5563 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5566 SplashAcid(x, y + 1);
5568 InitMovingField(x, y, MV_DOWN);
5569 started_moving = TRUE;
5571 Store[x][y] = EL_ACID;
5573 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5574 GfxAction[x][y + 1] = ACTION_ACTIVE;
5578 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5579 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5581 #if USE_IMPACT_BUGFIX
5582 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5583 CAN_FALL(element) && WasJustFalling[x][y] &&
5584 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5586 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5587 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5588 (Feld[x][y + 1] == EL_BLOCKED)))
5590 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5591 CAN_SMASH(element) && WasJustFalling[x][y] &&
5592 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5594 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5595 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5596 (Feld[x][y + 1] == EL_BLOCKED)))
5601 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5602 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5603 WasJustMoving[x][y] && !Pushed[x][y + 1])
5605 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5606 WasJustMoving[x][y])
5611 /* this is needed for a special case not covered by calling "Impact()"
5612 from "ContinueMoving()": if an element moves to a tile directly below
5613 another element which was just falling on that tile (which was empty
5614 in the previous frame), the falling element above would just stop
5615 instead of smashing the element below (in previous version, the above
5616 element was just checked for "moving" instead of "falling", resulting
5617 in incorrect smashes caused by horizontal movement of the above
5618 element; also, the case of the player being the element to smash was
5619 simply not covered here... :-/ ) */
5622 WasJustMoving[x][y] = 0;
5623 WasJustFalling[x][y] = 0;
5626 CheckCollision[x][y] = 0;
5629 if (IS_PLAYER(x, y + 1))
5630 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5635 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5637 if (MovDir[x][y] == MV_NO_MOVING)
5639 InitMovingField(x, y, MV_DOWN);
5640 started_moving = TRUE;
5643 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5645 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5646 MovDir[x][y] = MV_DOWN;
5648 InitMovingField(x, y, MV_DOWN);
5649 started_moving = TRUE;
5651 else if (element == EL_AMOEBA_DROP)
5653 Feld[x][y] = EL_AMOEBA_GROWING;
5654 Store[x][y] = EL_AMOEBA_WET;
5656 /* Store[x][y + 1] must be zero, because:
5657 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5660 #if OLD_GAME_BEHAVIOUR
5661 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5663 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5664 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5665 element != EL_DX_SUPABOMB)
5668 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5669 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5670 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5671 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5674 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5675 (IS_FREE(x - 1, y + 1) ||
5676 Feld[x - 1][y + 1] == EL_ACID));
5677 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5678 (IS_FREE(x + 1, y + 1) ||
5679 Feld[x + 1][y + 1] == EL_ACID));
5680 boolean can_fall_any = (can_fall_left || can_fall_right);
5681 boolean can_fall_both = (can_fall_left && can_fall_right);
5683 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5685 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5687 if (slippery_type == SLIPPERY_ONLY_LEFT)
5688 can_fall_right = FALSE;
5689 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5690 can_fall_left = FALSE;
5691 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5692 can_fall_right = FALSE;
5693 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5694 can_fall_left = FALSE;
5696 can_fall_any = (can_fall_left || can_fall_right);
5697 can_fall_both = (can_fall_left && can_fall_right);
5700 #if USE_NEW_SP_SLIPPERY
5701 /* !!! better use the same properties as for custom elements here !!! */
5702 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5703 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5705 can_fall_right = FALSE; /* slip down on left side */
5706 can_fall_both = FALSE;
5713 if (game.emulation == EMU_BOULDERDASH ||
5714 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5715 can_fall_right = FALSE; /* slip down on left side */
5717 can_fall_left = !(can_fall_right = RND(2));
5719 can_fall_both = FALSE;
5726 if (can_fall_both &&
5727 (game.emulation != EMU_BOULDERDASH &&
5728 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5729 can_fall_left = !(can_fall_right = RND(2));
5732 /* if not determined otherwise, prefer left side for slipping down */
5733 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5734 started_moving = TRUE;
5738 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5740 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5743 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5744 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5745 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5746 int belt_dir = game.belt_dir[belt_nr];
5748 if ((belt_dir == MV_LEFT && left_is_free) ||
5749 (belt_dir == MV_RIGHT && right_is_free))
5752 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5755 InitMovingField(x, y, belt_dir);
5756 started_moving = TRUE;
5759 Pushed[x][y] = TRUE;
5760 Pushed[nextx][y] = TRUE;
5763 GfxAction[x][y] = ACTION_DEFAULT;
5767 MovDir[x][y] = 0; /* if element was moving, stop it */
5772 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5774 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5776 if (CAN_MOVE(element) && !started_moving)
5779 int move_pattern = element_info[element].move_pattern;
5784 if (MovDir[x][y] == MV_NO_MOVING)
5786 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5787 x, y, element, element_info[element].token_name);
5788 printf("StartMoving(): This should never happen!\n");
5793 Moving2Blocked(x, y, &newx, &newy);
5796 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5799 if ((element == EL_SATELLITE ||
5800 element == EL_BALLOON ||
5801 element == EL_SPRING)
5802 && JustBeingPushed(x, y))
5809 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5810 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5812 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5813 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5814 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5818 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5819 element, element_info[element].token_name,
5820 WasJustMoving[x][y],
5821 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5822 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5823 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5824 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5828 WasJustMoving[x][y] = 0;
5831 CheckCollision[x][y] = 0;
5833 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5836 if (Feld[x][y] != element) /* element has changed */
5838 element = Feld[x][y];
5839 move_pattern = element_info[element].move_pattern;
5841 if (!CAN_MOVE(element))
5845 if (Feld[x][y] != element) /* element has changed */
5853 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5854 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5856 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5858 Moving2Blocked(x, y, &newx, &newy);
5859 if (Feld[newx][newy] == EL_BLOCKED)
5860 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5866 if (FrameCounter < 1 && x == 0 && y == 29)
5867 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5870 if (!MovDelay[x][y]) /* start new movement phase */
5872 /* all objects that can change their move direction after each step
5873 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5875 if (element != EL_YAMYAM &&
5876 element != EL_DARK_YAMYAM &&
5877 element != EL_PACMAN &&
5878 !(move_pattern & MV_ANY_DIRECTION) &&
5879 move_pattern != MV_TURNING_LEFT &&
5880 move_pattern != MV_TURNING_RIGHT &&
5881 move_pattern != MV_TURNING_LEFT_RIGHT &&
5882 move_pattern != MV_TURNING_RIGHT_LEFT &&
5883 move_pattern != MV_TURNING_RANDOM)
5888 if (FrameCounter < 1 && x == 0 && y == 29)
5889 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5892 if (MovDelay[x][y] && (element == EL_BUG ||
5893 element == EL_SPACESHIP ||
5894 element == EL_SP_SNIKSNAK ||
5895 element == EL_SP_ELECTRON ||
5896 element == EL_MOLE))
5897 DrawLevelField(x, y);
5901 if (MovDelay[x][y]) /* wait some time before next movement */
5906 if (element == EL_YAMYAM)
5909 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5910 DrawLevelElementAnimation(x, y, element);
5914 if (MovDelay[x][y]) /* element still has to wait some time */
5917 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5918 ResetGfxAnimation(x, y);
5922 if (GfxAction[x][y] != ACTION_WAITING)
5923 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5925 GfxAction[x][y] = ACTION_WAITING;
5929 if (element == EL_ROBOT ||
5931 element == EL_PACMAN ||
5933 element == EL_YAMYAM ||
5934 element == EL_DARK_YAMYAM)
5937 DrawLevelElementAnimation(x, y, element);
5939 DrawLevelElementAnimationIfNeeded(x, y, element);
5941 PlayLevelSoundAction(x, y, ACTION_WAITING);
5943 else if (element == EL_SP_ELECTRON)
5944 DrawLevelElementAnimationIfNeeded(x, y, element);
5945 else if (element == EL_DRAGON)
5948 int dir = MovDir[x][y];
5949 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5950 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5951 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5952 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5953 dir == MV_UP ? IMG_FLAMES_1_UP :
5954 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5955 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5958 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5961 GfxAction[x][y] = ACTION_ATTACKING;
5963 if (IS_PLAYER(x, y))
5964 DrawPlayerField(x, y);
5966 DrawLevelField(x, y);
5968 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5970 for (i = 1; i <= 3; i++)
5972 int xx = x + i * dx;
5973 int yy = y + i * dy;
5974 int sx = SCREENX(xx);
5975 int sy = SCREENY(yy);
5976 int flame_graphic = graphic + (i - 1);
5978 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5983 int flamed = MovingOrBlocked2Element(xx, yy);
5987 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5989 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5990 RemoveMovingField(xx, yy);
5992 RemoveField(xx, yy);
5994 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5997 RemoveMovingField(xx, yy);
6001 if (ChangeDelay[xx][yy])
6002 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6003 Feld[xx][yy] == EL_BLOCKED));
6007 ChangeDelay[xx][yy] = 0;
6009 Feld[xx][yy] = EL_FLAMES;
6010 if (IN_SCR_FIELD(sx, sy))
6012 DrawLevelFieldCrumbledSand(xx, yy);
6013 DrawGraphic(sx, sy, flame_graphic, frame);
6018 if (Feld[xx][yy] == EL_FLAMES)
6019 Feld[xx][yy] = EL_EMPTY;
6020 DrawLevelField(xx, yy);
6025 if (MovDelay[x][y]) /* element still has to wait some time */
6027 PlayLevelSoundAction(x, y, ACTION_WAITING);
6033 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6034 for all other elements GfxAction will be set by InitMovingField() */
6035 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6036 GfxAction[x][y] = ACTION_MOVING;
6040 /* now make next step */
6042 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6044 if (DONT_COLLIDE_WITH(element) &&
6045 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6046 !PLAYER_ENEMY_PROTECTED(newx, newy))
6049 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6053 /* player killed by element which is deadly when colliding with */
6055 KillHero(PLAYERINFO(newx, newy));
6062 else if (CAN_MOVE_INTO_ACID(element) &&
6063 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6064 (MovDir[x][y] == MV_DOWN ||
6065 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6067 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6068 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6072 else if ((element == EL_PENGUIN ||
6073 element == EL_ROBOT ||
6074 element == EL_SATELLITE ||
6075 element == EL_BALLOON ||
6076 IS_CUSTOM_ELEMENT(element)) &&
6077 IN_LEV_FIELD(newx, newy) &&
6078 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6081 SplashAcid(newx, newy);
6082 Store[x][y] = EL_ACID;
6084 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6086 if (Feld[newx][newy] == EL_EXIT_OPEN)
6090 DrawLevelField(x, y);
6092 Feld[x][y] = EL_EMPTY;
6093 DrawLevelField(x, y);
6096 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6097 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6098 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6100 local_player->friends_still_needed--;
6101 if (!local_player->friends_still_needed &&
6102 !local_player->GameOver && AllPlayersGone)
6103 local_player->LevelSolved = local_player->GameOver = TRUE;
6107 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6109 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6110 DrawLevelField(newx, newy);
6112 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6114 else if (!IS_FREE(newx, newy))
6116 GfxAction[x][y] = ACTION_WAITING;
6118 if (IS_PLAYER(x, y))
6119 DrawPlayerField(x, y);
6121 DrawLevelField(x, y);
6126 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6128 if (IS_FOOD_PIG(Feld[newx][newy]))
6130 if (IS_MOVING(newx, newy))
6131 RemoveMovingField(newx, newy);
6134 Feld[newx][newy] = EL_EMPTY;
6135 DrawLevelField(newx, newy);
6138 PlayLevelSound(x, y, SND_PIG_DIGGING);
6140 else if (!IS_FREE(newx, newy))
6142 if (IS_PLAYER(x, y))
6143 DrawPlayerField(x, y);
6145 DrawLevelField(x, y);
6154 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6157 else if (IS_CUSTOM_ELEMENT(element) &&
6158 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6162 !IS_FREE(newx, newy)
6167 int new_element = Feld[newx][newy];
6170 printf("::: '%s' digs '%s' [%d]\n",
6171 element_info[element].token_name,
6172 element_info[Feld[newx][newy]].token_name,
6173 StorePlayer[newx][newy]);
6176 if (!IS_FREE(newx, newy))
6178 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6179 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6182 /* no element can dig solid indestructible elements */
6183 if (IS_INDESTRUCTIBLE(new_element) &&
6184 !IS_DIGGABLE(new_element) &&
6185 !IS_COLLECTIBLE(new_element))
6188 if (AmoebaNr[newx][newy] &&
6189 (new_element == EL_AMOEBA_FULL ||
6190 new_element == EL_BD_AMOEBA ||
6191 new_element == EL_AMOEBA_GROWING))
6193 AmoebaCnt[AmoebaNr[newx][newy]]--;
6194 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6197 if (IS_MOVING(newx, newy))
6198 RemoveMovingField(newx, newy);
6201 RemoveField(newx, newy);
6202 DrawLevelField(newx, newy);
6205 /* if digged element was about to explode, prevent the explosion */
6206 ExplodeField[newx][newy] = EX_TYPE_NONE;
6208 PlayLevelSoundAction(x, y, action);
6213 Store[newx][newy] = EL_EMPTY;
6214 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6215 Store[newx][newy] = element_info[element].move_leave_element;
6217 Store[newx][newy] = EL_EMPTY;
6218 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6219 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6220 Store[newx][newy] = element_info[element].move_leave_element;
6223 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6224 element_info[element].can_leave_element = TRUE;
6227 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6229 RunnerVisit[x][y] = FrameCounter;
6230 PlayerVisit[x][y] /= 8; /* expire player visit path */
6236 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6238 if (!IS_FREE(newx, newy))
6240 if (IS_PLAYER(x, y))
6241 DrawPlayerField(x, y);
6243 DrawLevelField(x, y);
6249 boolean wanna_flame = !RND(10);
6250 int dx = newx - x, dy = newy - y;
6251 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6252 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6253 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6254 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6255 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6256 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6259 IS_CLASSIC_ENEMY(element1) ||
6260 IS_CLASSIC_ENEMY(element2)) &&
6261 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6262 element1 != EL_FLAMES && element2 != EL_FLAMES)
6265 ResetGfxAnimation(x, y);
6266 GfxAction[x][y] = ACTION_ATTACKING;
6269 if (IS_PLAYER(x, y))
6270 DrawPlayerField(x, y);
6272 DrawLevelField(x, y);
6274 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6276 MovDelay[x][y] = 50;
6280 RemoveField(newx, newy);
6282 Feld[newx][newy] = EL_FLAMES;
6283 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6286 RemoveField(newx1, newy1);
6288 Feld[newx1][newy1] = EL_FLAMES;
6290 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6293 RemoveField(newx2, newy2);
6295 Feld[newx2][newy2] = EL_FLAMES;
6302 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6303 Feld[newx][newy] == EL_DIAMOND)
6305 if (IS_MOVING(newx, newy))
6306 RemoveMovingField(newx, newy);
6309 Feld[newx][newy] = EL_EMPTY;
6310 DrawLevelField(newx, newy);
6313 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6315 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6316 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6318 if (AmoebaNr[newx][newy])
6320 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6321 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6322 Feld[newx][newy] == EL_BD_AMOEBA)
6323 AmoebaCnt[AmoebaNr[newx][newy]]--;
6328 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6330 if (IS_MOVING(newx, newy))
6333 RemoveMovingField(newx, newy);
6337 Feld[newx][newy] = EL_EMPTY;
6338 DrawLevelField(newx, newy);
6341 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6343 else if ((element == EL_PACMAN || element == EL_MOLE)
6344 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6346 if (AmoebaNr[newx][newy])
6348 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6349 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6350 Feld[newx][newy] == EL_BD_AMOEBA)
6351 AmoebaCnt[AmoebaNr[newx][newy]]--;
6354 if (element == EL_MOLE)
6356 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6357 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6359 ResetGfxAnimation(x, y);
6360 GfxAction[x][y] = ACTION_DIGGING;
6361 DrawLevelField(x, y);
6363 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6365 return; /* wait for shrinking amoeba */
6367 else /* element == EL_PACMAN */
6369 Feld[newx][newy] = EL_EMPTY;
6370 DrawLevelField(newx, newy);
6371 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6374 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6375 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6376 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6378 /* wait for shrinking amoeba to completely disappear */
6381 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6383 /* object was running against a wall */
6388 if (move_pattern & MV_ANY_DIRECTION &&
6389 move_pattern == MovDir[x][y])
6391 int blocking_element =
6392 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6395 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6396 element_info[element].token_name,
6397 element_info[blocking_element].token_name,
6401 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6404 element = Feld[x][y]; /* element might have changed */
6409 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6410 DrawLevelElementAnimation(x, y, element);
6412 if (element == EL_BUG ||
6413 element == EL_SPACESHIP ||
6414 element == EL_SP_SNIKSNAK)
6415 DrawLevelField(x, y);
6416 else if (element == EL_MOLE)
6417 DrawLevelField(x, y);
6418 else if (element == EL_BD_BUTTERFLY ||
6419 element == EL_BD_FIREFLY)
6420 DrawLevelElementAnimationIfNeeded(x, y, element);
6421 else if (element == EL_SATELLITE)
6422 DrawLevelElementAnimationIfNeeded(x, y, element);
6423 else if (element == EL_SP_ELECTRON)
6424 DrawLevelElementAnimationIfNeeded(x, y, element);
6427 if (DONT_TOUCH(element))
6428 TestIfBadThingTouchesHero(x, y);
6431 PlayLevelSoundAction(x, y, ACTION_WAITING);
6437 InitMovingField(x, y, MovDir[x][y]);
6439 PlayLevelSoundAction(x, y, ACTION_MOVING);
6443 ContinueMoving(x, y);
6446 void ContinueMoving(int x, int y)
6448 int element = Feld[x][y];
6449 int stored = Store[x][y];
6450 struct ElementInfo *ei = &element_info[element];
6451 int direction = MovDir[x][y];
6452 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6453 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6454 int newx = x + dx, newy = y + dy;
6456 int nextx = newx + dx, nexty = newy + dy;
6459 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6460 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6462 boolean pushed_by_player = Pushed[x][y];
6465 MovPos[x][y] += getElementMoveStepsize(x, y);
6468 if (pushed_by_player && IS_PLAYER(x, y))
6470 /* special case: moving object pushed by player */
6471 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6474 if (pushed_by_player) /* special case: moving object pushed by player */
6475 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6478 if (ABS(MovPos[x][y]) < TILEX)
6480 DrawLevelField(x, y);
6482 return; /* element is still moving */
6485 /* element reached destination field */
6487 Feld[x][y] = EL_EMPTY;
6488 Feld[newx][newy] = element;
6489 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6492 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6494 element = Feld[newx][newy] = EL_ACID;
6497 else if (element == EL_MOLE)
6499 Feld[x][y] = EL_SAND;
6501 DrawLevelFieldCrumbledSandNeighbours(x, y);
6503 else if (element == EL_QUICKSAND_FILLING)
6505 element = Feld[newx][newy] = get_next_element(element);
6506 Store[newx][newy] = Store[x][y];
6508 else if (element == EL_QUICKSAND_EMPTYING)
6510 Feld[x][y] = get_next_element(element);
6511 element = Feld[newx][newy] = Store[x][y];
6513 else if (element == EL_MAGIC_WALL_FILLING)
6515 element = Feld[newx][newy] = get_next_element(element);
6516 if (!game.magic_wall_active)
6517 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6518 Store[newx][newy] = Store[x][y];
6520 else if (element == EL_MAGIC_WALL_EMPTYING)
6522 Feld[x][y] = get_next_element(element);
6523 if (!game.magic_wall_active)
6524 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6525 element = Feld[newx][newy] = Store[x][y];
6527 else if (element == EL_BD_MAGIC_WALL_FILLING)
6529 element = Feld[newx][newy] = get_next_element(element);
6530 if (!game.magic_wall_active)
6531 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6532 Store[newx][newy] = Store[x][y];
6534 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6536 Feld[x][y] = get_next_element(element);
6537 if (!game.magic_wall_active)
6538 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6539 element = Feld[newx][newy] = Store[x][y];
6541 else if (element == EL_AMOEBA_DROPPING)
6543 Feld[x][y] = get_next_element(element);
6544 element = Feld[newx][newy] = Store[x][y];
6546 else if (element == EL_SOKOBAN_OBJECT)
6549 Feld[x][y] = Back[x][y];
6551 if (Back[newx][newy])
6552 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6554 Back[x][y] = Back[newx][newy] = 0;
6557 else if (Store[x][y] == EL_ACID)
6559 element = Feld[newx][newy] = EL_ACID;
6563 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6564 ei->move_leave_element != EL_EMPTY &&
6565 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6566 Store[x][y] != EL_EMPTY))
6568 /* some elements can leave other elements behind after moving */
6570 Feld[x][y] = ei->move_leave_element;
6571 InitField(x, y, FALSE);
6573 if (GFX_CRUMBLED(Feld[x][y]))
6574 DrawLevelFieldCrumbledSandNeighbours(x, y);
6578 Store[x][y] = EL_EMPTY;
6582 MovDelay[newx][newy] = 0;
6584 if (CAN_CHANGE(element))
6586 /* copy element change control values to new field */
6587 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6588 ChangePage[newx][newy] = ChangePage[x][y];
6589 Changed[newx][newy] = Changed[x][y];
6590 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6593 ChangeDelay[x][y] = 0;
6594 ChangePage[x][y] = -1;
6595 Changed[x][y] = FALSE;
6596 ChangeEvent[x][y] = -1;
6598 /* copy animation control values to new field */
6599 GfxFrame[newx][newy] = GfxFrame[x][y];
6600 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6601 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6602 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6604 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6607 /* do this after checking for left-behind element */
6608 ResetGfxAnimation(x, y); /* reset animation values for old field */
6612 /* some elements can leave other elements behind after moving */
6614 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6615 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6616 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6618 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6619 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6623 int move_leave_element = ei->move_leave_element;
6625 Feld[x][y] = move_leave_element;
6627 #if USE_PREVIOUS_MOVE_DIR
6628 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6629 MovDir[x][y] = direction;
6632 InitField(x, y, FALSE);
6634 if (GFX_CRUMBLED(Feld[x][y]))
6635 DrawLevelFieldCrumbledSandNeighbours(x, y);
6637 if (ELEM_IS_PLAYER(move_leave_element))
6638 RelocatePlayer(x, y, move_leave_element);
6643 /* some elements can leave other elements behind after moving */
6644 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6645 ei->move_leave_element != EL_EMPTY &&
6646 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6647 ei->can_leave_element_last))
6649 Feld[x][y] = ei->move_leave_element;
6650 InitField(x, y, FALSE);
6652 if (GFX_CRUMBLED(Feld[x][y]))
6653 DrawLevelFieldCrumbledSandNeighbours(x, y);
6656 ei->can_leave_element_last = ei->can_leave_element;
6657 ei->can_leave_element = FALSE;
6661 /* do this after checking for left-behind element */
6662 ResetGfxAnimation(x, y); /* reset animation values for old field */
6666 /* 2.1.1 (does not work correctly for spring) */
6667 if (!CAN_MOVE(element))
6668 MovDir[newx][newy] = 0;
6672 /* (does not work for falling objects that slide horizontally) */
6673 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6674 MovDir[newx][newy] = 0;
6677 if (!CAN_MOVE(element) ||
6678 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6679 MovDir[newx][newy] = 0;
6683 if (!CAN_MOVE(element) ||
6684 (CAN_FALL(element) && direction == MV_DOWN))
6685 GfxDir[x][y] = MovDir[newx][newy] = 0;
6687 if (!CAN_MOVE(element) ||
6688 (CAN_FALL(element) && direction == MV_DOWN &&
6689 (element == EL_SPRING ||
6690 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6691 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6692 GfxDir[x][y] = MovDir[newx][newy] = 0;
6698 DrawLevelField(x, y);
6699 DrawLevelField(newx, newy);
6701 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6703 /* prevent pushed element from moving on in pushed direction */
6704 if (pushed_by_player && CAN_MOVE(element) &&
6705 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6706 !(element_info[element].move_pattern & direction))
6707 TurnRound(newx, newy);
6710 /* prevent elements on conveyor belt from moving on in last direction */
6711 if (pushed_by_conveyor && CAN_FALL(element) &&
6712 direction & MV_HORIZONTAL)
6715 if (CAN_MOVE(element))
6716 InitMovDir(newx, newy);
6718 MovDir[newx][newy] = 0;
6720 MovDir[newx][newy] = 0;
6725 if (!pushed_by_player)
6727 int nextx = newx + dx, nexty = newy + dy;
6728 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6730 WasJustMoving[newx][newy] = 3;
6732 if (CAN_FALL(element) && direction == MV_DOWN)
6733 WasJustFalling[newx][newy] = 3;
6735 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6736 CheckCollision[newx][newy] = 2;
6739 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6741 TestIfBadThingTouchesHero(newx, newy);
6742 TestIfBadThingTouchesFriend(newx, newy);
6744 if (!IS_CUSTOM_ELEMENT(element))
6745 TestIfBadThingTouchesOtherBadThing(newx, newy);
6747 else if (element == EL_PENGUIN)
6748 TestIfFriendTouchesBadThing(newx, newy);
6750 #if USE_NEW_MOVE_STYLE
6752 if (CAN_FALL(element) && direction == MV_DOWN &&
6753 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6754 IS_PLAYER(x, newy + 1))
6755 printf("::: we would now kill the player [%d]\n", FrameCounter);
6758 /* give the player one last chance (one more frame) to move away */
6759 if (CAN_FALL(element) && direction == MV_DOWN &&
6760 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6761 ((newy < lev_fieldy - 1 && !IS_PLAYER(x, newy + 1)) ||
6762 game.engine_version < VERSION_IDENT(3,1,1,0)))
6765 if (CAN_FALL(element) && direction == MV_DOWN &&
6766 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6774 if (pushed_by_player && !game.use_change_when_pushing_bug)
6776 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6779 if (pushed_by_player)
6784 int dig_side = MV_DIR_OPPOSITE(direction);
6786 static int trigger_sides[4] =
6788 CH_SIDE_RIGHT, /* moving left */
6789 CH_SIDE_LEFT, /* moving right */
6790 CH_SIDE_BOTTOM, /* moving up */
6791 CH_SIDE_TOP, /* moving down */
6793 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6795 struct PlayerInfo *player = PLAYERINFO(x, y);
6797 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6798 player->index_bit, dig_side);
6799 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6800 player->index_bit, dig_side);
6805 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6809 if (ChangePage[newx][newy] != -1) /* delayed change */
6810 ChangeElement(newx, newy, ChangePage[newx][newy]);
6815 TestIfElementHitsCustomElement(newx, newy, direction);
6819 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6821 int hitting_element = Feld[newx][newy];
6823 /* !!! fix side (direction) orientation here and elsewhere !!! */
6824 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6828 if (IN_LEV_FIELD(nextx, nexty))
6830 int opposite_direction = MV_DIR_OPPOSITE(direction);
6831 int hitting_side = direction;
6832 int touched_side = opposite_direction;
6833 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6834 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6835 MovDir[nextx][nexty] != direction ||
6836 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6842 CheckElementChangeBySide(nextx, nexty, touched_element,
6843 CE_HIT_BY_SOMETHING, opposite_direction);
6845 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6846 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6848 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6850 struct ElementChangeInfo *change =
6851 &element_info[hitting_element].change_page[i];
6853 if (change->can_change &&
6854 change->has_event[CE_HITTING_X] &&
6855 change->trigger_side & touched_side &&
6856 change->trigger_element == touched_element)
6858 CheckElementChangeByPage(newx, newy, hitting_element,
6859 touched_element, CE_HITTING_X, i);
6865 if (IS_CUSTOM_ELEMENT(touched_element) &&
6866 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6868 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6870 struct ElementChangeInfo *change =
6871 &element_info[touched_element].change_page[i];
6873 if (change->can_change &&
6874 change->has_event[CE_HIT_BY_X] &&
6875 change->trigger_side & hitting_side &&
6876 change->trigger_element == hitting_element)
6878 CheckElementChangeByPage(nextx, nexty, touched_element,
6879 hitting_element, CE_HIT_BY_X,i);
6890 TestIfPlayerTouchesCustomElement(newx, newy);
6891 TestIfElementTouchesCustomElement(newx, newy);
6894 int AmoebeNachbarNr(int ax, int ay)
6897 int element = Feld[ax][ay];
6899 static int xy[4][2] =
6907 for (i = 0; i < NUM_DIRECTIONS; i++)
6909 int x = ax + xy[i][0];
6910 int y = ay + xy[i][1];
6912 if (!IN_LEV_FIELD(x, y))
6915 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6916 group_nr = AmoebaNr[x][y];
6922 void AmoebenVereinigen(int ax, int ay)
6924 int i, x, y, xx, yy;
6925 int new_group_nr = AmoebaNr[ax][ay];
6926 static int xy[4][2] =
6934 if (new_group_nr == 0)
6937 for (i = 0; i < NUM_DIRECTIONS; i++)
6942 if (!IN_LEV_FIELD(x, y))
6945 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6946 Feld[x][y] == EL_BD_AMOEBA ||
6947 Feld[x][y] == EL_AMOEBA_DEAD) &&
6948 AmoebaNr[x][y] != new_group_nr)
6950 int old_group_nr = AmoebaNr[x][y];
6952 if (old_group_nr == 0)
6955 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6956 AmoebaCnt[old_group_nr] = 0;
6957 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6958 AmoebaCnt2[old_group_nr] = 0;
6960 for (yy = 0; yy < lev_fieldy; yy++)
6962 for (xx = 0; xx < lev_fieldx; xx++)
6964 if (AmoebaNr[xx][yy] == old_group_nr)
6965 AmoebaNr[xx][yy] = new_group_nr;
6972 void AmoebeUmwandeln(int ax, int ay)
6976 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6978 int group_nr = AmoebaNr[ax][ay];
6983 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6984 printf("AmoebeUmwandeln(): This should never happen!\n");
6989 for (y = 0; y < lev_fieldy; y++)
6991 for (x = 0; x < lev_fieldx; x++)
6993 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6996 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7000 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7001 SND_AMOEBA_TURNING_TO_GEM :
7002 SND_AMOEBA_TURNING_TO_ROCK));
7007 static int xy[4][2] =
7015 for (i = 0; i < NUM_DIRECTIONS; i++)
7020 if (!IN_LEV_FIELD(x, y))
7023 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7025 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7026 SND_AMOEBA_TURNING_TO_GEM :
7027 SND_AMOEBA_TURNING_TO_ROCK));
7034 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7037 int group_nr = AmoebaNr[ax][ay];
7038 boolean done = FALSE;
7043 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7044 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7049 for (y = 0; y < lev_fieldy; y++)
7051 for (x = 0; x < lev_fieldx; x++)
7053 if (AmoebaNr[x][y] == group_nr &&
7054 (Feld[x][y] == EL_AMOEBA_DEAD ||
7055 Feld[x][y] == EL_BD_AMOEBA ||
7056 Feld[x][y] == EL_AMOEBA_GROWING))
7059 Feld[x][y] = new_element;
7060 InitField(x, y, FALSE);
7061 DrawLevelField(x, y);
7068 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7069 SND_BD_AMOEBA_TURNING_TO_ROCK :
7070 SND_BD_AMOEBA_TURNING_TO_GEM));
7073 void AmoebeWaechst(int x, int y)
7075 static unsigned long sound_delay = 0;
7076 static unsigned long sound_delay_value = 0;
7078 if (!MovDelay[x][y]) /* start new growing cycle */
7082 if (DelayReached(&sound_delay, sound_delay_value))
7085 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7087 if (Store[x][y] == EL_BD_AMOEBA)
7088 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7090 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7092 sound_delay_value = 30;
7096 if (MovDelay[x][y]) /* wait some time before growing bigger */
7099 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7101 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7102 6 - MovDelay[x][y]);
7104 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7107 if (!MovDelay[x][y])
7109 Feld[x][y] = Store[x][y];
7111 DrawLevelField(x, y);
7116 void AmoebaDisappearing(int x, int y)
7118 static unsigned long sound_delay = 0;
7119 static unsigned long sound_delay_value = 0;
7121 if (!MovDelay[x][y]) /* start new shrinking cycle */
7125 if (DelayReached(&sound_delay, sound_delay_value))
7126 sound_delay_value = 30;
7129 if (MovDelay[x][y]) /* wait some time before shrinking */
7132 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7134 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7135 6 - MovDelay[x][y]);
7137 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7140 if (!MovDelay[x][y])
7142 Feld[x][y] = EL_EMPTY;
7143 DrawLevelField(x, y);
7145 /* don't let mole enter this field in this cycle;
7146 (give priority to objects falling to this field from above) */
7152 void AmoebeAbleger(int ax, int ay)
7155 int element = Feld[ax][ay];
7156 int graphic = el2img(element);
7157 int newax = ax, neway = ay;
7158 static int xy[4][2] =
7166 if (!level.amoeba_speed)
7168 Feld[ax][ay] = EL_AMOEBA_DEAD;
7169 DrawLevelField(ax, ay);
7173 if (IS_ANIMATED(graphic))
7174 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7176 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7177 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7179 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7182 if (MovDelay[ax][ay])
7186 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7189 int x = ax + xy[start][0];
7190 int y = ay + xy[start][1];
7192 if (!IN_LEV_FIELD(x, y))
7196 if (IS_FREE(x, y) ||
7197 CAN_GROW_INTO(Feld[x][y]) ||
7198 Feld[x][y] == EL_QUICKSAND_EMPTY)
7204 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7205 if (IS_FREE(x, y) ||
7206 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7213 if (newax == ax && neway == ay)
7216 else /* normal or "filled" (BD style) amoeba */
7219 boolean waiting_for_player = FALSE;
7221 for (i = 0; i < NUM_DIRECTIONS; i++)
7223 int j = (start + i) % 4;
7224 int x = ax + xy[j][0];
7225 int y = ay + xy[j][1];
7227 if (!IN_LEV_FIELD(x, y))
7231 if (IS_FREE(x, y) ||
7232 CAN_GROW_INTO(Feld[x][y]) ||
7233 Feld[x][y] == EL_QUICKSAND_EMPTY)
7240 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7241 if (IS_FREE(x, y) ||
7242 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7249 else if (IS_PLAYER(x, y))
7250 waiting_for_player = TRUE;
7253 if (newax == ax && neway == ay) /* amoeba cannot grow */
7256 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7258 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7261 Feld[ax][ay] = EL_AMOEBA_DEAD;
7262 DrawLevelField(ax, ay);
7263 AmoebaCnt[AmoebaNr[ax][ay]]--;
7265 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7267 if (element == EL_AMOEBA_FULL)
7268 AmoebeUmwandeln(ax, ay);
7269 else if (element == EL_BD_AMOEBA)
7270 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7275 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7277 /* amoeba gets larger by growing in some direction */
7279 int new_group_nr = AmoebaNr[ax][ay];
7282 if (new_group_nr == 0)
7284 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7285 printf("AmoebeAbleger(): This should never happen!\n");
7290 AmoebaNr[newax][neway] = new_group_nr;
7291 AmoebaCnt[new_group_nr]++;
7292 AmoebaCnt2[new_group_nr]++;
7294 /* if amoeba touches other amoeba(s) after growing, unify them */
7295 AmoebenVereinigen(newax, neway);
7297 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7299 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7305 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7306 (neway == lev_fieldy - 1 && newax != ax))
7308 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7309 Store[newax][neway] = element;
7311 else if (neway == ay)
7313 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7315 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7317 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7322 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7323 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7324 Store[ax][ay] = EL_AMOEBA_DROP;
7325 ContinueMoving(ax, ay);
7329 DrawLevelField(newax, neway);
7332 void Life(int ax, int ay)
7335 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7337 int element = Feld[ax][ay];
7338 int graphic = el2img(element);
7339 boolean changed = FALSE;
7341 if (IS_ANIMATED(graphic))
7342 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7347 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7348 MovDelay[ax][ay] = life_time;
7350 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7353 if (MovDelay[ax][ay])
7357 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7359 int xx = ax+x1, yy = ay+y1;
7362 if (!IN_LEV_FIELD(xx, yy))
7365 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7367 int x = xx+x2, y = yy+y2;
7369 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7372 if (((Feld[x][y] == element ||
7373 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7375 (IS_FREE(x, y) && Stop[x][y]))
7379 if (xx == ax && yy == ay) /* field in the middle */
7381 if (nachbarn < life[0] || nachbarn > life[1])
7383 Feld[xx][yy] = EL_EMPTY;
7385 DrawLevelField(xx, yy);
7386 Stop[xx][yy] = TRUE;
7391 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7392 { /* free border field */
7393 if (nachbarn >= life[2] && nachbarn <= life[3])
7395 Feld[xx][yy] = element;
7396 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7398 DrawLevelField(xx, yy);
7399 Stop[xx][yy] = TRUE;
7404 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7405 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7406 { /* free border field */
7407 if (nachbarn >= life[2] && nachbarn <= life[3])
7409 Feld[xx][yy] = element;
7410 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7412 DrawLevelField(xx, yy);
7413 Stop[xx][yy] = TRUE;
7421 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7422 SND_GAME_OF_LIFE_GROWING);
7425 static void InitRobotWheel(int x, int y)
7427 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7430 static void RunRobotWheel(int x, int y)
7432 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7435 static void StopRobotWheel(int x, int y)
7437 if (ZX == x && ZY == y)
7441 static void InitTimegateWheel(int x, int y)
7444 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7446 /* another brainless, "type style" bug ... :-( */
7447 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7451 static void RunTimegateWheel(int x, int y)
7453 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7456 void CheckExit(int x, int y)
7458 if (local_player->gems_still_needed > 0 ||
7459 local_player->sokobanfields_still_needed > 0 ||
7460 local_player->lights_still_needed > 0)
7462 int element = Feld[x][y];
7463 int graphic = el2img(element);
7465 if (IS_ANIMATED(graphic))
7466 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7471 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7474 Feld[x][y] = EL_EXIT_OPENING;
7476 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7479 void CheckExitSP(int x, int y)
7481 if (local_player->gems_still_needed > 0)
7483 int element = Feld[x][y];
7484 int graphic = el2img(element);
7486 if (IS_ANIMATED(graphic))
7487 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7492 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7495 Feld[x][y] = EL_SP_EXIT_OPENING;
7497 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7500 static void CloseAllOpenTimegates()
7504 for (y = 0; y < lev_fieldy; y++)
7506 for (x = 0; x < lev_fieldx; x++)
7508 int element = Feld[x][y];
7510 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7512 Feld[x][y] = EL_TIMEGATE_CLOSING;
7514 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7516 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7523 void EdelsteinFunkeln(int x, int y)
7525 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7528 if (Feld[x][y] == EL_BD_DIAMOND)
7531 if (MovDelay[x][y] == 0) /* next animation frame */
7532 MovDelay[x][y] = 11 * !SimpleRND(500);
7534 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7538 if (setup.direct_draw && MovDelay[x][y])
7539 SetDrawtoField(DRAW_BUFFERED);
7541 DrawLevelElementAnimation(x, y, Feld[x][y]);
7543 if (MovDelay[x][y] != 0)
7545 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7546 10 - MovDelay[x][y]);
7548 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7550 if (setup.direct_draw)
7554 dest_x = FX + SCREENX(x) * TILEX;
7555 dest_y = FY + SCREENY(y) * TILEY;
7557 BlitBitmap(drawto_field, window,
7558 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7559 SetDrawtoField(DRAW_DIRECT);
7565 void MauerWaechst(int x, int y)
7569 if (!MovDelay[x][y]) /* next animation frame */
7570 MovDelay[x][y] = 3 * delay;
7572 if (MovDelay[x][y]) /* wait some time before next frame */
7576 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7578 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7579 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7581 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7584 if (!MovDelay[x][y])
7586 if (MovDir[x][y] == MV_LEFT)
7588 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7589 DrawLevelField(x - 1, y);
7591 else if (MovDir[x][y] == MV_RIGHT)
7593 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7594 DrawLevelField(x + 1, y);
7596 else if (MovDir[x][y] == MV_UP)
7598 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7599 DrawLevelField(x, y - 1);
7603 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7604 DrawLevelField(x, y + 1);
7607 Feld[x][y] = Store[x][y];
7609 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7610 DrawLevelField(x, y);
7615 void MauerAbleger(int ax, int ay)
7617 int element = Feld[ax][ay];
7618 int graphic = el2img(element);
7619 boolean oben_frei = FALSE, unten_frei = FALSE;
7620 boolean links_frei = FALSE, rechts_frei = FALSE;
7621 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7622 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7623 boolean new_wall = FALSE;
7625 if (IS_ANIMATED(graphic))
7626 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7628 if (!MovDelay[ax][ay]) /* start building new wall */
7629 MovDelay[ax][ay] = 6;
7631 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7634 if (MovDelay[ax][ay])
7638 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7640 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7642 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7644 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7647 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7648 element == EL_EXPANDABLE_WALL_ANY)
7652 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7653 Store[ax][ay-1] = element;
7654 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7655 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7656 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7657 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7662 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7663 Store[ax][ay+1] = element;
7664 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7665 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7666 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7667 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7672 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7673 element == EL_EXPANDABLE_WALL_ANY ||
7674 element == EL_EXPANDABLE_WALL)
7678 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7679 Store[ax-1][ay] = element;
7680 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7681 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7682 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7683 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7689 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7690 Store[ax+1][ay] = element;
7691 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7692 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7693 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7694 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7699 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7700 DrawLevelField(ax, ay);
7702 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7704 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7705 unten_massiv = TRUE;
7706 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7707 links_massiv = TRUE;
7708 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7709 rechts_massiv = TRUE;
7711 if (((oben_massiv && unten_massiv) ||
7712 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7713 element == EL_EXPANDABLE_WALL) &&
7714 ((links_massiv && rechts_massiv) ||
7715 element == EL_EXPANDABLE_WALL_VERTICAL))
7716 Feld[ax][ay] = EL_WALL;
7720 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7722 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7726 void CheckForDragon(int x, int y)
7729 boolean dragon_found = FALSE;
7730 static int xy[4][2] =
7738 for (i = 0; i < NUM_DIRECTIONS; i++)
7740 for (j = 0; j < 4; j++)
7742 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7744 if (IN_LEV_FIELD(xx, yy) &&
7745 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7747 if (Feld[xx][yy] == EL_DRAGON)
7748 dragon_found = TRUE;
7757 for (i = 0; i < NUM_DIRECTIONS; i++)
7759 for (j = 0; j < 3; j++)
7761 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7763 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7765 Feld[xx][yy] = EL_EMPTY;
7766 DrawLevelField(xx, yy);
7775 static void InitBuggyBase(int x, int y)
7777 int element = Feld[x][y];
7778 int activating_delay = FRAMES_PER_SECOND / 4;
7781 (element == EL_SP_BUGGY_BASE ?
7782 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7783 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7785 element == EL_SP_BUGGY_BASE_ACTIVE ?
7786 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7789 static void WarnBuggyBase(int x, int y)
7792 static int xy[4][2] =
7800 for (i = 0; i < NUM_DIRECTIONS; i++)
7802 int xx = x + xy[i][0], yy = y + xy[i][1];
7804 if (IS_PLAYER(xx, yy))
7806 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7813 static void InitTrap(int x, int y)
7815 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7818 static void ActivateTrap(int x, int y)
7820 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7823 static void ChangeActiveTrap(int x, int y)
7825 int graphic = IMG_TRAP_ACTIVE;
7827 /* if new animation frame was drawn, correct crumbled sand border */
7828 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7829 DrawLevelFieldCrumbledSand(x, y);
7832 static void ChangeElementNowExt(int x, int y, int target_element)
7834 int previous_move_direction = MovDir[x][y];
7836 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7837 IS_WALKABLE(Feld[x][y]));
7839 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7840 IS_WALKABLE(Feld[x][y]) &&
7844 /* check if element under player changes from accessible to unaccessible
7845 (needed for special case of dropping element which then changes) */
7846 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7847 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7850 printf("::: BOOOM! [%d, '%s']\n", target_element,
7851 element_info[target_element].token_name);
7863 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7864 RemoveMovingField(x, y);
7868 Feld[x][y] = target_element;
7871 Feld[x][y] = target_element;
7874 ResetGfxAnimation(x, y);
7875 ResetRandomAnimationValue(x, y);
7877 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7878 MovDir[x][y] = previous_move_direction;
7881 InitField_WithBug1(x, y, FALSE);
7883 InitField(x, y, FALSE);
7884 if (CAN_MOVE(Feld[x][y]))
7888 DrawLevelField(x, y);
7890 if (GFX_CRUMBLED(Feld[x][y]))
7891 DrawLevelFieldCrumbledSandNeighbours(x, y);
7895 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7899 TestIfBadThingTouchesHero(x, y);
7900 TestIfPlayerTouchesCustomElement(x, y);
7901 TestIfElementTouchesCustomElement(x, y);
7904 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7905 if (ELEM_IS_PLAYER(target_element))
7906 RelocatePlayer(x, y, target_element);
7909 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7911 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7915 TestIfBadThingTouchesHero(x, y);
7916 TestIfPlayerTouchesCustomElement(x, y);
7917 TestIfElementTouchesCustomElement(x, y);
7921 static boolean ChangeElementNow(int x, int y, int element, int page)
7923 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7925 int old_element = Feld[x][y];
7927 /* always use default change event to prevent running into a loop */
7928 if (ChangeEvent[x][y] == -1)
7929 ChangeEvent[x][y] = CE_DELAY;
7931 if (ChangeEvent[x][y] == CE_DELAY)
7933 /* reset actual trigger element and player */
7934 change->actual_trigger_element = EL_EMPTY;
7935 change->actual_trigger_player = EL_PLAYER_1;
7939 /* do not change any elements that have already changed in this frame */
7943 /* do not change already changed elements with same change event */
7944 if (Changed[x][y] & ChangeEvent[x][y])
7949 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7951 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7955 /* !!! indirect change before direct change !!! */
7956 CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
7959 if (change->explode)
7966 if (change->use_target_content)
7968 boolean complete_replace = TRUE;
7969 boolean can_replace[3][3];
7972 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7975 boolean is_walkable;
7976 boolean is_diggable;
7977 boolean is_collectible;
7978 boolean is_removable;
7979 boolean is_destructible;
7980 int ex = x + xx - 1;
7981 int ey = y + yy - 1;
7982 int content_element = change->target_content[xx][yy];
7985 can_replace[xx][yy] = TRUE;
7987 if (ex == x && ey == y) /* do not check changing element itself */
7990 if (content_element == EL_EMPTY_SPACE)
7992 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7997 if (!IN_LEV_FIELD(ex, ey))
7999 can_replace[xx][yy] = FALSE;
8000 complete_replace = FALSE;
8006 if (Changed[ex][ey]) /* do not change already changed elements */
8008 can_replace[xx][yy] = FALSE;
8009 complete_replace = FALSE;
8017 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8018 e = MovingOrBlocked2Element(ex, ey);
8023 is_empty = (IS_FREE(ex, ey) ||
8024 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8025 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8026 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8030 is_empty = (IS_FREE(ex, ey) ||
8031 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8033 is_empty = (IS_FREE(ex, ey) ||
8034 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8039 is_walkable = (is_empty || IS_WALKABLE(e));
8040 is_diggable = (is_empty || IS_DIGGABLE(e));
8041 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8042 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8043 is_removable = (is_diggable || is_collectible);
8045 can_replace[xx][yy] =
8046 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8047 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8048 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8049 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8050 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8051 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8052 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8054 if (!can_replace[xx][yy])
8055 complete_replace = FALSE;
8057 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8058 IS_WALKABLE(content_element)));
8060 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8062 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8065 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8066 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8067 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8069 can_replace[xx][yy] = FALSE;
8070 complete_replace = FALSE;
8075 if (!change->only_if_complete || complete_replace)
8077 boolean something_has_changed = FALSE;
8079 if (change->only_if_complete && change->use_random_replace &&
8080 RND(100) < change->random_percentage)
8083 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8085 int ex = x + xx - 1;
8086 int ey = y + yy - 1;
8087 int content_element;
8089 if (can_replace[xx][yy] && (!change->use_random_replace ||
8090 RND(100) < change->random_percentage))
8092 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8093 RemoveMovingField(ex, ey);
8095 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8097 content_element = change->target_content[xx][yy];
8098 target_element = GET_TARGET_ELEMENT(content_element, change);
8100 ChangeElementNowExt(ex, ey, target_element);
8102 something_has_changed = TRUE;
8104 /* for symmetry reasons, freeze newly created border elements */
8105 if (ex != x || ey != y)
8106 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8110 if (something_has_changed)
8111 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8116 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8118 ChangeElementNowExt(x, y, target_element);
8120 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8124 /* this uses direct change before indirect change */
8125 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8131 static void ChangeElement(int x, int y, int page)
8133 int element = MovingOrBlocked2Element(x, y);
8134 struct ElementInfo *ei = &element_info[element];
8135 struct ElementChangeInfo *change = &ei->change_page[page];
8138 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8141 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8142 x, y, element, element_info[element].token_name);
8143 printf("ChangeElement(): This should never happen!\n");
8148 /* this can happen with classic bombs on walkable, changing elements */
8149 if (!CAN_CHANGE(element))
8152 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8153 ChangeDelay[x][y] = 0;
8159 if (ChangeDelay[x][y] == 0) /* initialize element change */
8161 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8162 RND(change->delay_random * change->delay_frames)) + 1;
8164 ResetGfxAnimation(x, y);
8165 ResetRandomAnimationValue(x, y);
8167 if (change->pre_change_function)
8168 change->pre_change_function(x, y);
8171 ChangeDelay[x][y]--;
8173 if (ChangeDelay[x][y] != 0) /* continue element change */
8175 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8177 if (IS_ANIMATED(graphic))
8178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8180 if (change->change_function)
8181 change->change_function(x, y);
8183 else /* finish element change */
8185 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8187 page = ChangePage[x][y];
8188 ChangePage[x][y] = -1;
8190 change = &ei->change_page[page];
8194 if (IS_MOVING(x, y) && !change->explode)
8196 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8199 ChangeDelay[x][y] = 1; /* try change after next move step */
8200 ChangePage[x][y] = page; /* remember page to use for change */
8205 if (ChangeElementNow(x, y, element, page))
8207 if (change->post_change_function)
8208 change->post_change_function(x, y);
8213 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8214 int trigger_element,
8221 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8223 if (!(trigger_events[trigger_element][trigger_event]))
8226 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8228 int element = EL_CUSTOM_START + i;
8230 boolean change_element = FALSE;
8233 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8236 for (j = 0; j < element_info[element].num_change_pages; j++)
8238 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8240 if (change->can_change &&
8241 change->has_event[trigger_event] &&
8242 change->trigger_side & trigger_side &&
8243 change->trigger_player & trigger_player &&
8244 change->trigger_page & trigger_page_bits &&
8245 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8248 if (!(change->has_event[trigger_event]))
8249 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8250 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8253 change_element = TRUE;
8256 change->actual_trigger_element = trigger_element;
8257 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8263 if (!change_element)
8266 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8269 if (x == lx && y == ly) /* do not change trigger element itself */
8273 if (Feld[x][y] == element)
8275 ChangeDelay[x][y] = 1;
8276 ChangeEvent[x][y] = trigger_event;
8277 ChangeElement(x, y, page);
8285 static boolean CheckElementChangeExt(int x, int y,
8287 int trigger_element,
8293 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8296 if (Feld[x][y] == EL_BLOCKED)
8298 Blocked2Moving(x, y, &x, &y);
8299 element = Feld[x][y];
8303 if (Feld[x][y] != element) /* check if element has already changed */
8306 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8307 Feld[x][y], element_info[Feld[x][y]].token_name,
8308 element, element_info[element].token_name,
8317 if (trigger_page < 0)
8319 boolean change_element = FALSE;
8322 for (i = 0; i < element_info[element].num_change_pages; i++)
8324 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8326 if (change->can_change &&
8327 change->has_event[trigger_event] &&
8328 change->trigger_side & trigger_side &&
8329 change->trigger_player & trigger_player)
8331 change_element = TRUE;
8334 change->actual_trigger_element = trigger_element;
8335 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8341 if (!change_element)
8346 struct ElementInfo *ei = &element_info[element];
8347 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8349 change->actual_trigger_element = trigger_element;
8350 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8355 /* !!! this check misses pages with same event, but different side !!! */
8357 if (trigger_page < 0)
8358 trigger_page = element_info[element].event_page_nr[trigger_event];
8360 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8364 ChangeDelay[x][y] = 1;
8365 ChangeEvent[x][y] = trigger_event;
8366 ChangeElement(x, y, trigger_page);
8371 static void PlayPlayerSound(struct PlayerInfo *player)
8373 int jx = player->jx, jy = player->jy;
8374 int element = player->element_nr;
8375 int last_action = player->last_action_waiting;
8376 int action = player->action_waiting;
8378 if (player->is_waiting)
8380 if (action != last_action)
8381 PlayLevelSoundElementAction(jx, jy, element, action);
8383 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8387 if (action != last_action)
8388 StopSound(element_info[element].sound[last_action]);
8390 if (last_action == ACTION_SLEEPING)
8391 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8395 static void PlayAllPlayersSound()
8399 for (i = 0; i < MAX_PLAYERS; i++)
8400 if (stored_player[i].active)
8401 PlayPlayerSound(&stored_player[i]);
8404 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8406 boolean last_waiting = player->is_waiting;
8407 int move_dir = player->MovDir;
8409 player->last_action_waiting = player->action_waiting;
8413 if (!last_waiting) /* not waiting -> waiting */
8415 player->is_waiting = TRUE;
8417 player->frame_counter_bored =
8419 game.player_boring_delay_fixed +
8420 SimpleRND(game.player_boring_delay_random);
8421 player->frame_counter_sleeping =
8423 game.player_sleeping_delay_fixed +
8424 SimpleRND(game.player_sleeping_delay_random);
8426 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8429 if (game.player_sleeping_delay_fixed +
8430 game.player_sleeping_delay_random > 0 &&
8431 player->anim_delay_counter == 0 &&
8432 player->post_delay_counter == 0 &&
8433 FrameCounter >= player->frame_counter_sleeping)
8434 player->is_sleeping = TRUE;
8435 else if (game.player_boring_delay_fixed +
8436 game.player_boring_delay_random > 0 &&
8437 FrameCounter >= player->frame_counter_bored)
8438 player->is_bored = TRUE;
8440 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8441 player->is_bored ? ACTION_BORING :
8444 if (player->is_sleeping)
8446 if (player->num_special_action_sleeping > 0)
8448 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8450 int last_special_action = player->special_action_sleeping;
8451 int num_special_action = player->num_special_action_sleeping;
8452 int special_action =
8453 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8454 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8455 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8456 last_special_action + 1 : ACTION_SLEEPING);
8457 int special_graphic =
8458 el_act_dir2img(player->element_nr, special_action, move_dir);
8460 player->anim_delay_counter =
8461 graphic_info[special_graphic].anim_delay_fixed +
8462 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8463 player->post_delay_counter =
8464 graphic_info[special_graphic].post_delay_fixed +
8465 SimpleRND(graphic_info[special_graphic].post_delay_random);
8467 player->special_action_sleeping = special_action;
8470 if (player->anim_delay_counter > 0)
8472 player->action_waiting = player->special_action_sleeping;
8473 player->anim_delay_counter--;
8475 else if (player->post_delay_counter > 0)
8477 player->post_delay_counter--;
8481 else if (player->is_bored)
8483 if (player->num_special_action_bored > 0)
8485 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8487 int special_action =
8488 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8489 int special_graphic =
8490 el_act_dir2img(player->element_nr, special_action, move_dir);
8492 player->anim_delay_counter =
8493 graphic_info[special_graphic].anim_delay_fixed +
8494 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8495 player->post_delay_counter =
8496 graphic_info[special_graphic].post_delay_fixed +
8497 SimpleRND(graphic_info[special_graphic].post_delay_random);
8499 player->special_action_bored = special_action;
8502 if (player->anim_delay_counter > 0)
8504 player->action_waiting = player->special_action_bored;
8505 player->anim_delay_counter--;
8507 else if (player->post_delay_counter > 0)
8509 player->post_delay_counter--;
8514 else if (last_waiting) /* waiting -> not waiting */
8516 player->is_waiting = FALSE;
8517 player->is_bored = FALSE;
8518 player->is_sleeping = FALSE;
8520 player->frame_counter_bored = -1;
8521 player->frame_counter_sleeping = -1;
8523 player->anim_delay_counter = 0;
8524 player->post_delay_counter = 0;
8526 player->action_waiting = ACTION_DEFAULT;
8528 player->special_action_bored = ACTION_DEFAULT;
8529 player->special_action_sleeping = ACTION_DEFAULT;
8534 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8537 static byte stored_player_action[MAX_PLAYERS];
8538 static int num_stored_actions = 0;
8540 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8541 int left = player_action & JOY_LEFT;
8542 int right = player_action & JOY_RIGHT;
8543 int up = player_action & JOY_UP;
8544 int down = player_action & JOY_DOWN;
8545 int button1 = player_action & JOY_BUTTON_1;
8546 int button2 = player_action & JOY_BUTTON_2;
8547 int dx = (left ? -1 : right ? 1 : 0);
8548 int dy = (up ? -1 : down ? 1 : 0);
8551 stored_player_action[player->index_nr] = 0;
8552 num_stored_actions++;
8556 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8559 if (!player->active || tape.pausing)
8563 printf("::: [%d %d %d %d] [%d %d]\n",
8564 left, right, up, down, button1, button2);
8570 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8575 if (player->MovPos == 0)
8576 CheckGravityMovement(player);
8579 snapped = SnapField(player, dx, dy);
8583 dropped = DropElement(player);
8585 moved = MovePlayer(player, dx, dy);
8588 if (tape.single_step && tape.recording && !tape.pausing)
8590 if (button1 || (dropped && !moved))
8592 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8593 SnapField(player, 0, 0); /* stop snapping */
8597 SetPlayerWaiting(player, FALSE);
8600 return player_action;
8602 stored_player_action[player->index_nr] = player_action;
8608 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8611 /* no actions for this player (no input at player's configured device) */
8613 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8614 SnapField(player, 0, 0);
8615 CheckGravityMovementWhenNotMoving(player);
8617 if (player->MovPos == 0)
8618 SetPlayerWaiting(player, TRUE);
8620 if (player->MovPos == 0) /* needed for tape.playing */
8621 player->is_moving = FALSE;
8623 player->is_dropping = FALSE;
8629 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8631 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8633 TapeRecordAction(stored_player_action);
8634 num_stored_actions = 0;
8641 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8643 static byte stored_player_action[MAX_PLAYERS];
8644 static int num_stored_actions = 0;
8645 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8646 int left = player_action & JOY_LEFT;
8647 int right = player_action & JOY_RIGHT;
8648 int up = player_action & JOY_UP;
8649 int down = player_action & JOY_DOWN;
8650 int button1 = player_action & JOY_BUTTON_1;
8651 int button2 = player_action & JOY_BUTTON_2;
8652 int dx = (left ? -1 : right ? 1 : 0);
8653 int dy = (up ? -1 : down ? 1 : 0);
8655 stored_player_action[player->index_nr] = 0;
8656 num_stored_actions++;
8658 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8660 if (!player->active || tape.pausing)
8665 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8668 snapped = SnapField(player, dx, dy);
8672 dropped = DropElement(player);
8674 moved = MovePlayer(player, dx, dy);
8677 if (tape.single_step && tape.recording && !tape.pausing)
8679 if (button1 || (dropped && !moved))
8681 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8682 SnapField(player, 0, 0); /* stop snapping */
8686 stored_player_action[player->index_nr] = player_action;
8690 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8692 /* no actions for this player (no input at player's configured device) */
8694 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8695 SnapField(player, 0, 0);
8696 CheckGravityMovementWhenNotMoving(player);
8698 if (player->MovPos == 0)
8699 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8701 if (player->MovPos == 0) /* needed for tape.playing */
8702 player->is_moving = FALSE;
8705 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8707 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8709 TapeRecordAction(stored_player_action);
8710 num_stored_actions = 0;
8715 void AdvanceFrameAndPlayerCounters(int player_nr)
8719 /* advance frame counters (global frame counter and time frame counter) */
8723 /* advance player counters (counters for move delay, move animation etc.) */
8724 for (i = 0; i < MAX_PLAYERS; i++)
8726 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8728 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8730 if (!advance_player_counters) /* not all players may be affected */
8733 stored_player[i].Frame += move_frames;
8735 if (stored_player[i].MovPos != 0)
8736 stored_player[i].StepFrame += move_frames;
8738 #if USE_NEW_MOVE_DELAY
8739 if (stored_player[i].move_delay > 0)
8740 stored_player[i].move_delay--;
8743 #if USE_NEW_PUSH_DELAY
8744 /* due to bugs in previous versions, counter must count up, not down */
8745 if (stored_player[i].push_delay != -1)
8746 stored_player[i].push_delay++;
8749 if (stored_player[i].drop_delay > 0)
8750 stored_player[i].drop_delay--;
8756 static unsigned long game_frame_delay = 0;
8757 unsigned long game_frame_delay_value;
8758 int magic_wall_x = 0, magic_wall_y = 0;
8759 int i, x, y, element, graphic;
8760 byte *recorded_player_action;
8761 byte summarized_player_action = 0;
8763 byte tape_action[MAX_PLAYERS];
8766 if (game_status != GAME_MODE_PLAYING)
8769 game_frame_delay_value =
8770 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8772 if (tape.playing && tape.warp_forward && !tape.pausing)
8773 game_frame_delay_value = 0;
8775 /* ---------- main game synchronization point ---------- */
8777 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8779 if (network_playing && !network_player_action_received)
8783 printf("DEBUG: try to get network player actions in time\n");
8787 #if defined(NETWORK_AVALIABLE)
8788 /* last chance to get network player actions without main loop delay */
8792 if (game_status != GAME_MODE_PLAYING)
8795 if (!network_player_action_received)
8799 printf("DEBUG: failed to get network player actions in time\n");
8810 printf("::: getting new tape action [%d]\n", FrameCounter);
8813 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8816 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8817 if (recorded_player_action == NULL && tape.pausing)
8822 printf("::: %d\n", stored_player[0].action);
8826 if (recorded_player_action != NULL)
8827 for (i = 0; i < MAX_PLAYERS; i++)
8828 stored_player[i].action = recorded_player_action[i];
8831 for (i = 0; i < MAX_PLAYERS; i++)
8833 summarized_player_action |= stored_player[i].action;
8835 if (!network_playing)
8836 stored_player[i].effective_action = stored_player[i].action;
8839 #if defined(NETWORK_AVALIABLE)
8840 if (network_playing)
8841 SendToServer_MovePlayer(summarized_player_action);
8844 if (!options.network && !setup.team_mode)
8845 local_player->effective_action = summarized_player_action;
8848 if (recorded_player_action != NULL)
8849 for (i = 0; i < MAX_PLAYERS; i++)
8850 stored_player[i].effective_action = recorded_player_action[i];
8854 for (i = 0; i < MAX_PLAYERS; i++)
8856 tape_action[i] = stored_player[i].effective_action;
8858 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8859 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8862 /* only save actions from input devices, but not programmed actions */
8864 TapeRecordAction(tape_action);
8867 for (i = 0; i < MAX_PLAYERS; i++)
8869 int actual_player_action = stored_player[i].effective_action;
8872 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8873 - rnd_equinox_tetrachloride 048
8874 - rnd_equinox_tetrachloride_ii 096
8875 - rnd_emanuel_schmieg 002
8876 - doctor_sloan_ww 001, 020
8878 if (stored_player[i].MovPos == 0)
8879 CheckGravityMovement(&stored_player[i]);
8883 /* overwrite programmed action with tape action */
8884 if (stored_player[i].programmed_action)
8885 actual_player_action = stored_player[i].programmed_action;
8889 if (stored_player[i].programmed_action)
8890 printf("::: %d\n", stored_player[i].programmed_action);
8893 if (recorded_player_action)
8896 if (stored_player[i].programmed_action &&
8897 stored_player[i].programmed_action != recorded_player_action[i])
8898 printf("::: %d: %d <-> %d\n", i,
8899 stored_player[i].programmed_action, recorded_player_action[i]);
8903 actual_player_action = recorded_player_action[i];
8908 /* overwrite tape action with programmed action */
8909 if (stored_player[i].programmed_action)
8910 actual_player_action = stored_player[i].programmed_action;
8915 printf("::: action: %d: %x [%d]\n",
8916 stored_player[i].MovPos, actual_player_action, FrameCounter);
8920 PlayerActions(&stored_player[i], actual_player_action);
8922 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8924 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8925 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8928 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8933 TapeRecordAction(tape_action);
8936 network_player_action_received = FALSE;
8938 ScrollScreen(NULL, SCROLL_GO_ON);
8944 for (i = 0; i < MAX_PLAYERS; i++)
8945 stored_player[i].Frame++;
8949 /* for backwards compatibility, the following code emulates a fixed bug that
8950 occured when pushing elements (causing elements that just made their last
8951 pushing step to already (if possible) make their first falling step in the
8952 same game frame, which is bad); this code is also needed to use the famous
8953 "spring push bug" which is used in older levels and might be wanted to be
8954 used also in newer levels, but in this case the buggy pushing code is only
8955 affecting the "spring" element and no other elements */
8958 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8960 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8963 for (i = 0; i < MAX_PLAYERS; i++)
8965 struct PlayerInfo *player = &stored_player[i];
8970 if (player->active && player->is_pushing && player->is_moving &&
8972 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8973 Feld[x][y] == EL_SPRING))
8975 if (player->active && player->is_pushing && player->is_moving &&
8979 ContinueMoving(x, y);
8981 /* continue moving after pushing (this is actually a bug) */
8982 if (!IS_MOVING(x, y))
8991 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8993 Changed[x][y] = FALSE;
8994 ChangeEvent[x][y] = -1;
8996 #if USE_NEW_BLOCK_STYLE
8997 /* this must be handled before main playfield loop */
8998 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9001 if (MovDelay[x][y] <= 0)
9007 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9009 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9010 printf("GameActions(): This should never happen!\n");
9012 ChangePage[x][y] = -1;
9017 if (WasJustMoving[x][y] > 0)
9018 WasJustMoving[x][y]--;
9019 if (WasJustFalling[x][y] > 0)
9020 WasJustFalling[x][y]--;
9021 if (CheckCollision[x][y] > 0)
9022 CheckCollision[x][y]--;
9027 /* reset finished pushing action (not done in ContinueMoving() to allow
9028 continous pushing animation for elements with zero push delay) */
9029 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9031 ResetGfxAnimation(x, y);
9032 DrawLevelField(x, y);
9037 if (IS_BLOCKED(x, y))
9041 Blocked2Moving(x, y, &oldx, &oldy);
9042 if (!IS_MOVING(oldx, oldy))
9044 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9045 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9046 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9047 printf("GameActions(): This should never happen!\n");
9053 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9055 element = Feld[x][y];
9057 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9059 graphic = el2img(element);
9065 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9067 element = graphic = 0;
9071 if (graphic_info[graphic].anim_global_sync)
9072 GfxFrame[x][y] = FrameCounter;
9074 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9075 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9076 ResetRandomAnimationValue(x, y);
9078 SetRandomAnimationValue(x, y);
9081 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9084 if (IS_INACTIVE(element))
9086 if (IS_ANIMATED(graphic))
9087 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9093 /* this may take place after moving, so 'element' may have changed */
9095 if (IS_CHANGING(x, y))
9097 if (IS_CHANGING(x, y) &&
9098 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9102 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9103 element_info[element].event_page_nr[CE_DELAY]);
9105 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9108 element = Feld[x][y];
9109 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9113 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9118 element = Feld[x][y];
9119 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9121 if (element == EL_MOLE)
9122 printf("::: %d, %d, %d [%d]\n",
9123 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9127 if (element == EL_YAMYAM)
9128 printf("::: %d, %d, %d\n",
9129 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9133 if (IS_ANIMATED(graphic) &&
9137 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9140 if (element == EL_BUG)
9141 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9145 if (element == EL_MOLE)
9146 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9150 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9151 EdelsteinFunkeln(x, y);
9153 else if ((element == EL_ACID ||
9154 element == EL_EXIT_OPEN ||
9155 element == EL_SP_EXIT_OPEN ||
9156 element == EL_SP_TERMINAL ||
9157 element == EL_SP_TERMINAL_ACTIVE ||
9158 element == EL_EXTRA_TIME ||
9159 element == EL_SHIELD_NORMAL ||
9160 element == EL_SHIELD_DEADLY) &&
9161 IS_ANIMATED(graphic))
9162 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9163 else if (IS_MOVING(x, y))
9164 ContinueMoving(x, y);
9165 else if (IS_ACTIVE_BOMB(element))
9166 CheckDynamite(x, y);
9168 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9169 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9171 else if (element == EL_AMOEBA_GROWING)
9172 AmoebeWaechst(x, y);
9173 else if (element == EL_AMOEBA_SHRINKING)
9174 AmoebaDisappearing(x, y);
9176 #if !USE_NEW_AMOEBA_CODE
9177 else if (IS_AMOEBALIVE(element))
9178 AmoebeAbleger(x, y);
9181 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9183 else if (element == EL_EXIT_CLOSED)
9185 else if (element == EL_SP_EXIT_CLOSED)
9187 else if (element == EL_EXPANDABLE_WALL_GROWING)
9189 else if (element == EL_EXPANDABLE_WALL ||
9190 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9191 element == EL_EXPANDABLE_WALL_VERTICAL ||
9192 element == EL_EXPANDABLE_WALL_ANY)
9194 else if (element == EL_FLAMES)
9195 CheckForDragon(x, y);
9197 else if (IS_AUTO_CHANGING(element))
9198 ChangeElement(x, y);
9200 else if (element == EL_EXPLOSION)
9201 ; /* drawing of correct explosion animation is handled separately */
9202 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9203 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9206 /* this may take place after moving, so 'element' may have changed */
9207 if (IS_AUTO_CHANGING(Feld[x][y]))
9208 ChangeElement(x, y);
9211 if (IS_BELT_ACTIVE(element))
9212 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9214 if (game.magic_wall_active)
9216 int jx = local_player->jx, jy = local_player->jy;
9218 /* play the element sound at the position nearest to the player */
9219 if ((element == EL_MAGIC_WALL_FULL ||
9220 element == EL_MAGIC_WALL_ACTIVE ||
9221 element == EL_MAGIC_WALL_EMPTYING ||
9222 element == EL_BD_MAGIC_WALL_FULL ||
9223 element == EL_BD_MAGIC_WALL_ACTIVE ||
9224 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9225 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9233 #if USE_NEW_AMOEBA_CODE
9234 /* new experimental amoeba growth stuff */
9236 if (!(FrameCounter % 8))
9239 static unsigned long random = 1684108901;
9241 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9244 x = (random >> 10) % lev_fieldx;
9245 y = (random >> 20) % lev_fieldy;
9247 x = RND(lev_fieldx);
9248 y = RND(lev_fieldy);
9250 element = Feld[x][y];
9253 if (!IS_PLAYER(x,y) &&
9254 (element == EL_EMPTY ||
9255 CAN_GROW_INTO(element) ||
9256 element == EL_QUICKSAND_EMPTY ||
9257 element == EL_ACID_SPLASH_LEFT ||
9258 element == EL_ACID_SPLASH_RIGHT))
9260 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9261 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9262 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9263 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9264 Feld[x][y] = EL_AMOEBA_DROP;
9267 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9268 if (!IS_PLAYER(x,y) &&
9269 (element == EL_EMPTY ||
9270 element == EL_SAND ||
9271 element == EL_QUICKSAND_EMPTY ||
9272 element == EL_ACID_SPLASH_LEFT ||
9273 element == EL_ACID_SPLASH_RIGHT))
9275 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9276 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9277 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9278 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9279 Feld[x][y] = EL_AMOEBA_DROP;
9283 random = random * 129 + 1;
9289 if (game.explosions_delayed)
9292 game.explosions_delayed = FALSE;
9294 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9296 element = Feld[x][y];
9298 if (ExplodeField[x][y])
9299 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9300 else if (element == EL_EXPLOSION)
9301 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9303 ExplodeField[x][y] = EX_TYPE_NONE;
9306 game.explosions_delayed = TRUE;
9309 if (game.magic_wall_active)
9311 if (!(game.magic_wall_time_left % 4))
9313 int element = Feld[magic_wall_x][magic_wall_y];
9315 if (element == EL_BD_MAGIC_WALL_FULL ||
9316 element == EL_BD_MAGIC_WALL_ACTIVE ||
9317 element == EL_BD_MAGIC_WALL_EMPTYING)
9318 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9320 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9323 if (game.magic_wall_time_left > 0)
9325 game.magic_wall_time_left--;
9326 if (!game.magic_wall_time_left)
9328 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9330 element = Feld[x][y];
9332 if (element == EL_MAGIC_WALL_ACTIVE ||
9333 element == EL_MAGIC_WALL_FULL)
9335 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9336 DrawLevelField(x, y);
9338 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9339 element == EL_BD_MAGIC_WALL_FULL)
9341 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9342 DrawLevelField(x, y);
9346 game.magic_wall_active = FALSE;
9351 if (game.light_time_left > 0)
9353 game.light_time_left--;
9355 if (game.light_time_left == 0)
9356 RedrawAllLightSwitchesAndInvisibleElements();
9359 if (game.timegate_time_left > 0)
9361 game.timegate_time_left--;
9363 if (game.timegate_time_left == 0)
9364 CloseAllOpenTimegates();
9367 for (i = 0; i < MAX_PLAYERS; i++)
9369 struct PlayerInfo *player = &stored_player[i];
9371 if (SHIELD_ON(player))
9373 if (player->shield_deadly_time_left)
9374 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9375 else if (player->shield_normal_time_left)
9376 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9380 if (TimeFrames >= FRAMES_PER_SECOND)
9385 for (i = 0; i < MAX_PLAYERS; i++)
9387 struct PlayerInfo *player = &stored_player[i];
9389 if (SHIELD_ON(player))
9391 player->shield_normal_time_left--;
9393 if (player->shield_deadly_time_left > 0)
9394 player->shield_deadly_time_left--;
9398 if (!level.use_step_counter)
9406 if (TimeLeft <= 10 && setup.time_limit)
9407 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9409 DrawGameValue_Time(TimeLeft);
9411 if (!TimeLeft && setup.time_limit)
9412 for (i = 0; i < MAX_PLAYERS; i++)
9413 KillHero(&stored_player[i]);
9415 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9416 DrawGameValue_Time(TimePlayed);
9419 if (tape.recording || tape.playing)
9420 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9424 PlayAllPlayersSound();
9426 if (options.debug) /* calculate frames per second */
9428 static unsigned long fps_counter = 0;
9429 static int fps_frames = 0;
9430 unsigned long fps_delay_ms = Counter() - fps_counter;
9434 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9436 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9439 fps_counter = Counter();
9442 redraw_mask |= REDRAW_FPS;
9446 if (stored_player[0].jx != stored_player[0].last_jx ||
9447 stored_player[0].jy != stored_player[0].last_jy)
9448 printf("::: %d, %d, %d, %d, %d\n",
9449 stored_player[0].MovDir,
9450 stored_player[0].MovPos,
9451 stored_player[0].GfxPos,
9452 stored_player[0].Frame,
9453 stored_player[0].StepFrame);
9456 #if USE_NEW_MOVE_DELAY
9457 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9462 for (i = 0; i < MAX_PLAYERS; i++)
9465 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9467 stored_player[i].Frame += move_frames;
9469 if (stored_player[i].MovPos != 0)
9470 stored_player[i].StepFrame += move_frames;
9472 #if USE_NEW_MOVE_DELAY
9473 if (stored_player[i].move_delay > 0)
9474 stored_player[i].move_delay--;
9477 if (stored_player[i].drop_delay > 0)
9478 stored_player[i].drop_delay--;
9483 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9485 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9487 local_player->show_envelope = 0;
9491 #if USE_NEW_RANDOMIZE
9492 /* use random number generator in every frame to make it less predictable */
9493 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9498 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9500 int min_x = x, min_y = y, max_x = x, max_y = y;
9503 for (i = 0; i < MAX_PLAYERS; i++)
9505 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9507 if (!stored_player[i].active || &stored_player[i] == player)
9510 min_x = MIN(min_x, jx);
9511 min_y = MIN(min_y, jy);
9512 max_x = MAX(max_x, jx);
9513 max_y = MAX(max_y, jy);
9516 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9519 static boolean AllPlayersInVisibleScreen()
9523 for (i = 0; i < MAX_PLAYERS; i++)
9525 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9527 if (!stored_player[i].active)
9530 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9537 void ScrollLevel(int dx, int dy)
9539 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9542 BlitBitmap(drawto_field, drawto_field,
9543 FX + TILEX * (dx == -1) - softscroll_offset,
9544 FY + TILEY * (dy == -1) - softscroll_offset,
9545 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9546 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9547 FX + TILEX * (dx == 1) - softscroll_offset,
9548 FY + TILEY * (dy == 1) - softscroll_offset);
9552 x = (dx == 1 ? BX1 : BX2);
9553 for (y = BY1; y <= BY2; y++)
9554 DrawScreenField(x, y);
9559 y = (dy == 1 ? BY1 : BY2);
9560 for (x = BX1; x <= BX2; x++)
9561 DrawScreenField(x, y);
9564 redraw_mask |= REDRAW_FIELD;
9568 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9570 int nextx = x + dx, nexty = y + dy;
9571 int element = Feld[x][y];
9574 element != EL_SP_PORT_LEFT &&
9575 element != EL_SP_GRAVITY_PORT_LEFT &&
9576 element != EL_SP_PORT_HORIZONTAL &&
9577 element != EL_SP_PORT_ANY) ||
9579 element != EL_SP_PORT_RIGHT &&
9580 element != EL_SP_GRAVITY_PORT_RIGHT &&
9581 element != EL_SP_PORT_HORIZONTAL &&
9582 element != EL_SP_PORT_ANY) ||
9584 element != EL_SP_PORT_UP &&
9585 element != EL_SP_GRAVITY_PORT_UP &&
9586 element != EL_SP_PORT_VERTICAL &&
9587 element != EL_SP_PORT_ANY) ||
9589 element != EL_SP_PORT_DOWN &&
9590 element != EL_SP_GRAVITY_PORT_DOWN &&
9591 element != EL_SP_PORT_VERTICAL &&
9592 element != EL_SP_PORT_ANY) ||
9593 !IN_LEV_FIELD(nextx, nexty) ||
9594 !IS_FREE(nextx, nexty))
9601 static boolean canFallDown(struct PlayerInfo *player)
9603 int jx = player->jx, jy = player->jy;
9605 return (IN_LEV_FIELD(jx, jy + 1) &&
9606 (IS_FREE(jx, jy + 1) ||
9607 #if USE_NEW_BLOCK_STYLE
9608 #if USE_GRAVITY_BUGFIX_OLD
9609 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9612 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9613 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9614 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9617 static boolean canPassField(int x, int y, int move_dir)
9619 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9620 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9621 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9624 int element = Feld[x][y];
9626 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9627 !CAN_MOVE(element) &&
9628 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9629 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9630 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9633 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9635 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9636 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9637 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9641 int nextx = newx + dx;
9642 int nexty = newy + dy;
9646 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9647 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9649 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9651 (IS_DIGGABLE(Feld[newx][newy]) ||
9652 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9653 canPassField(newx, newy, move_dir)));
9656 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9657 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9658 (IS_DIGGABLE(Feld[newx][newy]) ||
9659 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9660 canPassField(newx, newy, move_dir)));
9663 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9664 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9665 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9666 canPassField(newx, newy, move_dir)));
9668 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9669 (IS_DIGGABLE(Feld[newx][newy]) ||
9670 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9671 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9672 !CAN_MOVE(Feld[newx][newy]) &&
9673 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9674 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9675 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9681 static void CheckGravityMovement(struct PlayerInfo *player)
9683 if (game.gravity && !player->programmed_action)
9686 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9687 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9689 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9690 int move_dir_vertical = player->action & MV_VERTICAL;
9694 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9696 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9699 int jx = player->jx, jy = player->jy;
9701 boolean player_is_moving_to_valid_field =
9702 (!player_is_snapping &&
9703 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9704 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9708 (player->last_move_dir & MV_HORIZONTAL ?
9709 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9710 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9714 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9715 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9716 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9717 int new_jx = jx + dx, new_jy = jy + dy;
9718 int nextx = new_jx + dx, nexty = new_jy + dy;
9724 boolean player_can_fall_down = canFallDown(player);
9726 boolean player_can_fall_down =
9727 (IN_LEV_FIELD(jx, jy + 1) &&
9728 (IS_FREE(jx, jy + 1) ||
9729 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9733 boolean player_can_fall_down =
9734 (IN_LEV_FIELD(jx, jy + 1) &&
9735 (IS_FREE(jx, jy + 1)));
9739 boolean player_is_moving_to_valid_field =
9742 !player_is_snapping &&
9746 IN_LEV_FIELD(new_jx, new_jy) &&
9747 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9748 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9749 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9750 IN_LEV_FIELD(nextx, nexty) &&
9751 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9753 IN_LEV_FIELD(new_jx, new_jy) &&
9754 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9755 Feld[new_jx][new_jy] == EL_SAND ||
9756 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9757 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9758 /* !!! extend EL_SAND to anything diggable !!! */
9764 boolean player_is_standing_on_valid_field =
9765 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9766 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9770 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9771 player_can_fall_down,
9772 player_is_standing_on_valid_field,
9773 player_is_moving_to_valid_field,
9774 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9775 player->effective_action,
9776 player->can_fall_into_acid);
9779 if (player_can_fall_down &&
9781 !player_is_standing_on_valid_field &&
9783 !player_is_moving_to_valid_field)
9786 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9787 jx, jy, FrameCounter);
9790 player->programmed_action = MV_DOWN;
9795 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9798 return CheckGravityMovement(player);
9801 if (game.gravity && !player->programmed_action)
9803 int jx = player->jx, jy = player->jy;
9804 boolean field_under_player_is_free =
9805 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9806 boolean player_is_standing_on_valid_field =
9807 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9808 (IS_WALKABLE(Feld[jx][jy]) &&
9809 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9811 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9812 player->programmed_action = MV_DOWN;
9818 -----------------------------------------------------------------------------
9819 dx, dy: direction (non-diagonal) to try to move the player to
9820 real_dx, real_dy: direction as read from input device (can be diagonal)
9823 boolean MovePlayerOneStep(struct PlayerInfo *player,
9824 int dx, int dy, int real_dx, int real_dy)
9827 static int trigger_sides[4][2] =
9829 /* enter side leave side */
9830 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9831 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9832 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9833 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9835 int move_direction = (dx == -1 ? MV_LEFT :
9836 dx == +1 ? MV_RIGHT :
9838 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9839 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9840 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9842 int jx = player->jx, jy = player->jy;
9843 int new_jx = jx + dx, new_jy = jy + dy;
9847 if (!player->active || (!dx && !dy))
9848 return MF_NO_ACTION;
9850 player->MovDir = (dx < 0 ? MV_LEFT :
9853 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9855 if (!IN_LEV_FIELD(new_jx, new_jy))
9856 return MF_NO_ACTION;
9858 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9859 return MF_NO_ACTION;
9862 element = MovingOrBlocked2Element(new_jx, new_jy);
9864 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9867 if (DONT_RUN_INTO(element))
9869 if (element == EL_ACID && dx == 0 && dy == 1)
9871 SplashAcid(new_jx, new_jy);
9872 Feld[jx][jy] = EL_PLAYER_1;
9873 InitMovingField(jx, jy, MV_DOWN);
9874 Store[jx][jy] = EL_ACID;
9875 ContinueMoving(jx, jy);
9879 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9884 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9885 if (can_move != MF_MOVING)
9888 /* check if DigField() has caused relocation of the player */
9889 if (player->jx != jx || player->jy != jy)
9890 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9892 StorePlayer[jx][jy] = 0;
9893 player->last_jx = jx;
9894 player->last_jy = jy;
9895 player->jx = new_jx;
9896 player->jy = new_jy;
9897 StorePlayer[new_jx][new_jy] = player->element_nr;
9900 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9902 player->step_counter++;
9905 player->drop_delay = 0;
9908 PlayerVisit[jx][jy] = FrameCounter;
9910 ScrollPlayer(player, SCROLL_INIT);
9913 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9915 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9917 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9920 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9922 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9923 CE_PLAYER_ENTERS_X, enter_side);
9924 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9925 CE_ENTERED_BY_PLAYER, enter_side);
9932 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9934 int jx = player->jx, jy = player->jy;
9935 int old_jx = jx, old_jy = jy;
9936 int moved = MF_NO_ACTION;
9939 if (!player->active)
9944 if (player->MovPos == 0)
9946 player->is_moving = FALSE;
9947 player->is_digging = FALSE;
9948 player->is_collecting = FALSE;
9949 player->is_snapping = FALSE;
9950 player->is_pushing = FALSE;
9956 if (!player->active || (!dx && !dy))
9961 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9969 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9970 player->move_delay + player->move_delay_value);
9973 #if USE_NEW_MOVE_DELAY
9974 if (player->move_delay > 0)
9976 if (!FrameReached(&player->move_delay, player->move_delay_value))
9980 printf("::: can NOT move\n");
9986 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9987 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9994 printf("::: COULD move now\n");
9997 #if USE_NEW_MOVE_DELAY
9998 player->move_delay = -1; /* set to "uninitialized" value */
10001 /* store if player is automatically moved to next field */
10002 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10004 /* remove the last programmed player action */
10005 player->programmed_action = 0;
10007 if (player->MovPos)
10009 /* should only happen if pre-1.2 tape recordings are played */
10010 /* this is only for backward compatibility */
10012 int original_move_delay_value = player->move_delay_value;
10015 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10019 /* scroll remaining steps with finest movement resolution */
10020 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10022 while (player->MovPos)
10024 ScrollPlayer(player, SCROLL_GO_ON);
10025 ScrollScreen(NULL, SCROLL_GO_ON);
10027 #if USE_NEW_MOVE_DELAY
10028 AdvanceFrameAndPlayerCounters(player->index_nr);
10037 player->move_delay_value = original_move_delay_value;
10040 if (player->last_move_dir & MV_HORIZONTAL)
10042 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10043 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10047 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10048 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10054 if (moved & MF_MOVING && !ScreenMovPos &&
10055 (player == local_player || !options.network))
10057 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10058 int offset = (setup.scroll_delay ? 3 : 0);
10060 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10062 /* actual player has left the screen -- scroll in that direction */
10063 if (jx != old_jx) /* player has moved horizontally */
10064 scroll_x += (jx - old_jx);
10065 else /* player has moved vertically */
10066 scroll_y += (jy - old_jy);
10070 if (jx != old_jx) /* player has moved horizontally */
10072 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10073 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10074 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10076 /* don't scroll over playfield boundaries */
10077 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10078 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10080 /* don't scroll more than one field at a time */
10081 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10083 /* don't scroll against the player's moving direction */
10084 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10085 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10086 scroll_x = old_scroll_x;
10088 else /* player has moved vertically */
10090 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10091 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10092 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10094 /* don't scroll over playfield boundaries */
10095 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10096 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10098 /* don't scroll more than one field at a time */
10099 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10101 /* don't scroll against the player's moving direction */
10102 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10103 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10104 scroll_y = old_scroll_y;
10108 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10110 if (!options.network && !AllPlayersInVisibleScreen())
10112 scroll_x = old_scroll_x;
10113 scroll_y = old_scroll_y;
10117 ScrollScreen(player, SCROLL_INIT);
10118 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10125 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10127 if (!(moved & MF_MOVING) && !player->is_pushing)
10132 player->StepFrame = 0;
10134 if (moved & MF_MOVING)
10137 printf("::: REALLY moves now\n");
10140 if (old_jx != jx && old_jy == jy)
10141 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10142 else if (old_jx == jx && old_jy != jy)
10143 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10145 DrawLevelField(jx, jy); /* for "crumbled sand" */
10147 player->last_move_dir = player->MovDir;
10148 player->is_moving = TRUE;
10150 player->is_snapping = FALSE;
10154 player->is_switching = FALSE;
10157 player->is_dropping = FALSE;
10161 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10164 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10167 int move_direction = player->MovDir;
10169 int enter_side = MV_DIR_OPPOSITE(move_direction);
10170 int leave_side = move_direction;
10172 static int trigger_sides[4][2] =
10174 /* enter side leave side */
10175 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10176 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10177 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10178 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10180 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10181 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10183 int old_element = Feld[old_jx][old_jy];
10184 int new_element = Feld[jx][jy];
10187 /* !!! TEST ONLY !!! */
10188 if (IS_CUSTOM_ELEMENT(old_element))
10189 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10191 player->index_bit, leave_side);
10193 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10194 CE_PLAYER_LEAVES_X,
10195 player->index_bit, leave_side);
10197 if (IS_CUSTOM_ELEMENT(new_element))
10198 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10199 player->index_bit, enter_side);
10201 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10202 CE_PLAYER_ENTERS_X,
10203 player->index_bit, enter_side);
10213 CheckGravityMovementWhenNotMoving(player);
10216 player->last_move_dir = MV_NO_MOVING;
10218 player->is_moving = FALSE;
10220 #if USE_NEW_MOVE_STYLE
10221 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10222 /* ensure that the player is also allowed to move in the next frame */
10223 /* (currently, the player is forced to wait eight frames before he can try
10226 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10227 player->move_delay = 0; /* allow direct movement in the next frame */
10231 #if USE_NEW_MOVE_DELAY
10232 if (player->move_delay == -1) /* not yet initialized by DigField() */
10233 player->move_delay = player->move_delay_value;
10236 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10238 TestIfHeroTouchesBadThing(jx, jy);
10239 TestIfPlayerTouchesCustomElement(jx, jy);
10242 if (!player->active)
10243 RemoveHero(player);
10248 void ScrollPlayer(struct PlayerInfo *player, int mode)
10250 int jx = player->jx, jy = player->jy;
10251 int last_jx = player->last_jx, last_jy = player->last_jy;
10252 int move_stepsize = TILEX / player->move_delay_value;
10254 if (!player->active || !player->MovPos)
10257 if (mode == SCROLL_INIT)
10259 player->actual_frame_counter = FrameCounter;
10260 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10263 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10265 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10266 player->block_delay);
10269 #if USE_NEW_BLOCK_STYLE
10272 if (player->block_delay <= 0)
10273 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10276 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10277 Feld[last_jx][last_jy] == EL_EMPTY)
10279 int last_field_block_delay = 0; /* start with no blocking at all */
10280 int block_delay_adjustment = player->block_delay_adjustment;
10282 /* if player blocks last field, add delay for exactly one move */
10283 if (player->block_last_field)
10285 last_field_block_delay += player->move_delay_value;
10287 #if USE_GRAVITY_BUGFIX_NEW
10288 /* when blocking enabled, prevent moving up despite gravity */
10289 if (game.gravity && player->MovDir == MV_UP)
10290 block_delay_adjustment = -1;
10294 /* add block delay adjustment (also possible when not blocking) */
10295 last_field_block_delay += block_delay_adjustment;
10298 #if USE_BLOCK_DELAY_BUGFIX
10299 /* when blocking enabled, correct block delay for fast movement */
10300 if (player->block_last_field &&
10301 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10302 last_field_block_delay =
10303 player->move_delay_value + player->block_delay_adjustment;
10308 #if USE_GRAVITY_BUGFIX_NEW
10309 /* when blocking enabled, correct block delay for gravity movement */
10310 if (player->block_last_field &&
10311 game.gravity && player->MovDir == MV_UP)
10312 last_field_block_delay = player->move_delay_value - 1;
10316 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10317 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10320 #if USE_NEW_MOVE_STYLE
10321 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10322 player->block_last_field) &&
10323 Feld[last_jx][last_jy] == EL_EMPTY)
10324 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10326 if (Feld[last_jx][last_jy] == EL_EMPTY)
10327 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10332 DrawPlayer(player);
10337 else if (!FrameReached(&player->actual_frame_counter, 1))
10340 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10341 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10343 #if USE_NEW_BLOCK_STYLE
10345 if (!player->block_last_field &&
10346 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10348 RemoveField(last_jx, last_jy);
10350 Feld[last_jx][last_jy] = EL_EMPTY;
10354 /* before DrawPlayer() to draw correct player graphic for this case */
10355 if (player->MovPos == 0)
10356 CheckGravityMovement(player);
10359 DrawPlayer(player); /* needed here only to cleanup last field */
10362 if (player->MovPos == 0) /* player reached destination field */
10365 if (player->move_delay_reset_counter > 0)
10367 player->move_delay_reset_counter--;
10369 if (player->move_delay_reset_counter == 0)
10371 /* continue with normal speed after quickly moving through gate */
10372 HALVE_PLAYER_SPEED(player);
10374 /* be able to make the next move without delay */
10375 player->move_delay = 0;
10379 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10381 /* continue with normal speed after quickly moving through gate */
10382 HALVE_PLAYER_SPEED(player);
10384 /* be able to make the next move without delay */
10385 player->move_delay = 0;
10389 #if USE_NEW_BLOCK_STYLE
10391 if (player->block_last_field &&
10392 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10394 RemoveField(last_jx, last_jy);
10396 Feld[last_jx][last_jy] = EL_EMPTY;
10400 player->last_jx = jx;
10401 player->last_jy = jy;
10403 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10404 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10405 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10407 DrawPlayer(player); /* needed here only to cleanup last field */
10408 RemoveHero(player);
10410 if (local_player->friends_still_needed == 0 ||
10411 IS_SP_ELEMENT(Feld[jx][jy]))
10412 player->LevelSolved = player->GameOver = TRUE;
10416 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10417 /* this breaks one level: "machine", level 000 */
10419 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10422 int move_direction = player->MovDir;
10424 int enter_side = MV_DIR_OPPOSITE(move_direction);
10425 int leave_side = move_direction;
10427 static int trigger_sides[4][2] =
10429 /* enter side leave side */
10430 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10431 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10432 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10433 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10435 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10436 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10438 int old_jx = last_jx;
10439 int old_jy = last_jy;
10440 int old_element = Feld[old_jx][old_jy];
10441 int new_element = Feld[jx][jy];
10444 /* !!! TEST ONLY !!! */
10445 if (IS_CUSTOM_ELEMENT(old_element))
10446 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10448 player->index_bit, leave_side);
10450 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10451 CE_PLAYER_LEAVES_X,
10452 player->index_bit, leave_side);
10454 if (IS_CUSTOM_ELEMENT(new_element))
10455 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10456 player->index_bit, enter_side);
10458 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10459 CE_PLAYER_ENTERS_X,
10460 player->index_bit, enter_side);
10466 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10468 TestIfHeroTouchesBadThing(jx, jy);
10469 TestIfPlayerTouchesCustomElement(jx, jy);
10472 /* needed because pushed element has not yet reached its destination,
10473 so it would trigger a change event at its previous field location */
10474 if (!player->is_pushing)
10476 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10479 if (!player->active)
10480 RemoveHero(player);
10483 if (level.use_step_counter)
10493 if (TimeLeft <= 10 && setup.time_limit)
10494 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10496 DrawGameValue_Time(TimeLeft);
10498 if (!TimeLeft && setup.time_limit)
10499 for (i = 0; i < MAX_PLAYERS; i++)
10500 KillHero(&stored_player[i]);
10502 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10503 DrawGameValue_Time(TimePlayed);
10506 if (tape.single_step && tape.recording && !tape.pausing &&
10507 !player->programmed_action)
10508 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10512 void ScrollScreen(struct PlayerInfo *player, int mode)
10514 static unsigned long screen_frame_counter = 0;
10516 if (mode == SCROLL_INIT)
10518 /* set scrolling step size according to actual player's moving speed */
10519 ScrollStepSize = TILEX / player->move_delay_value;
10521 screen_frame_counter = FrameCounter;
10522 ScreenMovDir = player->MovDir;
10523 ScreenMovPos = player->MovPos;
10524 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10527 else if (!FrameReached(&screen_frame_counter, 1))
10532 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10533 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10534 redraw_mask |= REDRAW_FIELD;
10537 ScreenMovDir = MV_NO_MOVING;
10540 void TestIfPlayerTouchesCustomElement(int x, int y)
10542 static int xy[4][2] =
10549 static int trigger_sides[4][2] =
10551 /* center side border side */
10552 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10553 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10554 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10555 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10557 static int touch_dir[4] =
10559 MV_LEFT | MV_RIGHT,
10564 int center_element = Feld[x][y]; /* should always be non-moving! */
10567 for (i = 0; i < NUM_DIRECTIONS; i++)
10569 int xx = x + xy[i][0];
10570 int yy = y + xy[i][1];
10571 int center_side = trigger_sides[i][0];
10572 int border_side = trigger_sides[i][1];
10573 int border_element;
10575 if (!IN_LEV_FIELD(xx, yy))
10578 if (IS_PLAYER(x, y))
10580 struct PlayerInfo *player = PLAYERINFO(x, y);
10582 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10583 border_element = Feld[xx][yy]; /* may be moving! */
10584 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10585 border_element = Feld[xx][yy];
10586 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10587 border_element = MovingOrBlocked2Element(xx, yy);
10589 continue; /* center and border element do not touch */
10592 /* !!! TEST ONLY !!! */
10593 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10594 player->index_bit, border_side);
10595 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10596 CE_PLAYER_TOUCHES_X,
10597 player->index_bit, border_side);
10599 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10600 CE_PLAYER_TOUCHES_X,
10601 player->index_bit, border_side);
10602 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10603 player->index_bit, border_side);
10606 else if (IS_PLAYER(xx, yy))
10608 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10610 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10612 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10613 continue; /* center and border element do not touch */
10617 /* !!! TEST ONLY !!! */
10618 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10619 player->index_bit, center_side);
10620 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10621 CE_PLAYER_TOUCHES_X,
10622 player->index_bit, center_side);
10624 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10625 CE_PLAYER_TOUCHES_X,
10626 player->index_bit, center_side);
10627 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10628 player->index_bit, center_side);
10636 void TestIfElementTouchesCustomElement(int x, int y)
10638 static int xy[4][2] =
10645 static int trigger_sides[4][2] =
10647 /* center side border side */
10648 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10649 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10650 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10651 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10653 static int touch_dir[4] =
10655 MV_LEFT | MV_RIGHT,
10660 boolean change_center_element = FALSE;
10661 int center_element_change_page = 0;
10662 int center_element = Feld[x][y]; /* should always be non-moving! */
10663 int border_trigger_element = EL_UNDEFINED;
10666 for (i = 0; i < NUM_DIRECTIONS; i++)
10668 int xx = x + xy[i][0];
10669 int yy = y + xy[i][1];
10670 int center_side = trigger_sides[i][0];
10671 int border_side = trigger_sides[i][1];
10672 int border_element;
10674 if (!IN_LEV_FIELD(xx, yy))
10677 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10678 border_element = Feld[xx][yy]; /* may be moving! */
10679 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10680 border_element = Feld[xx][yy];
10681 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10682 border_element = MovingOrBlocked2Element(xx, yy);
10684 continue; /* center and border element do not touch */
10686 /* check for change of center element (but change it only once) */
10687 if (IS_CUSTOM_ELEMENT(center_element) &&
10688 HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10689 !change_center_element)
10691 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10693 struct ElementChangeInfo *change =
10694 &element_info[center_element].change_page[j];
10696 if (change->can_change &&
10697 change->has_event[CE_TOUCHING_X] &&
10698 change->trigger_side & border_side &&
10700 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10702 change->trigger_element == border_element
10706 change_center_element = TRUE;
10707 center_element_change_page = j;
10708 border_trigger_element = border_element;
10715 /* check for change of border element */
10716 if (IS_CUSTOM_ELEMENT(border_element) &&
10717 HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10719 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10721 struct ElementChangeInfo *change =
10722 &element_info[border_element].change_page[j];
10724 if (change->can_change &&
10725 change->has_event[CE_TOUCHING_X] &&
10726 change->trigger_side & center_side &&
10728 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10730 change->trigger_element == center_element
10735 printf("::: border_element %d, %d\n", x, y);
10738 CheckElementChangeByPage(xx, yy, border_element, center_element,
10746 if (change_center_element)
10749 printf("::: center_element %d, %d\n", x, y);
10752 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10753 CE_TOUCHING_X, center_element_change_page);
10757 void TestIfElementHitsCustomElement(int x, int y, int direction)
10759 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10760 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10761 int hitx = x + dx, hity = y + dy;
10762 int hitting_element = Feld[x][y];
10763 int touched_element;
10765 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10766 !IS_FREE(hitx, hity) &&
10767 (!IS_MOVING(hitx, hity) ||
10768 MovDir[hitx][hity] != direction ||
10769 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10772 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10776 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10780 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10781 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10783 #if !USE_HITTING_SOMETHING_BUGFIX
10784 /* "hitting something" is also true when hitting the playfield border */
10785 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10786 CE_HITTING_SOMETHING, direction);
10789 if (IN_LEV_FIELD(hitx, hity))
10791 int opposite_direction = MV_DIR_OPPOSITE(direction);
10792 int hitting_side = direction;
10793 int touched_side = opposite_direction;
10795 int touched_element = MovingOrBlocked2Element(hitx, hity);
10798 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10799 MovDir[hitx][hity] != direction ||
10800 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10809 #if !USE_HIT_BY_SOMETHING_BUGFIX
10810 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10811 CE_HIT_BY_SOMETHING, opposite_direction);
10814 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10815 HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10817 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10819 struct ElementChangeInfo *change =
10820 &element_info[hitting_element].change_page[i];
10822 if (change->can_change &&
10823 change->has_event[CE_HITTING_X] &&
10824 change->trigger_side & touched_side &&
10827 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10829 change->trigger_element == touched_element
10833 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10840 if (IS_CUSTOM_ELEMENT(touched_element) &&
10841 HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10843 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10845 struct ElementChangeInfo *change =
10846 &element_info[touched_element].change_page[i];
10848 if (change->can_change &&
10849 change->has_event[CE_HIT_BY_X] &&
10850 change->trigger_side & hitting_side &&
10852 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10854 change->trigger_element == hitting_element
10858 CheckElementChangeByPage(hitx, hity, touched_element,
10859 hitting_element, CE_HIT_BY_X, i);
10865 #if USE_HIT_BY_SOMETHING_BUGFIX
10866 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10867 CE_HIT_BY_SOMETHING, opposite_direction);
10872 #if USE_HITTING_SOMETHING_BUGFIX
10873 /* "hitting something" is also true when hitting the playfield border */
10874 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10875 CE_HITTING_SOMETHING, direction);
10880 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10882 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10883 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10884 int hitx = x + dx, hity = y + dy;
10885 int hitting_element = Feld[x][y];
10886 int touched_element;
10888 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10889 !IS_FREE(hitx, hity) &&
10890 (!IS_MOVING(hitx, hity) ||
10891 MovDir[hitx][hity] != direction ||
10892 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10895 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10899 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10903 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10904 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10906 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10907 EP_CAN_SMASH_EVERYTHING, direction);
10909 if (IN_LEV_FIELD(hitx, hity))
10911 int opposite_direction = MV_DIR_OPPOSITE(direction);
10912 int hitting_side = direction;
10913 int touched_side = opposite_direction;
10915 int touched_element = MovingOrBlocked2Element(hitx, hity);
10918 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10919 MovDir[hitx][hity] != direction ||
10920 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10929 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10930 CE_SMASHED_BY_SOMETHING, opposite_direction);
10932 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10933 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10935 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10937 struct ElementChangeInfo *change =
10938 &element_info[hitting_element].change_page[i];
10940 if (change->can_change &&
10941 change->has_event[CE_OTHER_IS_SMASHING] &&
10942 change->trigger_side & touched_side &&
10945 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10947 change->trigger_element == touched_element
10951 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10952 CE_OTHER_IS_SMASHING, i);
10958 if (IS_CUSTOM_ELEMENT(touched_element) &&
10959 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10961 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10963 struct ElementChangeInfo *change =
10964 &element_info[touched_element].change_page[i];
10966 if (change->can_change &&
10967 change->has_event[CE_OTHER_GETS_SMASHED] &&
10968 change->trigger_side & hitting_side &&
10970 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10972 change->trigger_element == hitting_element
10976 CheckElementChangeByPage(hitx, hity, touched_element,
10977 hitting_element, CE_OTHER_GETS_SMASHED,i);
10987 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10989 int i, kill_x = -1, kill_y = -1;
10990 int bad_element = -1;
10991 static int test_xy[4][2] =
10998 static int test_dir[4] =
11006 for (i = 0; i < NUM_DIRECTIONS; i++)
11008 int test_x, test_y, test_move_dir, test_element;
11010 test_x = good_x + test_xy[i][0];
11011 test_y = good_y + test_xy[i][1];
11013 if (!IN_LEV_FIELD(test_x, test_y))
11017 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11020 test_element = Feld[test_x][test_y];
11022 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11025 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11026 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11028 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11029 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11033 bad_element = test_element;
11039 if (kill_x != -1 || kill_y != -1)
11041 if (IS_PLAYER(good_x, good_y))
11043 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11046 if (player->shield_deadly_time_left > 0 &&
11047 !IS_INDESTRUCTIBLE(bad_element))
11048 Bang(kill_x, kill_y);
11049 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11052 if (player->shield_deadly_time_left > 0)
11053 Bang(kill_x, kill_y);
11054 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11059 Bang(good_x, good_y);
11063 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11065 int i, kill_x = -1, kill_y = -1;
11066 int bad_element = Feld[bad_x][bad_y];
11067 static int test_xy[4][2] =
11074 static int touch_dir[4] =
11076 MV_LEFT | MV_RIGHT,
11081 static int test_dir[4] =
11089 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11092 for (i = 0; i < NUM_DIRECTIONS; i++)
11094 int test_x, test_y, test_move_dir, test_element;
11096 test_x = bad_x + test_xy[i][0];
11097 test_y = bad_y + test_xy[i][1];
11098 if (!IN_LEV_FIELD(test_x, test_y))
11102 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11104 test_element = Feld[test_x][test_y];
11106 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11107 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11109 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11110 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11112 /* good thing is player or penguin that does not move away */
11113 if (IS_PLAYER(test_x, test_y))
11115 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11117 if (bad_element == EL_ROBOT && player->is_moving)
11118 continue; /* robot does not kill player if he is moving */
11120 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11122 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11123 continue; /* center and border element do not touch */
11130 else if (test_element == EL_PENGUIN)
11139 if (kill_x != -1 || kill_y != -1)
11141 if (IS_PLAYER(kill_x, kill_y))
11143 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11146 if (player->shield_deadly_time_left > 0 &&
11147 !IS_INDESTRUCTIBLE(bad_element))
11148 Bang(bad_x, bad_y);
11149 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11152 if (player->shield_deadly_time_left > 0)
11153 Bang(bad_x, bad_y);
11154 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11159 Bang(kill_x, kill_y);
11163 void TestIfHeroTouchesBadThing(int x, int y)
11165 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11168 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11170 TestIfGoodThingHitsBadThing(x, y, move_dir);
11173 void TestIfBadThingTouchesHero(int x, int y)
11175 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11178 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11180 TestIfBadThingHitsGoodThing(x, y, move_dir);
11183 void TestIfFriendTouchesBadThing(int x, int y)
11185 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11188 void TestIfBadThingTouchesFriend(int x, int y)
11190 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11193 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11195 int i, kill_x = bad_x, kill_y = bad_y;
11196 static int xy[4][2] =
11204 for (i = 0; i < NUM_DIRECTIONS; i++)
11208 x = bad_x + xy[i][0];
11209 y = bad_y + xy[i][1];
11210 if (!IN_LEV_FIELD(x, y))
11213 element = Feld[x][y];
11214 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11215 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11223 if (kill_x != bad_x || kill_y != bad_y)
11224 Bang(bad_x, bad_y);
11227 void KillHero(struct PlayerInfo *player)
11229 int jx = player->jx, jy = player->jy;
11231 if (!player->active)
11234 /* remove accessible field at the player's position */
11235 Feld[jx][jy] = EL_EMPTY;
11237 /* deactivate shield (else Bang()/Explode() would not work right) */
11238 player->shield_normal_time_left = 0;
11239 player->shield_deadly_time_left = 0;
11245 static void KillHeroUnlessEnemyProtected(int x, int y)
11247 if (!PLAYER_ENEMY_PROTECTED(x, y))
11248 KillHero(PLAYERINFO(x, y));
11251 static void KillHeroUnlessExplosionProtected(int x, int y)
11253 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11254 KillHero(PLAYERINFO(x, y));
11257 void BuryHero(struct PlayerInfo *player)
11259 int jx = player->jx, jy = player->jy;
11261 if (!player->active)
11265 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11267 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11269 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11271 player->GameOver = TRUE;
11272 RemoveHero(player);
11275 void RemoveHero(struct PlayerInfo *player)
11277 int jx = player->jx, jy = player->jy;
11278 int i, found = FALSE;
11280 player->present = FALSE;
11281 player->active = FALSE;
11283 if (!ExplodeField[jx][jy])
11284 StorePlayer[jx][jy] = 0;
11286 for (i = 0; i < MAX_PLAYERS; i++)
11287 if (stored_player[i].active)
11291 AllPlayersGone = TRUE;
11298 =============================================================================
11299 checkDiagonalPushing()
11300 -----------------------------------------------------------------------------
11301 check if diagonal input device direction results in pushing of object
11302 (by checking if the alternative direction is walkable, diggable, ...)
11303 =============================================================================
11306 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11307 int x, int y, int real_dx, int real_dy)
11309 int jx, jy, dx, dy, xx, yy;
11311 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11314 /* diagonal direction: check alternative direction */
11319 xx = jx + (dx == 0 ? real_dx : 0);
11320 yy = jy + (dy == 0 ? real_dy : 0);
11322 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11326 =============================================================================
11328 -----------------------------------------------------------------------------
11329 x, y: field next to player (non-diagonal) to try to dig to
11330 real_dx, real_dy: direction as read from input device (can be diagonal)
11331 =============================================================================
11334 int DigField(struct PlayerInfo *player,
11335 int oldx, int oldy, int x, int y,
11336 int real_dx, int real_dy, int mode)
11339 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11341 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11342 boolean player_was_pushing = player->is_pushing;
11343 int jx = oldx, jy = oldy;
11344 int dx = x - jx, dy = y - jy;
11345 int nextx = x + dx, nexty = y + dy;
11346 int move_direction = (dx == -1 ? MV_LEFT :
11347 dx == +1 ? MV_RIGHT :
11349 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11350 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11352 int dig_side = MV_DIR_OPPOSITE(move_direction);
11354 static int trigger_sides[4] =
11356 CH_SIDE_RIGHT, /* moving left */
11357 CH_SIDE_LEFT, /* moving right */
11358 CH_SIDE_BOTTOM, /* moving up */
11359 CH_SIDE_TOP, /* moving down */
11361 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11363 int old_element = Feld[jx][jy];
11366 if (is_player) /* function can also be called by EL_PENGUIN */
11368 if (player->MovPos == 0)
11370 player->is_digging = FALSE;
11371 player->is_collecting = FALSE;
11374 if (player->MovPos == 0) /* last pushing move finished */
11375 player->is_pushing = FALSE;
11377 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11379 player->is_switching = FALSE;
11380 #if USE_NEW_PUSH_DELAY
11381 player->push_delay = -1;
11383 player->push_delay = 0;
11386 return MF_NO_ACTION;
11390 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11391 return MF_NO_ACTION;
11396 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11398 if (IS_TUBE(Feld[jx][jy]) ||
11399 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11403 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11404 int tube_leave_directions[][2] =
11406 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11407 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11408 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11409 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11410 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11411 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11412 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11413 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11414 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11415 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11416 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11417 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11420 while (tube_leave_directions[i][0] != tube_element)
11423 if (tube_leave_directions[i][0] == -1) /* should not happen */
11427 if (!(tube_leave_directions[i][1] & move_direction))
11428 return MF_NO_ACTION; /* tube has no opening in this direction */
11433 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11434 old_element = Back[jx][jy];
11438 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11439 return MF_NO_ACTION; /* field has no opening in this direction */
11441 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11442 return MF_NO_ACTION; /* field has no opening in this direction */
11444 element = Feld[x][y];
11446 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11447 return MF_NO_ACTION;
11449 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11450 game.engine_version >= VERSION_IDENT(2,2,0,0))
11451 return MF_NO_ACTION;
11454 if (game.gravity && is_player && !player->is_auto_moving &&
11455 canFallDown(player) && move_direction != MV_DOWN &&
11456 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11457 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11461 if (element == EL_EMPTY_SPACE &&
11462 game.gravity && !player->is_auto_moving &&
11463 canFallDown(player) && move_direction != MV_DOWN)
11464 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11470 case EL_SP_PORT_LEFT:
11471 case EL_SP_PORT_RIGHT:
11472 case EL_SP_PORT_UP:
11473 case EL_SP_PORT_DOWN:
11474 case EL_SP_PORT_HORIZONTAL:
11475 case EL_SP_PORT_VERTICAL:
11476 case EL_SP_PORT_ANY:
11477 case EL_SP_GRAVITY_PORT_LEFT:
11478 case EL_SP_GRAVITY_PORT_RIGHT:
11479 case EL_SP_GRAVITY_PORT_UP:
11480 case EL_SP_GRAVITY_PORT_DOWN:
11482 if (!canEnterSupaplexPort(x, y, dx, dy))
11483 return MF_NO_ACTION;
11486 element != EL_SP_PORT_LEFT &&
11487 element != EL_SP_GRAVITY_PORT_LEFT &&
11488 element != EL_SP_PORT_HORIZONTAL &&
11489 element != EL_SP_PORT_ANY) ||
11491 element != EL_SP_PORT_RIGHT &&
11492 element != EL_SP_GRAVITY_PORT_RIGHT &&
11493 element != EL_SP_PORT_HORIZONTAL &&
11494 element != EL_SP_PORT_ANY) ||
11496 element != EL_SP_PORT_UP &&
11497 element != EL_SP_GRAVITY_PORT_UP &&
11498 element != EL_SP_PORT_VERTICAL &&
11499 element != EL_SP_PORT_ANY) ||
11501 element != EL_SP_PORT_DOWN &&
11502 element != EL_SP_GRAVITY_PORT_DOWN &&
11503 element != EL_SP_PORT_VERTICAL &&
11504 element != EL_SP_PORT_ANY) ||
11505 !IN_LEV_FIELD(nextx, nexty) ||
11506 !IS_FREE(nextx, nexty))
11507 return MF_NO_ACTION;
11510 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11511 element == EL_SP_GRAVITY_PORT_RIGHT ||
11512 element == EL_SP_GRAVITY_PORT_UP ||
11513 element == EL_SP_GRAVITY_PORT_DOWN)
11514 game.gravity = !game.gravity;
11516 /* automatically move to the next field with double speed */
11517 player->programmed_action = move_direction;
11519 if (player->move_delay_reset_counter == 0)
11521 player->move_delay_reset_counter = 2; /* two double speed steps */
11523 DOUBLE_PLAYER_SPEED(player);
11526 player->move_delay_reset_counter = 2;
11528 DOUBLE_PLAYER_SPEED(player);
11532 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11535 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11541 case EL_TUBE_VERTICAL:
11542 case EL_TUBE_HORIZONTAL:
11543 case EL_TUBE_VERTICAL_LEFT:
11544 case EL_TUBE_VERTICAL_RIGHT:
11545 case EL_TUBE_HORIZONTAL_UP:
11546 case EL_TUBE_HORIZONTAL_DOWN:
11547 case EL_TUBE_LEFT_UP:
11548 case EL_TUBE_LEFT_DOWN:
11549 case EL_TUBE_RIGHT_UP:
11550 case EL_TUBE_RIGHT_DOWN:
11553 int tube_enter_directions[][2] =
11555 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11556 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11557 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11558 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11559 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11560 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11561 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11562 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11563 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11564 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11565 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11566 { -1, MV_NO_MOVING }
11569 while (tube_enter_directions[i][0] != element)
11572 if (tube_enter_directions[i][0] == -1) /* should not happen */
11576 if (!(tube_enter_directions[i][1] & move_direction))
11577 return MF_NO_ACTION; /* tube has no opening in this direction */
11579 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11587 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11589 if (IS_WALKABLE(element))
11592 int sound_element = SND_ELEMENT(element);
11593 int sound_action = ACTION_WALKING;
11596 if (!ACCESS_FROM(element, opposite_direction))
11597 return MF_NO_ACTION; /* field not accessible from this direction */
11601 if (element == EL_EMPTY_SPACE &&
11602 game.gravity && !player->is_auto_moving &&
11603 canFallDown(player) && move_direction != MV_DOWN)
11604 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11607 if (IS_RND_GATE(element))
11609 if (!player->key[RND_GATE_NR(element)])
11610 return MF_NO_ACTION;
11612 else if (IS_RND_GATE_GRAY(element))
11614 if (!player->key[RND_GATE_GRAY_NR(element)])
11615 return MF_NO_ACTION;
11617 else if (element == EL_EXIT_OPEN ||
11618 element == EL_SP_EXIT_OPEN ||
11619 element == EL_SP_EXIT_OPENING)
11621 sound_action = ACTION_PASSING; /* player is passing exit */
11623 else if (element == EL_EMPTY)
11625 sound_action = ACTION_MOVING; /* nothing to walk on */
11628 /* play sound from background or player, whatever is available */
11629 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11630 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11632 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11637 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11639 else if (IS_PASSABLE(element))
11643 if (!canPassField(x, y, move_direction))
11644 return MF_NO_ACTION;
11649 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11650 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11651 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11652 return MF_NO_ACTION;
11654 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11655 return MF_NO_ACTION;
11660 if (!ACCESS_FROM(element, opposite_direction))
11661 return MF_NO_ACTION; /* field not accessible from this direction */
11663 if (IS_CUSTOM_ELEMENT(element) &&
11664 !ACCESS_FROM(element, opposite_direction))
11665 return MF_NO_ACTION; /* field not accessible from this direction */
11669 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11670 return MF_NO_ACTION;
11675 if (IS_EM_GATE(element))
11677 if (!player->key[EM_GATE_NR(element)])
11678 return MF_NO_ACTION;
11680 else if (IS_EM_GATE_GRAY(element))
11682 if (!player->key[EM_GATE_GRAY_NR(element)])
11683 return MF_NO_ACTION;
11685 else if (IS_SP_PORT(element))
11687 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11688 element == EL_SP_GRAVITY_PORT_RIGHT ||
11689 element == EL_SP_GRAVITY_PORT_UP ||
11690 element == EL_SP_GRAVITY_PORT_DOWN)
11691 game.gravity = !game.gravity;
11692 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11693 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11694 element == EL_SP_GRAVITY_ON_PORT_UP ||
11695 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11696 game.gravity = TRUE;
11697 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11698 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11699 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11700 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11701 game.gravity = FALSE;
11704 /* automatically move to the next field with double speed */
11705 player->programmed_action = move_direction;
11707 if (player->move_delay_reset_counter == 0)
11709 player->move_delay_reset_counter = 2; /* two double speed steps */
11711 DOUBLE_PLAYER_SPEED(player);
11714 player->move_delay_reset_counter = 2;
11716 DOUBLE_PLAYER_SPEED(player);
11719 PlayLevelSoundAction(x, y, ACTION_PASSING);
11723 else if (IS_DIGGABLE(element))
11727 if (mode != DF_SNAP)
11730 GfxElement[x][y] = GFX_ELEMENT(element);
11733 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11735 player->is_digging = TRUE;
11738 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11740 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11741 player->index_bit, dig_side);
11744 if (mode == DF_SNAP)
11745 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11750 else if (IS_COLLECTIBLE(element))
11754 if (is_player && mode != DF_SNAP)
11756 GfxElement[x][y] = element;
11757 player->is_collecting = TRUE;
11760 if (element == EL_SPEED_PILL)
11761 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11762 else if (element == EL_EXTRA_TIME && level.time > 0)
11765 DrawGameValue_Time(TimeLeft);
11767 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11769 player->shield_normal_time_left += 10;
11770 if (element == EL_SHIELD_DEADLY)
11771 player->shield_deadly_time_left += 10;
11773 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11775 if (player->inventory_size < MAX_INVENTORY_SIZE)
11776 player->inventory_element[player->inventory_size++] = element;
11778 DrawGameValue_Dynamite(local_player->inventory_size);
11780 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11782 player->dynabomb_count++;
11783 player->dynabombs_left++;
11785 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11787 player->dynabomb_size++;
11789 else if (element == EL_DYNABOMB_INCREASE_POWER)
11791 player->dynabomb_xl = TRUE;
11793 else if (IS_KEY(element))
11795 player->key[KEY_NR(element)] = TRUE;
11797 DrawGameValue_Keys(player->key);
11799 redraw_mask |= REDRAW_DOOR_1;
11801 else if (IS_ENVELOPE(element))
11804 player->show_envelope = element;
11806 ShowEnvelope(element - EL_ENVELOPE_1);
11809 else if (IS_DROPPABLE(element) ||
11810 IS_THROWABLE(element)) /* can be collected and dropped */
11814 if (element_info[element].collect_count == 0)
11815 player->inventory_infinite_element = element;
11817 for (i = 0; i < element_info[element].collect_count; i++)
11818 if (player->inventory_size < MAX_INVENTORY_SIZE)
11819 player->inventory_element[player->inventory_size++] = element;
11821 DrawGameValue_Dynamite(local_player->inventory_size);
11823 else if (element_info[element].collect_count > 0)
11825 local_player->gems_still_needed -=
11826 element_info[element].collect_count;
11827 if (local_player->gems_still_needed < 0)
11828 local_player->gems_still_needed = 0;
11830 DrawGameValue_Emeralds(local_player->gems_still_needed);
11833 RaiseScoreElement(element);
11834 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11837 CheckTriggeredElementChangeByPlayer(x, y, element,
11838 CE_PLAYER_COLLECTS_X,
11839 player->index_bit, dig_side);
11842 if (mode == DF_SNAP)
11843 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11848 else if (IS_PUSHABLE(element))
11850 if (mode == DF_SNAP && element != EL_BD_ROCK)
11851 return MF_NO_ACTION;
11853 if (CAN_FALL(element) && dy)
11854 return MF_NO_ACTION;
11856 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11857 !(element == EL_SPRING && level.use_spring_bug))
11858 return MF_NO_ACTION;
11861 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11862 ((move_direction & MV_VERTICAL &&
11863 ((element_info[element].move_pattern & MV_LEFT &&
11864 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11865 (element_info[element].move_pattern & MV_RIGHT &&
11866 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11867 (move_direction & MV_HORIZONTAL &&
11868 ((element_info[element].move_pattern & MV_UP &&
11869 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11870 (element_info[element].move_pattern & MV_DOWN &&
11871 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11872 return MF_NO_ACTION;
11876 /* do not push elements already moving away faster than player */
11877 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11878 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11879 return MF_NO_ACTION;
11881 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11882 return MF_NO_ACTION;
11888 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11890 if (player->push_delay_value == -1 || !player_was_pushing)
11891 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11893 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11895 if (player->push_delay_value == -1)
11896 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11899 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11901 if (player->push_delay_value == -1 || !player_was_pushing)
11902 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11905 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11907 if (!player->is_pushing)
11908 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11912 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11913 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11914 !player_is_pushing))
11915 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11918 if (!player->is_pushing &&
11919 game.engine_version >= VERSION_IDENT(2,2,0,7))
11920 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11924 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11925 player->push_delay, player->push_delay_value,
11926 FrameCounter, game.engine_version,
11927 player_was_pushing, player->is_pushing,
11928 element, element_info[element].token_name,
11929 GET_NEW_PUSH_DELAY(element));
11932 player->is_pushing = TRUE;
11934 if (!(IN_LEV_FIELD(nextx, nexty) &&
11935 (IS_FREE(nextx, nexty) ||
11936 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11937 IS_SB_ELEMENT(element)))))
11938 return MF_NO_ACTION;
11940 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11941 return MF_NO_ACTION;
11943 #if USE_NEW_PUSH_DELAY
11946 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11947 printf("::: ALERT: %d, %d [%d / %d]\n",
11948 player->push_delay, player->push_delay2,
11949 FrameCounter, FrameCounter / 50);
11952 if (player->push_delay == -1) /* new pushing; restart delay */
11953 player->push_delay = 0;
11955 if (player->push_delay == 0) /* new pushing; restart delay */
11956 player->push_delay = FrameCounter;
11959 #if USE_NEW_PUSH_DELAY
11961 if ( (player->push_delay > 0) != (!xxx_fr) )
11962 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11963 player->push_delay,
11964 xxx_pdv2, player->push_delay2, player->push_delay_value,
11965 FrameCounter, FrameCounter / 50);
11969 if (player->push_delay > 0 &&
11970 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11971 element != EL_SPRING && element != EL_BALLOON)
11974 if (player->push_delay < player->push_delay_value &&
11975 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11976 element != EL_SPRING && element != EL_BALLOON)
11980 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11981 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11982 element != EL_SPRING && element != EL_BALLOON)
11985 /* make sure that there is no move delay before next try to push */
11986 #if USE_NEW_MOVE_DELAY
11987 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11988 player->move_delay = 0;
11990 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11991 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11994 return MF_NO_ACTION;
11998 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12001 if (IS_SB_ELEMENT(element))
12003 if (element == EL_SOKOBAN_FIELD_FULL)
12005 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12006 local_player->sokobanfields_still_needed++;
12009 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12011 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12012 local_player->sokobanfields_still_needed--;
12015 Feld[x][y] = EL_SOKOBAN_OBJECT;
12017 if (Back[x][y] == Back[nextx][nexty])
12018 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12019 else if (Back[x][y] != 0)
12020 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12023 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12026 if (local_player->sokobanfields_still_needed == 0 &&
12027 game.emulation == EMU_SOKOBAN)
12029 player->LevelSolved = player->GameOver = TRUE;
12030 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12034 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12036 InitMovingField(x, y, move_direction);
12037 GfxAction[x][y] = ACTION_PUSHING;
12039 if (mode == DF_SNAP)
12040 ContinueMoving(x, y);
12042 MovPos[x][y] = (dx != 0 ? dx : dy);
12044 Pushed[x][y] = TRUE;
12045 Pushed[nextx][nexty] = TRUE;
12047 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12048 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12050 player->push_delay_value = -1; /* get new value later */
12052 #if USE_PUSH_BUGFIX
12053 /* now: check for element change _after_ element has been pushed! */
12055 if (game.use_change_when_pushing_bug)
12057 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12060 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12061 player->index_bit, dig_side);
12062 CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12063 player->index_bit, dig_side);
12069 /* check for element change _after_ element has been pushed! */
12073 /* !!! TEST ONLY !!! */
12074 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12075 player->index_bit, dig_side);
12076 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12077 player->index_bit, dig_side);
12079 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12080 player->index_bit, dig_side);
12081 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12082 player->index_bit, dig_side);
12090 else if (IS_SWITCHABLE(element))
12092 if (PLAYER_SWITCHING(player, x, y))
12094 CheckTriggeredElementChangeByPlayer(x,y, element,
12095 CE_PLAYER_PRESSES_X,
12096 player->index_bit, dig_side);
12101 player->is_switching = TRUE;
12102 player->switch_x = x;
12103 player->switch_y = y;
12105 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12107 if (element == EL_ROBOT_WHEEL)
12109 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12113 DrawLevelField(x, y);
12115 else if (element == EL_SP_TERMINAL)
12119 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12121 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12123 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12124 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12127 else if (IS_BELT_SWITCH(element))
12129 ToggleBeltSwitch(x, y);
12131 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12132 element == EL_SWITCHGATE_SWITCH_DOWN)
12134 ToggleSwitchgateSwitch(x, y);
12136 else if (element == EL_LIGHT_SWITCH ||
12137 element == EL_LIGHT_SWITCH_ACTIVE)
12139 ToggleLightSwitch(x, y);
12142 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12143 SND_LIGHT_SWITCH_ACTIVATING :
12144 SND_LIGHT_SWITCH_DEACTIVATING);
12147 else if (element == EL_TIMEGATE_SWITCH)
12149 ActivateTimegateSwitch(x, y);
12151 else if (element == EL_BALLOON_SWITCH_LEFT ||
12152 element == EL_BALLOON_SWITCH_RIGHT ||
12153 element == EL_BALLOON_SWITCH_UP ||
12154 element == EL_BALLOON_SWITCH_DOWN ||
12155 element == EL_BALLOON_SWITCH_ANY)
12157 if (element == EL_BALLOON_SWITCH_ANY)
12158 game.balloon_dir = move_direction;
12160 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12161 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12162 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12163 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12166 else if (element == EL_LAMP)
12168 Feld[x][y] = EL_LAMP_ACTIVE;
12169 local_player->lights_still_needed--;
12171 ResetGfxAnimation(x, y);
12172 DrawLevelField(x, y);
12174 else if (element == EL_TIME_ORB_FULL)
12176 Feld[x][y] = EL_TIME_ORB_EMPTY;
12178 DrawGameValue_Time(TimeLeft);
12180 ResetGfxAnimation(x, y);
12181 DrawLevelField(x, y);
12184 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12188 CheckTriggeredElementChangeByPlayer(x, y, element,
12190 player->index_bit, dig_side);
12192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12193 player->index_bit, dig_side);
12199 if (!PLAYER_SWITCHING(player, x, y))
12201 player->is_switching = TRUE;
12202 player->switch_x = x;
12203 player->switch_y = y;
12206 /* !!! TEST ONLY !!! */
12207 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12208 player->index_bit, dig_side);
12209 CheckTriggeredElementChangeByPlayer(x, y, element,
12211 player->index_bit, dig_side);
12213 CheckTriggeredElementChangeByPlayer(x, y, element,
12215 player->index_bit, dig_side);
12216 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12217 player->index_bit, dig_side);
12222 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12223 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12224 player->index_bit, dig_side);
12225 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12226 player->index_bit, dig_side);
12228 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12229 player->index_bit, dig_side);
12230 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12231 player->index_bit, dig_side);
12235 return MF_NO_ACTION;
12238 #if USE_NEW_PUSH_DELAY
12239 player->push_delay = -1;
12241 player->push_delay = 0;
12244 #if USE_PENGUIN_COLLECT_BUGFIX
12245 if (is_player) /* function can also be called by EL_PENGUIN */
12248 if (Feld[x][y] != element) /* really digged/collected something */
12249 player->is_collecting = !player->is_digging;
12255 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12257 int jx = player->jx, jy = player->jy;
12258 int x = jx + dx, y = jy + dy;
12259 int snap_direction = (dx == -1 ? MV_LEFT :
12260 dx == +1 ? MV_RIGHT :
12262 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12265 if (player->MovPos != 0)
12268 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12272 if (!player->active || !IN_LEV_FIELD(x, y))
12280 if (player->MovPos == 0)
12281 player->is_pushing = FALSE;
12283 player->is_snapping = FALSE;
12285 if (player->MovPos == 0)
12287 player->is_moving = FALSE;
12288 player->is_digging = FALSE;
12289 player->is_collecting = FALSE;
12295 if (player->is_snapping)
12298 player->MovDir = snap_direction;
12301 if (player->MovPos == 0)
12304 player->is_moving = FALSE;
12305 player->is_digging = FALSE;
12306 player->is_collecting = FALSE;
12309 player->is_dropping = FALSE;
12311 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12314 player->is_snapping = TRUE;
12317 if (player->MovPos == 0)
12320 player->is_moving = FALSE;
12321 player->is_digging = FALSE;
12322 player->is_collecting = FALSE;
12326 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12327 DrawLevelField(player->last_jx, player->last_jy);
12330 DrawLevelField(x, y);
12339 boolean DropElement(struct PlayerInfo *player)
12341 int old_element, new_element;
12342 int dropx = player->jx, dropy = player->jy;
12343 int drop_direction = player->MovDir;
12345 int drop_side = drop_direction;
12347 static int trigger_sides[4] =
12349 CH_SIDE_LEFT, /* dropping left */
12350 CH_SIDE_RIGHT, /* dropping right */
12351 CH_SIDE_TOP, /* dropping up */
12352 CH_SIDE_BOTTOM, /* dropping down */
12354 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12356 int drop_element = (player->inventory_size > 0 ?
12357 player->inventory_element[player->inventory_size - 1] :
12358 player->inventory_infinite_element != EL_UNDEFINED ?
12359 player->inventory_infinite_element :
12360 player->dynabombs_left > 0 ?
12361 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12364 if (IS_THROWABLE(drop_element))
12366 dropx += GET_DX_FROM_DIR(drop_direction);
12367 dropy += GET_DY_FROM_DIR(drop_direction);
12369 if (!IN_LEV_FIELD(dropx, dropy))
12373 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12374 new_element = drop_element; /* default: no change when dropping */
12376 /* check if player is active, not moving and ready to drop */
12377 if (!player->active || player->MovPos || player->drop_delay > 0)
12380 /* check if player has anything that can be dropped */
12382 if (new_element == EL_UNDEFINED)
12385 if (player->inventory_size == 0 &&
12386 player->inventory_infinite_element == EL_UNDEFINED &&
12387 player->dynabombs_left == 0)
12391 /* check if anything can be dropped at the current position */
12392 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12395 /* collected custom elements can only be dropped on empty fields */
12397 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12400 if (player->inventory_size > 0 &&
12401 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12402 && old_element != EL_EMPTY)
12406 if (old_element != EL_EMPTY)
12407 Back[dropx][dropy] = old_element; /* store old element on this field */
12409 ResetGfxAnimation(dropx, dropy);
12410 ResetRandomAnimationValue(dropx, dropy);
12412 if (player->inventory_size > 0 ||
12413 player->inventory_infinite_element != EL_UNDEFINED)
12415 if (player->inventory_size > 0)
12417 player->inventory_size--;
12420 new_element = player->inventory_element[player->inventory_size];
12423 DrawGameValue_Dynamite(local_player->inventory_size);
12425 if (new_element == EL_DYNAMITE)
12426 new_element = EL_DYNAMITE_ACTIVE;
12427 else if (new_element == EL_SP_DISK_RED)
12428 new_element = EL_SP_DISK_RED_ACTIVE;
12431 Feld[dropx][dropy] = new_element;
12433 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12434 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12435 el2img(Feld[dropx][dropy]), 0);
12437 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12440 /* needed if previous element just changed to "empty" in the last frame */
12441 Changed[dropx][dropy] = FALSE; /* allow another change */
12445 /* !!! TEST ONLY !!! */
12446 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12447 player->index_bit, drop_side);
12448 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12450 player->index_bit, drop_side);
12452 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12454 player->index_bit, drop_side);
12455 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12456 player->index_bit, drop_side);
12459 TestIfElementTouchesCustomElement(dropx, dropy);
12461 else /* player is dropping a dyna bomb */
12463 player->dynabombs_left--;
12466 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12469 Feld[dropx][dropy] = new_element;
12471 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12472 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12473 el2img(Feld[dropx][dropy]), 0);
12475 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12482 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12485 InitField_WithBug1(dropx, dropy, FALSE);
12487 InitField(dropx, dropy, FALSE);
12488 if (CAN_MOVE(Feld[dropx][dropy]))
12489 InitMovDir(dropx, dropy);
12493 new_element = Feld[dropx][dropy]; /* element might have changed */
12495 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12496 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12499 int move_stepsize = element_info[new_element].move_stepsize;
12501 int move_direction, nextx, nexty;
12503 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12504 MovDir[dropx][dropy] = drop_direction;
12506 move_direction = MovDir[dropx][dropy];
12507 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12508 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12511 Changed[dropx][dropy] = FALSE; /* allow another change */
12512 CheckCollision[dropx][dropy] = 2;
12515 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12518 WasJustMoving[dropx][dropy] = 3;
12521 InitMovingField(dropx, dropy, move_direction);
12522 ContinueMoving(dropx, dropy);
12527 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12530 Changed[dropx][dropy] = FALSE; /* allow another change */
12533 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12535 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12536 CE_HITTING_SOMETHING, move_direction);
12544 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12549 player->drop_delay = 8 + 8 + 8;
12553 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12558 player->is_dropping = TRUE;
12564 /* ------------------------------------------------------------------------- */
12565 /* game sound playing functions */
12566 /* ------------------------------------------------------------------------- */
12568 static int *loop_sound_frame = NULL;
12569 static int *loop_sound_volume = NULL;
12571 void InitPlayLevelSound()
12573 int num_sounds = getSoundListSize();
12575 checked_free(loop_sound_frame);
12576 checked_free(loop_sound_volume);
12578 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12579 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12582 static void PlayLevelSound(int x, int y, int nr)
12584 int sx = SCREENX(x), sy = SCREENY(y);
12585 int volume, stereo_position;
12586 int max_distance = 8;
12587 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12589 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12590 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12593 if (!IN_LEV_FIELD(x, y) ||
12594 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12595 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12598 volume = SOUND_MAX_VOLUME;
12600 if (!IN_SCR_FIELD(sx, sy))
12602 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12603 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12605 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12608 stereo_position = (SOUND_MAX_LEFT +
12609 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12610 (SCR_FIELDX + 2 * max_distance));
12612 if (IS_LOOP_SOUND(nr))
12614 /* This assures that quieter loop sounds do not overwrite louder ones,
12615 while restarting sound volume comparison with each new game frame. */
12617 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12620 loop_sound_volume[nr] = volume;
12621 loop_sound_frame[nr] = FrameCounter;
12624 PlaySoundExt(nr, volume, stereo_position, type);
12627 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12629 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12630 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12631 y < LEVELY(BY1) ? LEVELY(BY1) :
12632 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12636 static void PlayLevelSoundAction(int x, int y, int action)
12638 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12641 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12643 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12645 if (sound_effect != SND_UNDEFINED)
12646 PlayLevelSound(x, y, sound_effect);
12649 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12652 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12654 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12655 PlayLevelSound(x, y, sound_effect);
12658 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12660 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12662 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12663 PlayLevelSound(x, y, sound_effect);
12666 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12668 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12670 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12671 StopSound(sound_effect);
12674 static void PlayLevelMusic()
12676 if (levelset.music[level_nr] != MUS_UNDEFINED)
12677 PlayMusic(levelset.music[level_nr]); /* from config file */
12679 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12682 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12684 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12687 if (sample == SAMPLE_bug)
12688 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12694 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12698 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12702 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12706 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12710 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12714 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12718 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12721 case SAMPLE_android_clone:
12722 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12725 case SAMPLE_android_move:
12726 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12729 case SAMPLE_spring:
12730 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12734 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12738 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12741 case SAMPLE_eater_eat:
12742 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12746 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12749 case SAMPLE_collect:
12750 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12753 case SAMPLE_diamond:
12754 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12757 case SAMPLE_squash:
12758 /* !!! CHECK THIS !!! */
12760 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12762 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12766 case SAMPLE_wonderfall:
12767 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12771 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12775 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12779 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12783 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12787 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12791 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12794 case SAMPLE_wonder:
12795 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12799 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12802 case SAMPLE_exit_open:
12803 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12806 case SAMPLE_exit_leave:
12807 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12810 case SAMPLE_dynamite:
12811 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12815 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12819 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12823 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12827 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12831 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12835 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12839 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12844 void RaiseScore(int value)
12846 local_player->score += value;
12848 DrawGameValue_Score(local_player->score);
12851 void RaiseScoreElement(int element)
12856 case EL_BD_DIAMOND:
12857 case EL_EMERALD_YELLOW:
12858 case EL_EMERALD_RED:
12859 case EL_EMERALD_PURPLE:
12860 case EL_SP_INFOTRON:
12861 RaiseScore(level.score[SC_EMERALD]);
12864 RaiseScore(level.score[SC_DIAMOND]);
12867 RaiseScore(level.score[SC_CRYSTAL]);
12870 RaiseScore(level.score[SC_PEARL]);
12873 case EL_BD_BUTTERFLY:
12874 case EL_SP_ELECTRON:
12875 RaiseScore(level.score[SC_BUG]);
12878 case EL_BD_FIREFLY:
12879 case EL_SP_SNIKSNAK:
12880 RaiseScore(level.score[SC_SPACESHIP]);
12883 case EL_DARK_YAMYAM:
12884 RaiseScore(level.score[SC_YAMYAM]);
12887 RaiseScore(level.score[SC_ROBOT]);
12890 RaiseScore(level.score[SC_PACMAN]);
12893 RaiseScore(level.score[SC_NUT]);
12896 case EL_SP_DISK_RED:
12897 case EL_DYNABOMB_INCREASE_NUMBER:
12898 case EL_DYNABOMB_INCREASE_SIZE:
12899 case EL_DYNABOMB_INCREASE_POWER:
12900 RaiseScore(level.score[SC_DYNAMITE]);
12902 case EL_SHIELD_NORMAL:
12903 case EL_SHIELD_DEADLY:
12904 RaiseScore(level.score[SC_SHIELD]);
12906 case EL_EXTRA_TIME:
12907 RaiseScore(level.score[SC_TIME_BONUS]);
12921 RaiseScore(level.score[SC_KEY]);
12924 RaiseScore(element_info[element].collect_score);
12929 void RequestQuitGame(boolean ask_if_really_quit)
12931 if (AllPlayersGone ||
12932 !ask_if_really_quit ||
12933 level_editor_test_game ||
12934 Request("Do you really want to quit the game ?",
12935 REQ_ASK | REQ_STAY_CLOSED))
12937 #if defined(NETWORK_AVALIABLE)
12938 if (options.network)
12939 SendToServer_StopPlaying();
12943 game_status = GAME_MODE_MAIN;
12951 if (tape.playing && tape.deactivate_display)
12952 TapeDeactivateDisplayOff(TRUE);
12955 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12958 if (tape.playing && tape.deactivate_display)
12959 TapeDeactivateDisplayOn();
12966 /* ---------- new game button stuff ---------------------------------------- */
12968 /* graphic position values for game buttons */
12969 #define GAME_BUTTON_XSIZE 30
12970 #define GAME_BUTTON_YSIZE 30
12971 #define GAME_BUTTON_XPOS 5
12972 #define GAME_BUTTON_YPOS 215
12973 #define SOUND_BUTTON_XPOS 5
12974 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12976 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12977 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12978 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12979 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12980 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12981 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12988 } gamebutton_info[NUM_GAME_BUTTONS] =
12991 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12996 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12997 GAME_CTRL_ID_PAUSE,
13001 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13006 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13007 SOUND_CTRL_ID_MUSIC,
13008 "background music on/off"
13011 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13012 SOUND_CTRL_ID_LOOPS,
13013 "sound loops on/off"
13016 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13017 SOUND_CTRL_ID_SIMPLE,
13018 "normal sounds on/off"
13022 void CreateGameButtons()
13026 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13028 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13029 struct GadgetInfo *gi;
13032 unsigned long event_mask;
13033 int gd_xoffset, gd_yoffset;
13034 int gd_x1, gd_x2, gd_y1, gd_y2;
13037 gd_xoffset = gamebutton_info[i].x;
13038 gd_yoffset = gamebutton_info[i].y;
13039 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13040 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13042 if (id == GAME_CTRL_ID_STOP ||
13043 id == GAME_CTRL_ID_PAUSE ||
13044 id == GAME_CTRL_ID_PLAY)
13046 button_type = GD_TYPE_NORMAL_BUTTON;
13048 event_mask = GD_EVENT_RELEASED;
13049 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13050 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13054 button_type = GD_TYPE_CHECK_BUTTON;
13056 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13057 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13058 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13059 event_mask = GD_EVENT_PRESSED;
13060 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13061 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13064 gi = CreateGadget(GDI_CUSTOM_ID, id,
13065 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13066 GDI_X, DX + gd_xoffset,
13067 GDI_Y, DY + gd_yoffset,
13068 GDI_WIDTH, GAME_BUTTON_XSIZE,
13069 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13070 GDI_TYPE, button_type,
13071 GDI_STATE, GD_BUTTON_UNPRESSED,
13072 GDI_CHECKED, checked,
13073 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13074 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13075 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13076 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13077 GDI_EVENT_MASK, event_mask,
13078 GDI_CALLBACK_ACTION, HandleGameButtons,
13082 Error(ERR_EXIT, "cannot create gadget");
13084 game_gadget[id] = gi;
13088 void FreeGameButtons()
13092 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13093 FreeGadget(game_gadget[i]);
13096 static void MapGameButtons()
13100 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13101 MapGadget(game_gadget[i]);
13104 void UnmapGameButtons()
13108 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13109 UnmapGadget(game_gadget[i]);
13112 static void HandleGameButtons(struct GadgetInfo *gi)
13114 int id = gi->custom_id;
13116 if (game_status != GAME_MODE_PLAYING)
13121 case GAME_CTRL_ID_STOP:
13122 RequestQuitGame(TRUE);
13125 case GAME_CTRL_ID_PAUSE:
13126 if (options.network)
13128 #if defined(NETWORK_AVALIABLE)
13130 SendToServer_ContinuePlaying();
13132 SendToServer_PausePlaying();
13136 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13139 case GAME_CTRL_ID_PLAY:
13142 #if defined(NETWORK_AVALIABLE)
13143 if (options.network)
13144 SendToServer_ContinuePlaying();
13148 tape.pausing = FALSE;
13149 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13154 case SOUND_CTRL_ID_MUSIC:
13155 if (setup.sound_music)
13157 setup.sound_music = FALSE;
13160 else if (audio.music_available)
13162 setup.sound = setup.sound_music = TRUE;
13164 SetAudioMode(setup.sound);
13170 case SOUND_CTRL_ID_LOOPS:
13171 if (setup.sound_loops)
13172 setup.sound_loops = FALSE;
13173 else if (audio.loops_available)
13175 setup.sound = setup.sound_loops = TRUE;
13176 SetAudioMode(setup.sound);
13180 case SOUND_CTRL_ID_SIMPLE:
13181 if (setup.sound_simple)
13182 setup.sound_simple = FALSE;
13183 else if (audio.sound_available)
13185 setup.sound = setup.sound_simple = TRUE;
13186 SetAudioMode(setup.sound);