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)
60 /* for MovePlayer() */
61 #define MF_NO_ACTION 0
65 /* for ScrollPlayer() */
67 #define SCROLL_GO_ON 1
70 #define EX_PHASE_START 0
71 #define EX_TYPE_NONE 0
72 #define EX_TYPE_NORMAL (1 << 0)
73 #define EX_TYPE_CENTER (1 << 1)
74 #define EX_TYPE_BORDER (1 << 2)
75 #define EX_TYPE_CROSS (1 << 3)
76 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
78 /* special positions in the game control window (relative to control window) */
81 #define XX_EMERALDS 29
82 #define YY_EMERALDS 54
83 #define XX_DYNAMITE 29
84 #define YY_DYNAMITE 89
93 /* special positions in the game control window (relative to main window) */
94 #define DX_LEVEL (DX + XX_LEVEL)
95 #define DY_LEVEL (DY + YY_LEVEL)
96 #define DX_EMERALDS (DX + XX_EMERALDS)
97 #define DY_EMERALDS (DY + YY_EMERALDS)
98 #define DX_DYNAMITE (DX + XX_DYNAMITE)
99 #define DY_DYNAMITE (DY + YY_DYNAMITE)
100 #define DX_KEYS (DX + XX_KEYS)
101 #define DY_KEYS (DY + YY_KEYS)
102 #define DX_SCORE (DX + XX_SCORE)
103 #define DY_SCORE (DY + YY_SCORE)
104 #define DX_TIME1 (DX + XX_TIME1)
105 #define DX_TIME2 (DX + XX_TIME2)
106 #define DY_TIME (DY + YY_TIME)
108 /* values for initial player move delay (initial delay counter value) */
109 #define INITIAL_MOVE_DELAY_OFF -1
110 #define INITIAL_MOVE_DELAY_ON 0
112 /* values for player movement speed (which is in fact a delay value) */
113 #define MOVE_DELAY_NORMAL_SPEED 8
114 #define MOVE_DELAY_HIGH_SPEED 4
116 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
117 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
118 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
119 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
121 /* values for other actions */
122 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
124 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
125 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
127 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
129 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
130 RND(element_info[e].push_delay_random))
131 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
132 RND(element_info[e].drop_delay_random))
133 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
134 RND(element_info[e].move_delay_random))
135 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
136 (element_info[e].move_delay_random))
138 #define GET_TARGET_ELEMENT(e, ch) \
139 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
140 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
142 #define GET_VALID_PLAYER_ELEMENT(e) \
143 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
145 #define CAN_GROW_INTO(e) \
146 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
148 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
149 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
152 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
153 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
154 (CAN_MOVE_INTO_ACID(e) && \
155 Feld[x][y] == EL_ACID) || \
158 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
159 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
160 (CAN_MOVE_INTO_ACID(e) && \
161 Feld[x][y] == EL_ACID) || \
164 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
165 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
167 (CAN_MOVE_INTO_ACID(e) && \
168 Feld[x][y] == EL_ACID) || \
169 (DONT_COLLIDE_WITH(e) && \
171 !PLAYER_ENEMY_PROTECTED(x, y))))
174 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
175 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
177 (DONT_COLLIDE_WITH(e) && \
179 !PLAYER_ENEMY_PROTECTED(x, y))))
182 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
183 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
186 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
189 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
194 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
197 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
198 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
202 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
205 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
206 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
208 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
209 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
211 #define PIG_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
214 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
216 IS_FOOD_PENGUIN(Feld[x][y])))
217 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
220 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
223 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
224 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
228 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
229 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
230 (CAN_MOVE_INTO_ACID(e) && \
231 Feld[x][y] == EL_ACID) || \
232 Feld[x][y] == EL_DIAMOND))
234 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
235 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
236 (CAN_MOVE_INTO_ACID(e) && \
237 Feld[x][y] == EL_ACID) || \
238 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
240 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
241 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
242 (CAN_MOVE_INTO_ACID(e) && \
243 Feld[x][y] == EL_ACID) || \
244 IS_AMOEBOID(Feld[x][y])))
246 #define PIG_CAN_ENTER_FIELD(e, x, y) \
247 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
248 (CAN_MOVE_INTO_ACID(e) && \
249 Feld[x][y] == EL_ACID) || \
250 IS_FOOD_PIG(Feld[x][y])))
252 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
253 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
254 (CAN_MOVE_INTO_ACID(e) && \
255 Feld[x][y] == EL_ACID) || \
256 IS_FOOD_PENGUIN(Feld[x][y]) || \
257 Feld[x][y] == EL_EXIT_OPEN))
259 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
260 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
261 (CAN_MOVE_INTO_ACID(e) && \
262 Feld[x][y] == EL_ACID)))
264 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
265 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
266 (CAN_MOVE_INTO_ACID(e) && \
267 Feld[x][y] == EL_ACID) || \
270 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
271 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
272 (CAN_MOVE_INTO_ACID(e) && \
273 Feld[x][y] == EL_ACID)))
277 #define GROUP_NR(e) ((e) - EL_GROUP_START)
278 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
279 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
280 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
282 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
283 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
286 #define CE_ENTER_FIELD_COND(e, x, y) \
287 (!IS_PLAYER(x, y) && \
288 (Feld[x][y] == EL_ACID || \
289 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
291 #define CE_ENTER_FIELD_COND(e, x, y) \
292 (!IS_PLAYER(x, y) && \
293 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
296 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
297 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
299 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
300 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
302 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
303 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
304 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
305 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
307 /* game button identifiers */
308 #define GAME_CTRL_ID_STOP 0
309 #define GAME_CTRL_ID_PAUSE 1
310 #define GAME_CTRL_ID_PLAY 2
311 #define SOUND_CTRL_ID_MUSIC 3
312 #define SOUND_CTRL_ID_LOOPS 4
313 #define SOUND_CTRL_ID_SIMPLE 5
315 #define NUM_GAME_BUTTONS 6
318 /* forward declaration for internal use */
320 static void AdvanceFrameAndPlayerCounters(int);
322 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
323 static boolean MovePlayer(struct PlayerInfo *, int, int);
324 static void ScrollPlayer(struct PlayerInfo *, int);
325 static void ScrollScreen(struct PlayerInfo *, int);
327 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
329 static void InitBeltMovement(void);
330 static void CloseAllOpenTimegates(void);
331 static void CheckGravityMovement(struct PlayerInfo *);
332 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
333 static void KillHeroUnlessEnemyProtected(int, int);
334 static void KillHeroUnlessExplosionProtected(int, int);
336 static void TestIfPlayerTouchesCustomElement(int, int);
337 static void TestIfElementTouchesCustomElement(int, int);
338 static void TestIfElementHitsCustomElement(int, int, int);
340 static void TestIfElementSmashesCustomElement(int, int, int);
343 static void ChangeElement(int, int, int);
345 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
346 #define CheckTriggeredElementChange(x, y, e, ev) \
347 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
349 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
350 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
351 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
352 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
353 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
354 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
357 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
358 #define CheckElementChange(x, y, e, te, ev) \
359 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
360 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
361 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
362 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
363 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
364 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
365 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
367 static void PlayLevelSound(int, int, int);
368 static void PlayLevelSoundNearest(int, int, int);
369 static void PlayLevelSoundAction(int, int, int);
370 static void PlayLevelSoundElementAction(int, int, int, int);
371 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
372 static void PlayLevelSoundActionIfLoop(int, int, int);
373 static void StopLevelSoundActionIfLoop(int, int, int);
374 static void PlayLevelMusic();
376 static void MapGameButtons();
377 static void HandleGameButtons(struct GadgetInfo *);
379 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
382 /* ------------------------------------------------------------------------- */
383 /* definition of elements that automatically change to other elements after */
384 /* a specified time, eventually calling a function when changing */
385 /* ------------------------------------------------------------------------- */
387 /* forward declaration for changer functions */
388 static void InitBuggyBase(int x, int y);
389 static void WarnBuggyBase(int x, int y);
391 static void InitTrap(int x, int y);
392 static void ActivateTrap(int x, int y);
393 static void ChangeActiveTrap(int x, int y);
395 static void InitRobotWheel(int x, int y);
396 static void RunRobotWheel(int x, int y);
397 static void StopRobotWheel(int x, int y);
399 static void InitTimegateWheel(int x, int y);
400 static void RunTimegateWheel(int x, int y);
402 struct ChangingElementInfo
407 void (*pre_change_function)(int x, int y);
408 void (*change_function)(int x, int y);
409 void (*post_change_function)(int x, int y);
412 static struct ChangingElementInfo change_delay_list[] =
463 EL_SWITCHGATE_OPENING,
471 EL_SWITCHGATE_CLOSING,
472 EL_SWITCHGATE_CLOSED,
504 EL_ACID_SPLASH_RIGHT,
513 EL_SP_BUGGY_BASE_ACTIVATING,
520 EL_SP_BUGGY_BASE_ACTIVATING,
521 EL_SP_BUGGY_BASE_ACTIVE,
528 EL_SP_BUGGY_BASE_ACTIVE,
552 EL_ROBOT_WHEEL_ACTIVE,
560 EL_TIMEGATE_SWITCH_ACTIVE,
581 int push_delay_fixed, push_delay_random;
586 { EL_BALLOON, 0, 0 },
588 { EL_SOKOBAN_OBJECT, 2, 0 },
589 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
590 { EL_SATELLITE, 2, 0 },
591 { EL_SP_DISK_YELLOW, 2, 0 },
593 { EL_UNDEFINED, 0, 0 },
601 move_stepsize_list[] =
603 { EL_AMOEBA_DROP, 2 },
604 { EL_AMOEBA_DROPPING, 2 },
605 { EL_QUICKSAND_FILLING, 1 },
606 { EL_QUICKSAND_EMPTYING, 1 },
607 { EL_MAGIC_WALL_FILLING, 2 },
608 { EL_BD_MAGIC_WALL_FILLING, 2 },
609 { EL_MAGIC_WALL_EMPTYING, 2 },
610 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
620 collect_count_list[] =
623 { EL_BD_DIAMOND, 1 },
624 { EL_EMERALD_YELLOW, 1 },
625 { EL_EMERALD_RED, 1 },
626 { EL_EMERALD_PURPLE, 1 },
628 { EL_SP_INFOTRON, 1 },
640 access_direction_list[] =
642 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
643 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
644 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
645 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
646 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
647 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
648 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
649 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
650 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
651 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
652 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
654 { EL_SP_PORT_LEFT, MV_RIGHT },
655 { EL_SP_PORT_RIGHT, MV_LEFT },
656 { EL_SP_PORT_UP, MV_DOWN },
657 { EL_SP_PORT_DOWN, MV_UP },
658 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
659 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
660 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
661 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
662 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
663 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
664 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
665 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
666 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
667 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
668 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
669 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
670 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
671 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
672 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
674 { EL_UNDEFINED, MV_NO_MOVING }
677 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
679 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
680 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
681 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
682 IS_JUST_CHANGING(x, y))
684 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
687 void GetPlayerConfig()
689 if (!audio.sound_available)
690 setup.sound_simple = FALSE;
692 if (!audio.loops_available)
693 setup.sound_loops = FALSE;
695 if (!audio.music_available)
696 setup.sound_music = FALSE;
698 if (!video.fullscreen_available)
699 setup.fullscreen = FALSE;
701 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
703 SetAudioMode(setup.sound);
707 static int getBeltNrFromBeltElement(int element)
709 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
710 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
711 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
714 static int getBeltNrFromBeltActiveElement(int element)
716 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
717 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
718 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
721 static int getBeltNrFromBeltSwitchElement(int element)
723 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
724 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
725 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
728 static int getBeltDirNrFromBeltSwitchElement(int element)
730 static int belt_base_element[4] =
732 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
733 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
734 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
735 EL_CONVEYOR_BELT_4_SWITCH_LEFT
738 int belt_nr = getBeltNrFromBeltSwitchElement(element);
739 int belt_dir_nr = element - belt_base_element[belt_nr];
741 return (belt_dir_nr % 3);
744 static int getBeltDirFromBeltSwitchElement(int element)
746 static int belt_move_dir[3] =
753 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
755 return belt_move_dir[belt_dir_nr];
758 static void InitPlayerField(int x, int y, int element, boolean init_game)
760 if (element == EL_SP_MURPHY)
764 if (stored_player[0].present)
766 Feld[x][y] = EL_SP_MURPHY_CLONE;
772 stored_player[0].use_murphy_graphic = TRUE;
775 Feld[x][y] = EL_PLAYER_1;
781 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
782 int jx = player->jx, jy = player->jy;
784 player->present = TRUE;
786 player->block_last_field = (element == EL_SP_MURPHY ?
787 level.sp_block_last_field :
788 level.block_last_field);
790 #if USE_NEW_BLOCK_STYLE
793 /* ---------- initialize player's last field block delay --------------- */
795 /* always start with reliable default value (no adjustment needed) */
796 player->block_delay_adjustment = 0;
798 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
799 if (player->block_last_field && element == EL_SP_MURPHY)
800 player->block_delay_adjustment = 1;
802 /* special case 2: in game engines before 3.1.1, blocking was different */
803 if (game.use_block_last_field_bug)
804 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
807 /* blocking the last field when moving was corrected in version 3.1.1 */
808 if (game.use_block_last_field_bug)
810 /* even "not blocking" was blocking the last field for one frame */
811 level.block_delay = (level.block_last_field ? 7 : 1);
812 level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
814 level.block_last_field = TRUE;
815 level.sp_block_last_field = TRUE;
819 #if 0 /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
820 level.block_delay = 8; /* when blocking, block 8 frames */
821 level.sp_block_delay = 9; /* SP indeed blocks 9 frames, not 8 */
825 printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
831 player->block_delay = (player->block_last_field ?
832 (element == EL_SP_MURPHY ?
833 level.sp_block_delay :
834 level.block_delay) : 0);
836 player->block_delay = (element == EL_SP_MURPHY ?
837 (player->block_last_field ? 7 : 1) :
838 (player->block_last_field ? 7 : 1));
844 printf("::: block_last_field == %d, block_delay = %d\n",
845 player->block_last_field, player->block_delay);
849 if (!options.network || player->connected)
851 player->active = TRUE;
853 /* remove potentially duplicate players */
854 if (StorePlayer[jx][jy] == Feld[x][y])
855 StorePlayer[jx][jy] = 0;
857 StorePlayer[x][y] = Feld[x][y];
861 printf("Player %d activated.\n", player->element_nr);
862 printf("[Local player is %d and currently %s.]\n",
863 local_player->element_nr,
864 local_player->active ? "active" : "not active");
868 Feld[x][y] = EL_EMPTY;
870 player->jx = player->last_jx = x;
871 player->jy = player->last_jy = y;
875 static void InitField(int x, int y, boolean init_game)
877 int element = Feld[x][y];
886 InitPlayerField(x, y, element, init_game);
889 case EL_SOKOBAN_FIELD_PLAYER:
890 element = Feld[x][y] = EL_PLAYER_1;
891 InitField(x, y, init_game);
893 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
894 InitField(x, y, init_game);
897 case EL_SOKOBAN_FIELD_EMPTY:
898 local_player->sokobanfields_still_needed++;
902 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
903 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
904 else if (x > 0 && Feld[x-1][y] == EL_ACID)
905 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
906 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
907 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
908 else if (y > 0 && Feld[x][y-1] == EL_ACID)
909 Feld[x][y] = EL_ACID_POOL_BOTTOM;
910 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
911 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
919 case EL_SPACESHIP_RIGHT:
920 case EL_SPACESHIP_UP:
921 case EL_SPACESHIP_LEFT:
922 case EL_SPACESHIP_DOWN:
924 case EL_BD_BUTTERFLY_RIGHT:
925 case EL_BD_BUTTERFLY_UP:
926 case EL_BD_BUTTERFLY_LEFT:
927 case EL_BD_BUTTERFLY_DOWN:
928 case EL_BD_BUTTERFLY:
929 case EL_BD_FIREFLY_RIGHT:
930 case EL_BD_FIREFLY_UP:
931 case EL_BD_FIREFLY_LEFT:
932 case EL_BD_FIREFLY_DOWN:
934 case EL_PACMAN_RIGHT:
958 if (y == lev_fieldy - 1)
960 Feld[x][y] = EL_AMOEBA_GROWING;
961 Store[x][y] = EL_AMOEBA_WET;
965 case EL_DYNAMITE_ACTIVE:
966 case EL_SP_DISK_RED_ACTIVE:
967 case EL_DYNABOMB_PLAYER_1_ACTIVE:
968 case EL_DYNABOMB_PLAYER_2_ACTIVE:
969 case EL_DYNABOMB_PLAYER_3_ACTIVE:
970 case EL_DYNABOMB_PLAYER_4_ACTIVE:
975 local_player->lights_still_needed++;
979 local_player->friends_still_needed++;
984 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
989 Feld[x][y] = EL_EMPTY;
994 case EL_EM_KEY_1_FILE:
995 Feld[x][y] = EL_EM_KEY_1;
997 case EL_EM_KEY_2_FILE:
998 Feld[x][y] = EL_EM_KEY_2;
1000 case EL_EM_KEY_3_FILE:
1001 Feld[x][y] = EL_EM_KEY_3;
1003 case EL_EM_KEY_4_FILE:
1004 Feld[x][y] = EL_EM_KEY_4;
1008 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1009 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1010 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1011 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1012 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1013 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1014 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1015 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1016 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1017 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1018 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1019 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1022 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1023 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1024 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1026 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1028 game.belt_dir[belt_nr] = belt_dir;
1029 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1031 else /* more than one switch -- set it like the first switch */
1033 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1038 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1040 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1043 case EL_LIGHT_SWITCH_ACTIVE:
1045 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1049 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1051 else if (IS_GROUP_ELEMENT(element))
1053 struct ElementGroupInfo *group = element_info[element].group;
1054 int last_anim_random_frame = gfx.anim_random_frame;
1057 if (group->choice_mode == ANIM_RANDOM)
1058 gfx.anim_random_frame = RND(group->num_elements_resolved);
1060 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1061 group->choice_mode, 0,
1064 if (group->choice_mode == ANIM_RANDOM)
1065 gfx.anim_random_frame = last_anim_random_frame;
1067 group->choice_pos++;
1069 Feld[x][y] = group->element_resolved[element_pos];
1071 InitField(x, y, init_game);
1077 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1079 InitField(x, y, init_game);
1081 /* not needed to call InitMovDir() -- already done by InitField()! */
1082 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1083 CAN_MOVE(Feld[x][y]))
1087 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1089 int old_element = Feld[x][y];
1091 InitField(x, y, init_game);
1093 /* not needed to call InitMovDir() -- already done by InitField()! */
1094 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1095 CAN_MOVE(old_element) &&
1096 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1099 /* this case is in fact a combination of not less than three bugs:
1100 first, it calls InitMovDir() for elements that can move, although this is
1101 already done by InitField(); then, it checks the element that was at this
1102 field _before_ the call to InitField() (which can change it); lastly, it
1103 was not called for "mole with direction" elements, which were treated as
1104 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1108 inline void DrawGameValue_Emeralds(int value)
1110 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1113 inline void DrawGameValue_Dynamite(int value)
1115 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1118 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1122 /* currently only 4 of 8 possible keys are displayed */
1123 for (i = 0; i < STD_NUM_KEYS; i++)
1125 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1126 el2edimg(EL_KEY_1 + i));
1129 inline void DrawGameValue_Score(int value)
1131 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1134 inline void DrawGameValue_Time(int value)
1137 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1139 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1142 inline void DrawGameValue_Level(int value)
1145 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1148 /* misuse area for displaying emeralds to draw bigger level number */
1149 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1150 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1152 /* now copy it to the area for displaying level number */
1153 BlitBitmap(drawto, drawto,
1154 DX_EMERALDS, DY_EMERALDS + 1,
1155 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1156 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1157 DX_LEVEL - 1, DY_LEVEL + 1);
1159 /* restore the area for displaying emeralds */
1160 DrawGameValue_Emeralds(local_player->gems_still_needed);
1162 /* yes, this is all really ugly :-) */
1166 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1169 int key[MAX_NUM_KEYS];
1172 for (i = 0; i < MAX_NUM_KEYS; i++)
1173 key[i] = key_bits & (1 << i);
1175 DrawGameValue_Level(level_nr);
1177 DrawGameValue_Emeralds(emeralds);
1178 DrawGameValue_Dynamite(dynamite);
1179 DrawGameValue_Score(score);
1180 DrawGameValue_Time(time);
1182 DrawGameValue_Keys(key);
1185 void DrawGameDoorValues()
1189 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1191 DrawGameDoorValues_EM();
1196 DrawGameValue_Level(level_nr);
1198 DrawGameValue_Emeralds(local_player->gems_still_needed);
1199 DrawGameValue_Dynamite(local_player->inventory_size);
1200 DrawGameValue_Score(local_player->score);
1201 DrawGameValue_Time(TimeLeft);
1203 for (i = 0; i < MAX_PLAYERS; i++)
1204 DrawGameValue_Keys(stored_player[i].key);
1207 static void resolve_group_element(int group_element, int recursion_depth)
1209 static int group_nr;
1210 static struct ElementGroupInfo *group;
1211 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1214 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1216 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1217 group_element - EL_GROUP_START + 1);
1219 /* replace element which caused too deep recursion by question mark */
1220 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1225 if (recursion_depth == 0) /* initialization */
1227 group = element_info[group_element].group;
1228 group_nr = group_element - EL_GROUP_START;
1230 group->num_elements_resolved = 0;
1231 group->choice_pos = 0;
1234 for (i = 0; i < actual_group->num_elements; i++)
1236 int element = actual_group->element[i];
1238 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1241 if (IS_GROUP_ELEMENT(element))
1242 resolve_group_element(element, recursion_depth + 1);
1245 group->element_resolved[group->num_elements_resolved++] = element;
1246 element_info[element].in_group[group_nr] = TRUE;
1251 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1253 printf("::: group %d: %d resolved elements\n",
1254 group_element - EL_GROUP_START, group->num_elements_resolved);
1255 for (i = 0; i < group->num_elements_resolved; i++)
1256 printf("::: - %d ['%s']\n", group->element_resolved[i],
1257 element_info[group->element_resolved[i]].token_name);
1264 =============================================================================
1266 -----------------------------------------------------------------------------
1267 initialize game engine due to level / tape version number
1268 =============================================================================
1271 static void InitGameEngine()
1275 /* set game engine from tape file when re-playing, else from level file */
1276 game.engine_version = (tape.playing ? tape.engine_version :
1277 level.game_version);
1279 /* ---------------------------------------------------------------------- */
1280 /* set flags for bugs and changes according to active game engine version */
1281 /* ---------------------------------------------------------------------- */
1284 Summary of bugfix/change:
1285 Fixed handling for custom elements that change when pushed by the player.
1287 Fixed/changed in version:
1291 Before 3.1.0, custom elements that "change when pushing" changed directly
1292 after the player started pushing them (until then handled in "DigField()").
1293 Since 3.1.0, these custom elements are not changed until the "pushing"
1294 move of the element is finished (now handled in "ContinueMoving()").
1296 Affected levels/tapes:
1297 The first condition is generally needed for all levels/tapes before version
1298 3.1.0, which might use the old behaviour before it was changed; known tapes
1299 that are affected are some tapes from the level set "Walpurgis Gardens" by
1301 The second condition is an exception from the above case and is needed for
1302 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1303 above (including some development versions of 3.1.0), but before it was
1304 known that this change would break tapes like the above and was fixed in
1305 3.1.1, so that the changed behaviour was active although the engine version
1306 while recording maybe was before 3.1.0. There is at least one tape that is
1307 affected by this exception, which is the tape for the one-level set "Bug
1308 Machine" by Juergen Bonhagen.
1311 game.use_change_when_pushing_bug =
1312 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1314 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1315 tape.game_version < VERSION_IDENT(3,1,1,0)));
1318 Summary of bugfix/change:
1319 Fixed handling for blocking the field the player leaves when moving.
1321 Fixed/changed in version:
1325 Before 3.1.1, when "block last field when moving" was enabled, the field
1326 the player is leaving when moving was blocked for the time of the move,
1327 and was directly unblocked afterwards. This resulted in the last field
1328 being blocked for exactly one less than the number of frames of one player
1329 move. Additionally, even when blocking was disabled, the last field was
1330 blocked for exactly one frame.
1331 Since 3.1.1, due to changes in player movement handling, the last field
1332 is not blocked at all when blocking is disabled. When blocking is enabled,
1333 the last field is blocked for exactly the number of frames of one player
1334 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1335 last field is blocked for exactly one more than the number of frames of
1338 Affected levels/tapes:
1339 (!!! yet to be determined -- probably many !!!)
1342 game.use_block_last_field_bug =
1343 (game.engine_version < VERSION_IDENT(3,1,1,0));
1345 /* ---------------------------------------------------------------------- */
1347 /* dynamically adjust element properties according to game engine version */
1348 InitElementPropertiesEngine(game.engine_version);
1351 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1352 printf(" tape version == %06d [%s] [file: %06d]\n",
1353 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1355 printf(" => game.engine_version == %06d\n", game.engine_version);
1358 /* ---------- recursively resolve group elements ------------------------- */
1360 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1361 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1362 element_info[i].in_group[j] = FALSE;
1364 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1365 resolve_group_element(EL_GROUP_START + i, 0);
1367 /* ---------- initialize player's initial move delay --------------------- */
1369 #if USE_NEW_MOVE_DELAY
1370 /* dynamically adjust player properties according to level information */
1371 game.initial_move_delay_value =
1372 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1374 /* dynamically adjust player properties according to game engine version */
1375 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1376 game.initial_move_delay_value : 0);
1378 /* dynamically adjust player properties according to game engine version */
1379 game.initial_move_delay =
1380 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1381 INITIAL_MOVE_DELAY_OFF);
1383 /* dynamically adjust player properties according to level information */
1384 game.initial_move_delay_value =
1385 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1388 /* ---------- initialize player's initial push delay --------------------- */
1390 /* dynamically adjust player properties according to game engine version */
1391 game.initial_push_delay_value =
1392 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1394 /* ---------- initialize changing elements ------------------------------- */
1396 /* initialize changing elements information */
1397 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1399 struct ElementInfo *ei = &element_info[i];
1401 /* this pointer might have been changed in the level editor */
1402 ei->change = &ei->change_page[0];
1404 if (!IS_CUSTOM_ELEMENT(i))
1406 ei->change->target_element = EL_EMPTY_SPACE;
1407 ei->change->delay_fixed = 0;
1408 ei->change->delay_random = 0;
1409 ei->change->delay_frames = 1;
1412 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1414 ei->has_change_event[j] = FALSE;
1416 ei->event_page_nr[j] = 0;
1417 ei->event_page[j] = &ei->change_page[0];
1421 /* add changing elements from pre-defined list */
1422 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1424 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1425 struct ElementInfo *ei = &element_info[ch_delay->element];
1427 ei->change->target_element = ch_delay->target_element;
1428 ei->change->delay_fixed = ch_delay->change_delay;
1430 ei->change->pre_change_function = ch_delay->pre_change_function;
1431 ei->change->change_function = ch_delay->change_function;
1432 ei->change->post_change_function = ch_delay->post_change_function;
1434 ei->has_change_event[CE_DELAY] = TRUE;
1437 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1442 /* add change events from custom element configuration */
1443 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1445 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1447 for (j = 0; j < ei->num_change_pages; j++)
1449 if (!ei->change_page[j].can_change)
1452 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1454 /* only add event page for the first page found with this event */
1455 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1457 ei->has_change_event[k] = TRUE;
1459 ei->event_page_nr[k] = j;
1460 ei->event_page[k] = &ei->change_page[j];
1468 /* add change events from custom element configuration */
1469 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1471 int element = EL_CUSTOM_START + i;
1473 /* only add custom elements that change after fixed/random frame delay */
1474 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1475 element_info[element].has_change_event[CE_DELAY] = TRUE;
1479 /* ---------- initialize run-time trigger player and element ------------- */
1481 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1483 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1485 for (j = 0; j < ei->num_change_pages; j++)
1487 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1488 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1492 /* ---------- initialize trigger events ---------------------------------- */
1494 /* initialize trigger events information */
1495 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1496 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1497 trigger_events[i][j] = FALSE;
1500 /* add trigger events from element change event properties */
1501 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1503 struct ElementInfo *ei = &element_info[i];
1505 for (j = 0; j < ei->num_change_pages; j++)
1507 if (!ei->change_page[j].can_change)
1510 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1512 int trigger_element = ei->change_page[j].trigger_element;
1514 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1516 if (ei->change_page[j].has_event[k])
1518 if (IS_GROUP_ELEMENT(trigger_element))
1520 struct ElementGroupInfo *group =
1521 element_info[trigger_element].group;
1523 for (l = 0; l < group->num_elements_resolved; l++)
1524 trigger_events[group->element_resolved[l]][k] = TRUE;
1527 trigger_events[trigger_element][k] = TRUE;
1534 /* add trigger events from element change event properties */
1535 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1536 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1537 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1538 if (element_info[i].change->has_event[j])
1539 trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1542 /* ---------- initialize push delay -------------------------------------- */
1544 /* initialize push delay values to default */
1545 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1547 if (!IS_CUSTOM_ELEMENT(i))
1549 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1550 element_info[i].push_delay_random = game.default_push_delay_random;
1554 /* set push delay value for certain elements from pre-defined list */
1555 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1557 int e = push_delay_list[i].element;
1559 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1560 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1563 /* set push delay value for Supaplex elements for newer engine versions */
1564 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1566 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1568 if (IS_SP_ELEMENT(i))
1570 #if USE_NEW_MOVE_STYLE
1571 /* set SP push delay to just enough to push under a falling zonk */
1572 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1574 element_info[i].push_delay_fixed = delay;
1575 element_info[i].push_delay_random = 0;
1577 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1578 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1584 /* ---------- initialize move stepsize ----------------------------------- */
1586 /* initialize move stepsize values to default */
1587 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1588 if (!IS_CUSTOM_ELEMENT(i))
1589 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1591 /* set move stepsize value for certain elements from pre-defined list */
1592 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1594 int e = move_stepsize_list[i].element;
1596 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1600 /* ---------- initialize move dig/leave ---------------------------------- */
1602 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1604 element_info[i].can_leave_element = FALSE;
1605 element_info[i].can_leave_element_last = FALSE;
1609 /* ---------- initialize gem count --------------------------------------- */
1611 /* initialize gem count values for each element */
1612 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1613 if (!IS_CUSTOM_ELEMENT(i))
1614 element_info[i].collect_count = 0;
1616 /* add gem count values for all elements from pre-defined list */
1617 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1618 element_info[collect_count_list[i].element].collect_count =
1619 collect_count_list[i].count;
1621 /* ---------- initialize access direction -------------------------------- */
1623 /* initialize access direction values to default (access from every side) */
1624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1625 if (!IS_CUSTOM_ELEMENT(i))
1626 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1628 /* set access direction value for certain elements from pre-defined list */
1629 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1630 element_info[access_direction_list[i].element].access_direction =
1631 access_direction_list[i].direction;
1636 =============================================================================
1638 -----------------------------------------------------------------------------
1639 initialize and start new game
1640 =============================================================================
1645 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1646 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1647 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1654 #if USE_NEW_AMOEBA_CODE
1655 printf("Using new amoeba code.\n");
1657 printf("Using old amoeba code.\n");
1662 /* don't play tapes over network */
1663 network_playing = (options.network && !tape.playing);
1665 for (i = 0; i < MAX_PLAYERS; i++)
1667 struct PlayerInfo *player = &stored_player[i];
1669 player->index_nr = i;
1670 player->index_bit = (1 << i);
1671 player->element_nr = EL_PLAYER_1 + i;
1673 player->present = FALSE;
1674 player->active = FALSE;
1677 player->effective_action = 0;
1678 player->programmed_action = 0;
1681 player->gems_still_needed = level.gems_needed;
1682 player->sokobanfields_still_needed = 0;
1683 player->lights_still_needed = 0;
1684 player->friends_still_needed = 0;
1686 for (j = 0; j < MAX_NUM_KEYS; j++)
1687 player->key[j] = FALSE;
1689 player->dynabomb_count = 0;
1690 player->dynabomb_size = 1;
1691 player->dynabombs_left = 0;
1692 player->dynabomb_xl = FALSE;
1694 player->MovDir = MV_NO_MOVING;
1697 player->GfxDir = MV_NO_MOVING;
1698 player->GfxAction = ACTION_DEFAULT;
1700 player->StepFrame = 0;
1702 player->use_murphy_graphic = FALSE;
1704 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1705 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1707 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1709 player->actual_frame_counter = 0;
1711 player->step_counter = 0;
1713 player->last_move_dir = MV_NO_MOVING;
1715 player->is_waiting = FALSE;
1716 player->is_moving = FALSE;
1717 player->is_auto_moving = FALSE;
1718 player->is_digging = FALSE;
1719 player->is_snapping = FALSE;
1720 player->is_collecting = FALSE;
1721 player->is_pushing = FALSE;
1722 player->is_switching = FALSE;
1723 player->is_dropping = FALSE;
1725 player->is_bored = FALSE;
1726 player->is_sleeping = FALSE;
1728 player->frame_counter_bored = -1;
1729 player->frame_counter_sleeping = -1;
1731 player->anim_delay_counter = 0;
1732 player->post_delay_counter = 0;
1734 player->action_waiting = ACTION_DEFAULT;
1735 player->last_action_waiting = ACTION_DEFAULT;
1736 player->special_action_bored = ACTION_DEFAULT;
1737 player->special_action_sleeping = ACTION_DEFAULT;
1739 player->num_special_action_bored = 0;
1740 player->num_special_action_sleeping = 0;
1742 /* determine number of special actions for bored and sleeping animation */
1743 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1745 boolean found = FALSE;
1747 for (k = 0; k < NUM_DIRECTIONS; k++)
1748 if (el_act_dir2img(player->element_nr, j, k) !=
1749 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1753 player->num_special_action_bored++;
1757 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1759 boolean found = FALSE;
1761 for (k = 0; k < NUM_DIRECTIONS; k++)
1762 if (el_act_dir2img(player->element_nr, j, k) !=
1763 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1767 player->num_special_action_sleeping++;
1772 player->switch_x = -1;
1773 player->switch_y = -1;
1775 player->show_envelope = 0;
1777 player->move_delay = game.initial_move_delay;
1778 player->move_delay_value = game.initial_move_delay_value;
1780 player->move_delay_reset_counter = 0;
1782 #if USE_NEW_PUSH_DELAY
1783 player->push_delay = -1; /* initialized when pushing starts */
1784 player->push_delay_value = game.initial_push_delay_value;
1786 player->push_delay = 0;
1787 player->push_delay_value = game.initial_push_delay_value;
1790 player->drop_delay = 0;
1792 player->last_jx = player->last_jy = 0;
1793 player->jx = player->jy = 0;
1795 player->shield_normal_time_left = 0;
1796 player->shield_deadly_time_left = 0;
1798 player->inventory_infinite_element = EL_UNDEFINED;
1799 player->inventory_size = 0;
1801 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1802 SnapField(player, 0, 0);
1804 player->LevelSolved = FALSE;
1805 player->GameOver = FALSE;
1808 network_player_action_received = FALSE;
1810 #if defined(NETWORK_AVALIABLE)
1811 /* initial null action */
1812 if (network_playing)
1813 SendToServer_MovePlayer(MV_NO_MOVING);
1822 TimeLeft = level.time;
1825 ScreenMovDir = MV_NO_MOVING;
1829 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1831 AllPlayersGone = FALSE;
1833 game.yamyam_content_nr = 0;
1834 game.magic_wall_active = FALSE;
1835 game.magic_wall_time_left = 0;
1836 game.light_time_left = 0;
1837 game.timegate_time_left = 0;
1838 game.switchgate_pos = 0;
1839 game.balloon_dir = MV_NO_MOVING;
1840 game.gravity = level.initial_gravity;
1841 game.explosions_delayed = TRUE;
1843 game.envelope_active = FALSE;
1845 for (i = 0; i < NUM_BELTS; i++)
1847 game.belt_dir[i] = MV_NO_MOVING;
1848 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1851 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1852 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1854 for (x = 0; x < lev_fieldx; x++)
1856 for (y = 0; y < lev_fieldy; y++)
1858 Feld[x][y] = level.field[x][y];
1859 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1860 ChangeDelay[x][y] = 0;
1861 ChangePage[x][y] = -1;
1862 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1864 WasJustMoving[x][y] = 0;
1865 WasJustFalling[x][y] = 0;
1866 CheckCollision[x][y] = 0;
1868 Pushed[x][y] = FALSE;
1870 Changed[x][y] = FALSE;
1871 ChangeEvent[x][y] = -1;
1873 ExplodePhase[x][y] = 0;
1874 ExplodeDelay[x][y] = 0;
1875 ExplodeField[x][y] = EX_TYPE_NONE;
1877 RunnerVisit[x][y] = 0;
1878 PlayerVisit[x][y] = 0;
1881 GfxRandom[x][y] = INIT_GFX_RANDOM();
1882 GfxElement[x][y] = EL_UNDEFINED;
1883 GfxAction[x][y] = ACTION_DEFAULT;
1884 GfxDir[x][y] = MV_NO_MOVING;
1888 for (y = 0; y < lev_fieldy; y++)
1890 for (x = 0; x < lev_fieldx; x++)
1892 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1894 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1896 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1899 InitField(x, y, TRUE);
1905 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1906 emulate_sb ? EMU_SOKOBAN :
1907 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1909 /* initialize explosion and ignition delay */
1910 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1912 if (!IS_CUSTOM_ELEMENT(i))
1915 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1916 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1917 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1918 int last_phase = (num_phase + 1) * delay;
1919 int half_phase = (num_phase / 2) * delay;
1921 element_info[i].explosion_delay = last_phase - 1;
1922 element_info[i].ignition_delay = half_phase;
1925 if (i == EL_BLACK_ORB)
1926 element_info[i].ignition_delay = 0;
1928 if (i == EL_BLACK_ORB)
1929 element_info[i].ignition_delay = 1;
1934 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1935 element_info[i].explosion_delay = 1;
1937 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1938 element_info[i].ignition_delay = 1;
1942 /* correct non-moving belts to start moving left */
1943 for (i = 0; i < NUM_BELTS; i++)
1944 if (game.belt_dir[i] == MV_NO_MOVING)
1945 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1947 /* check if any connected player was not found in playfield */
1948 for (i = 0; i < MAX_PLAYERS; i++)
1950 struct PlayerInfo *player = &stored_player[i];
1952 if (player->connected && !player->present)
1954 for (j = 0; j < MAX_PLAYERS; j++)
1956 struct PlayerInfo *some_player = &stored_player[j];
1957 int jx = some_player->jx, jy = some_player->jy;
1959 /* assign first free player found that is present in the playfield */
1960 if (some_player->present && !some_player->connected)
1962 player->present = TRUE;
1963 player->active = TRUE;
1965 some_player->present = FALSE;
1966 some_player->active = FALSE;
1969 player->element_nr = some_player->element_nr;
1972 #if USE_NEW_BLOCK_STYLE
1973 player->block_last_field = some_player->block_last_field;
1974 player->block_delay_adjustment = some_player->block_delay_adjustment;
1977 StorePlayer[jx][jy] = player->element_nr;
1978 player->jx = player->last_jx = jx;
1979 player->jy = player->last_jy = jy;
1989 /* when playing a tape, eliminate all players which do not participate */
1991 for (i = 0; i < MAX_PLAYERS; i++)
1993 if (stored_player[i].active && !tape.player_participates[i])
1995 struct PlayerInfo *player = &stored_player[i];
1996 int jx = player->jx, jy = player->jy;
1998 player->active = FALSE;
1999 StorePlayer[jx][jy] = 0;
2000 Feld[jx][jy] = EL_EMPTY;
2004 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2006 /* when in single player mode, eliminate all but the first active player */
2008 for (i = 0; i < MAX_PLAYERS; i++)
2010 if (stored_player[i].active)
2012 for (j = i + 1; j < MAX_PLAYERS; j++)
2014 if (stored_player[j].active)
2016 struct PlayerInfo *player = &stored_player[j];
2017 int jx = player->jx, jy = player->jy;
2019 player->active = FALSE;
2020 player->present = FALSE;
2022 StorePlayer[jx][jy] = 0;
2023 Feld[jx][jy] = EL_EMPTY;
2030 /* when recording the game, store which players take part in the game */
2033 for (i = 0; i < MAX_PLAYERS; i++)
2034 if (stored_player[i].active)
2035 tape.player_participates[i] = TRUE;
2040 for (i = 0; i < MAX_PLAYERS; i++)
2042 struct PlayerInfo *player = &stored_player[i];
2044 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2049 if (local_player == player)
2050 printf("Player %d is local player.\n", i+1);
2054 if (BorderElement == EL_EMPTY)
2057 SBX_Right = lev_fieldx - SCR_FIELDX;
2059 SBY_Lower = lev_fieldy - SCR_FIELDY;
2064 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2066 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2069 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2070 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2072 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2073 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2075 /* if local player not found, look for custom element that might create
2076 the player (make some assumptions about the right custom element) */
2077 if (!local_player->present)
2079 int start_x = 0, start_y = 0;
2080 int found_rating = 0;
2081 int found_element = EL_UNDEFINED;
2083 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2085 int element = Feld[x][y];
2090 if (!IS_CUSTOM_ELEMENT(element))
2093 if (CAN_CHANGE(element))
2095 for (i = 0; i < element_info[element].num_change_pages; i++)
2097 content = element_info[element].change_page[i].target_element;
2098 is_player = ELEM_IS_PLAYER(content);
2100 if (is_player && (found_rating < 3 || element < found_element))
2106 found_element = element;
2111 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2113 content = element_info[element].content[xx][yy];
2114 is_player = ELEM_IS_PLAYER(content);
2116 if (is_player && (found_rating < 2 || element < found_element))
2118 start_x = x + xx - 1;
2119 start_y = y + yy - 1;
2122 found_element = element;
2125 if (!CAN_CHANGE(element))
2128 for (i = 0; i < element_info[element].num_change_pages; i++)
2130 content= element_info[element].change_page[i].target_content[xx][yy];
2131 is_player = ELEM_IS_PLAYER(content);
2133 if (is_player && (found_rating < 1 || element < found_element))
2135 start_x = x + xx - 1;
2136 start_y = y + yy - 1;
2139 found_element = element;
2145 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2146 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2149 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2150 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2156 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2157 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2158 local_player->jx - MIDPOSX);
2160 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2161 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2162 local_player->jy - MIDPOSY);
2164 scroll_x = SBX_Left;
2165 scroll_y = SBY_Upper;
2166 if (local_player->jx >= SBX_Left + MIDPOSX)
2167 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2168 local_player->jx - MIDPOSX :
2170 if (local_player->jy >= SBY_Upper + MIDPOSY)
2171 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2172 local_player->jy - MIDPOSY :
2177 CloseDoor(DOOR_CLOSE_1);
2179 /* !!! FIX THIS (START) !!! */
2180 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2182 InitGameEngine_EM();
2189 /* after drawing the level, correct some elements */
2190 if (game.timegate_time_left == 0)
2191 CloseAllOpenTimegates();
2193 if (setup.soft_scrolling)
2194 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2196 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2199 /* !!! FIX THIS (END) !!! */
2201 /* copy default game door content to main double buffer */
2202 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2203 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2205 DrawGameDoorValues();
2209 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2210 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2211 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2215 /* copy actual game door content to door double buffer for OpenDoor() */
2216 BlitBitmap(drawto, bitmap_db_door,
2217 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2219 OpenDoor(DOOR_OPEN_ALL);
2221 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2223 if (setup.sound_music)
2226 KeyboardAutoRepeatOffUnlessAutoplay();
2230 for (i = 0; i < MAX_PLAYERS; i++)
2231 printf("Player %d %sactive.\n",
2232 i + 1, (stored_player[i].active ? "" : "not "));
2236 printf("::: starting game [%d]\n", FrameCounter);
2240 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2242 /* this is used for non-R'n'D game engines to update certain engine values */
2244 /* needed to determine if sounds are played within the visible screen area */
2245 scroll_x = actual_scroll_x;
2246 scroll_y = actual_scroll_y;
2249 void InitMovDir(int x, int y)
2251 int i, element = Feld[x][y];
2252 static int xy[4][2] =
2259 static int direction[3][4] =
2261 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2262 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2263 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2272 Feld[x][y] = EL_BUG;
2273 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2276 case EL_SPACESHIP_RIGHT:
2277 case EL_SPACESHIP_UP:
2278 case EL_SPACESHIP_LEFT:
2279 case EL_SPACESHIP_DOWN:
2280 Feld[x][y] = EL_SPACESHIP;
2281 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2284 case EL_BD_BUTTERFLY_RIGHT:
2285 case EL_BD_BUTTERFLY_UP:
2286 case EL_BD_BUTTERFLY_LEFT:
2287 case EL_BD_BUTTERFLY_DOWN:
2288 Feld[x][y] = EL_BD_BUTTERFLY;
2289 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2292 case EL_BD_FIREFLY_RIGHT:
2293 case EL_BD_FIREFLY_UP:
2294 case EL_BD_FIREFLY_LEFT:
2295 case EL_BD_FIREFLY_DOWN:
2296 Feld[x][y] = EL_BD_FIREFLY;
2297 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2300 case EL_PACMAN_RIGHT:
2302 case EL_PACMAN_LEFT:
2303 case EL_PACMAN_DOWN:
2304 Feld[x][y] = EL_PACMAN;
2305 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2308 case EL_SP_SNIKSNAK:
2309 MovDir[x][y] = MV_UP;
2312 case EL_SP_ELECTRON:
2313 MovDir[x][y] = MV_LEFT;
2320 Feld[x][y] = EL_MOLE;
2321 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2325 if (IS_CUSTOM_ELEMENT(element))
2327 struct ElementInfo *ei = &element_info[element];
2328 int move_direction_initial = ei->move_direction_initial;
2329 int move_pattern = ei->move_pattern;
2331 if (move_direction_initial == MV_START_PREVIOUS)
2333 if (MovDir[x][y] != MV_NO_MOVING)
2336 move_direction_initial = MV_START_AUTOMATIC;
2339 if (move_direction_initial == MV_START_RANDOM)
2340 MovDir[x][y] = 1 << RND(4);
2341 else if (move_direction_initial & MV_ANY_DIRECTION)
2342 MovDir[x][y] = move_direction_initial;
2343 else if (move_pattern == MV_ALL_DIRECTIONS ||
2344 move_pattern == MV_TURNING_LEFT ||
2345 move_pattern == MV_TURNING_RIGHT ||
2346 move_pattern == MV_TURNING_LEFT_RIGHT ||
2347 move_pattern == MV_TURNING_RIGHT_LEFT ||
2348 move_pattern == MV_TURNING_RANDOM)
2349 MovDir[x][y] = 1 << RND(4);
2350 else if (move_pattern == MV_HORIZONTAL)
2351 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2352 else if (move_pattern == MV_VERTICAL)
2353 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2354 else if (move_pattern & MV_ANY_DIRECTION)
2355 MovDir[x][y] = element_info[element].move_pattern;
2356 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2357 move_pattern == MV_ALONG_RIGHT_SIDE)
2360 /* use random direction as default start direction */
2361 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2362 MovDir[x][y] = 1 << RND(4);
2365 for (i = 0; i < NUM_DIRECTIONS; i++)
2367 int x1 = x + xy[i][0];
2368 int y1 = y + xy[i][1];
2370 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2372 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2373 MovDir[x][y] = direction[0][i];
2375 MovDir[x][y] = direction[1][i];
2384 MovDir[x][y] = 1 << RND(4);
2386 if (element != EL_BUG &&
2387 element != EL_SPACESHIP &&
2388 element != EL_BD_BUTTERFLY &&
2389 element != EL_BD_FIREFLY)
2392 for (i = 0; i < NUM_DIRECTIONS; i++)
2394 int x1 = x + xy[i][0];
2395 int y1 = y + xy[i][1];
2397 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2399 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2401 MovDir[x][y] = direction[0][i];
2404 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2405 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2407 MovDir[x][y] = direction[1][i];
2416 GfxDir[x][y] = MovDir[x][y];
2419 void InitAmoebaNr(int x, int y)
2422 int group_nr = AmoebeNachbarNr(x, y);
2426 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2428 if (AmoebaCnt[i] == 0)
2436 AmoebaNr[x][y] = group_nr;
2437 AmoebaCnt[group_nr]++;
2438 AmoebaCnt2[group_nr]++;
2444 boolean raise_level = FALSE;
2446 if (local_player->MovPos)
2450 if (tape.auto_play) /* tape might already be stopped here */
2451 tape.auto_play_level_solved = TRUE;
2453 if (tape.playing && tape.auto_play)
2454 tape.auto_play_level_solved = TRUE;
2457 local_player->LevelSolved = FALSE;
2459 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2463 if (!tape.playing && setup.sound_loops)
2464 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2465 SND_CTRL_PLAY_LOOP);
2467 while (TimeLeft > 0)
2469 if (!tape.playing && !setup.sound_loops)
2470 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2471 if (TimeLeft > 0 && !(TimeLeft % 10))
2472 RaiseScore(level.score[SC_TIME_BONUS]);
2473 if (TimeLeft > 100 && !(TimeLeft % 10))
2478 DrawGameValue_Time(TimeLeft);
2486 if (!tape.playing && setup.sound_loops)
2487 StopSound(SND_GAME_LEVELTIME_BONUS);
2489 else if (level.time == 0) /* level without time limit */
2491 if (!tape.playing && setup.sound_loops)
2492 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2493 SND_CTRL_PLAY_LOOP);
2495 while (TimePlayed < 999)
2497 if (!tape.playing && !setup.sound_loops)
2498 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2499 if (TimePlayed < 999 && !(TimePlayed % 10))
2500 RaiseScore(level.score[SC_TIME_BONUS]);
2501 if (TimePlayed < 900 && !(TimePlayed % 10))
2506 DrawGameValue_Time(TimePlayed);
2514 if (!tape.playing && setup.sound_loops)
2515 StopSound(SND_GAME_LEVELTIME_BONUS);
2518 /* close exit door after last player */
2519 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2520 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2521 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2523 int element = Feld[ExitX][ExitY];
2525 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2526 EL_SP_EXIT_CLOSING);
2528 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2531 /* Hero disappears */
2532 if (ExitX >= 0 && ExitY >= 0)
2533 DrawLevelField(ExitX, ExitY);
2540 CloseDoor(DOOR_CLOSE_1);
2545 SaveTape(tape.level_nr); /* Ask to save tape */
2548 if (level_nr == leveldir_current->handicap_level)
2550 leveldir_current->handicap_level++;
2551 SaveLevelSetup_SeriesInfo();
2554 if (level_editor_test_game)
2555 local_player->score = -1; /* no highscore when playing from editor */
2556 else if (level_nr < leveldir_current->last_level)
2557 raise_level = TRUE; /* advance to next level */
2559 if ((hi_pos = NewHiScore()) >= 0)
2561 game_status = GAME_MODE_SCORES;
2562 DrawHallOfFame(hi_pos);
2571 game_status = GAME_MODE_MAIN;
2588 LoadScore(level_nr);
2590 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2591 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2594 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2596 if (local_player->score > highscore[k].Score)
2598 /* player has made it to the hall of fame */
2600 if (k < MAX_SCORE_ENTRIES - 1)
2602 int m = MAX_SCORE_ENTRIES - 1;
2605 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2606 if (!strcmp(setup.player_name, highscore[l].Name))
2608 if (m == k) /* player's new highscore overwrites his old one */
2612 for (l = m; l > k; l--)
2614 strcpy(highscore[l].Name, highscore[l - 1].Name);
2615 highscore[l].Score = highscore[l - 1].Score;
2622 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2623 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2624 highscore[k].Score = local_player->score;
2630 else if (!strncmp(setup.player_name, highscore[k].Name,
2631 MAX_PLAYER_NAME_LEN))
2632 break; /* player already there with a higher score */
2638 SaveScore(level_nr);
2643 inline static int getElementMoveStepsize(int x, int y)
2645 int element = Feld[x][y];
2646 int direction = MovDir[x][y];
2647 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2648 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2649 int horiz_move = (dx != 0);
2650 int sign = (horiz_move ? dx : dy);
2651 int step = sign * element_info[element].move_stepsize;
2653 /* special values for move stepsize for spring and things on conveyor belt */
2657 if (element == EL_SPRING)
2658 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2659 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2660 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2661 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2663 if (CAN_FALL(element) &&
2664 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2665 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2666 else if (element == EL_SPRING)
2667 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2674 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2676 if (player->GfxAction != action || player->GfxDir != dir)
2679 printf("Player frame reset! (%d => %d, %d => %d)\n",
2680 player->GfxAction, action, player->GfxDir, dir);
2683 player->GfxAction = action;
2684 player->GfxDir = dir;
2686 player->StepFrame = 0;
2690 static void ResetRandomAnimationValue(int x, int y)
2692 GfxRandom[x][y] = INIT_GFX_RANDOM();
2695 static void ResetGfxAnimation(int x, int y)
2698 GfxAction[x][y] = ACTION_DEFAULT;
2699 GfxDir[x][y] = MovDir[x][y];
2702 void InitMovingField(int x, int y, int direction)
2704 int element = Feld[x][y];
2705 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2706 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2710 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2711 ResetGfxAnimation(x, y);
2713 #if USE_CAN_MOVE_NOT_MOVING
2715 MovDir[x][y] = direction;
2716 GfxDir[x][y] = direction;
2717 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2718 ACTION_FALLING : ACTION_MOVING);
2720 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2722 if (Feld[newx][newy] == EL_EMPTY)
2723 Feld[newx][newy] = EL_BLOCKED;
2725 MovDir[newx][newy] = MovDir[x][y];
2726 GfxFrame[newx][newy] = GfxFrame[x][y];
2727 GfxRandom[newx][newy] = GfxRandom[x][y];
2728 GfxAction[newx][newy] = GfxAction[x][y];
2729 GfxDir[newx][newy] = GfxDir[x][y];
2734 MovDir[newx][newy] = MovDir[x][y] = direction;
2735 GfxDir[x][y] = direction;
2737 if (Feld[newx][newy] == EL_EMPTY)
2738 Feld[newx][newy] = EL_BLOCKED;
2740 if (direction == MV_DOWN && CAN_FALL(element))
2741 GfxAction[x][y] = ACTION_FALLING;
2743 GfxAction[x][y] = ACTION_MOVING;
2745 GfxFrame[newx][newy] = GfxFrame[x][y];
2746 GfxRandom[newx][newy] = GfxRandom[x][y];
2747 GfxAction[newx][newy] = GfxAction[x][y];
2748 GfxDir[newx][newy] = GfxDir[x][y];
2752 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2754 int direction = MovDir[x][y];
2755 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2756 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2762 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2764 int oldx = x, oldy = y;
2765 int direction = MovDir[x][y];
2767 if (direction == MV_LEFT)
2769 else if (direction == MV_RIGHT)
2771 else if (direction == MV_UP)
2773 else if (direction == MV_DOWN)
2776 *comes_from_x = oldx;
2777 *comes_from_y = oldy;
2780 int MovingOrBlocked2Element(int x, int y)
2782 int element = Feld[x][y];
2784 if (element == EL_BLOCKED)
2788 Blocked2Moving(x, y, &oldx, &oldy);
2789 return Feld[oldx][oldy];
2795 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2797 /* like MovingOrBlocked2Element(), but if element is moving
2798 and (x,y) is the field the moving element is just leaving,
2799 return EL_BLOCKED instead of the element value */
2800 int element = Feld[x][y];
2802 if (IS_MOVING(x, y))
2804 if (element == EL_BLOCKED)
2808 Blocked2Moving(x, y, &oldx, &oldy);
2809 return Feld[oldx][oldy];
2818 static void RemoveField(int x, int y)
2820 Feld[x][y] = EL_EMPTY;
2827 ChangeDelay[x][y] = 0;
2828 ChangePage[x][y] = -1;
2829 Pushed[x][y] = FALSE;
2832 ExplodeField[x][y] = EX_TYPE_NONE;
2835 GfxElement[x][y] = EL_UNDEFINED;
2836 GfxAction[x][y] = ACTION_DEFAULT;
2837 GfxDir[x][y] = MV_NO_MOVING;
2840 void RemoveMovingField(int x, int y)
2842 int oldx = x, oldy = y, newx = x, newy = y;
2843 int element = Feld[x][y];
2844 int next_element = EL_UNDEFINED;
2846 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2849 if (IS_MOVING(x, y))
2851 Moving2Blocked(x, y, &newx, &newy);
2853 if (Feld[newx][newy] != EL_BLOCKED)
2856 if (Feld[newx][newy] != EL_BLOCKED)
2858 /* element is moving, but target field is not free (blocked), but
2859 already occupied by something different (example: acid pool);
2860 in this case, only remove the moving field, but not the target */
2862 RemoveField(oldx, oldy);
2864 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2866 DrawLevelField(oldx, oldy);
2872 else if (element == EL_BLOCKED)
2874 Blocked2Moving(x, y, &oldx, &oldy);
2875 if (!IS_MOVING(oldx, oldy))
2879 if (element == EL_BLOCKED &&
2880 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2881 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2882 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2883 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2884 next_element = get_next_element(Feld[oldx][oldy]);
2886 RemoveField(oldx, oldy);
2887 RemoveField(newx, newy);
2889 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2891 if (next_element != EL_UNDEFINED)
2892 Feld[oldx][oldy] = next_element;
2894 DrawLevelField(oldx, oldy);
2895 DrawLevelField(newx, newy);
2898 void DrawDynamite(int x, int y)
2900 int sx = SCREENX(x), sy = SCREENY(y);
2901 int graphic = el2img(Feld[x][y]);
2904 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2907 if (IS_WALKABLE_INSIDE(Back[x][y]))
2911 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2912 else if (Store[x][y])
2913 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2915 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2918 if (Back[x][y] || Store[x][y])
2919 DrawGraphicThruMask(sx, sy, graphic, frame);
2921 DrawGraphic(sx, sy, graphic, frame);
2923 if (game.emulation == EMU_SUPAPLEX)
2924 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2925 else if (Store[x][y])
2926 DrawGraphicThruMask(sx, sy, graphic, frame);
2928 DrawGraphic(sx, sy, graphic, frame);
2932 void CheckDynamite(int x, int y)
2934 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2938 if (MovDelay[x][y] != 0)
2941 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2948 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2950 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2951 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2952 StopSound(SND_DYNAMITE_ACTIVE);
2954 StopSound(SND_DYNABOMB_ACTIVE);
2960 void DrawRelocatePlayer(struct PlayerInfo *player)
2962 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2963 boolean no_delay = (tape.warp_forward);
2964 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2965 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2966 int jx = player->jx;
2967 int jy = player->jy;
2969 if (level.instant_relocation)
2972 int offset = (setup.scroll_delay ? 3 : 0);
2974 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2976 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2977 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2978 local_player->jx - MIDPOSX);
2980 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2981 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2982 local_player->jy - MIDPOSY);
2986 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2987 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2988 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2990 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2991 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2992 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2994 /* don't scroll over playfield boundaries */
2995 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2996 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2998 /* don't scroll over playfield boundaries */
2999 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3000 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3003 scroll_x += (local_player->jx - old_jx);
3004 scroll_y += (local_player->jy - old_jy);
3006 /* don't scroll over playfield boundaries */
3007 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3008 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3010 /* don't scroll over playfield boundaries */
3011 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3012 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3015 RedrawPlayfield(TRUE, 0,0,0,0);
3021 int offset = (setup.scroll_delay ? 3 : 0);
3023 int scroll_xx = -999, scroll_yy = -999;
3025 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3027 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3030 int fx = FX, fy = FY;
3032 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3033 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3034 local_player->jx - MIDPOSX);
3036 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3037 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3038 local_player->jy - MIDPOSY);
3040 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3041 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3044 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3047 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3054 fx += dx * TILEX / 2;
3055 fy += dy * TILEY / 2;
3057 ScrollLevel(dx, dy);
3060 /* scroll in two steps of half tile size to make things smoother */
3061 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3063 Delay(wait_delay_value);
3065 /* scroll second step to align at full tile size */
3067 Delay(wait_delay_value);
3070 int scroll_xx = -999, scroll_yy = -999;
3072 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3074 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3077 int fx = FX, fy = FY;
3079 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3080 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3081 local_player->jx - MIDPOSX);
3083 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3084 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3085 local_player->jy - MIDPOSY);
3087 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3088 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3091 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3094 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3101 fx += dx * TILEX / 2;
3102 fy += dy * TILEY / 2;
3104 ScrollLevel(dx, dy);
3107 /* scroll in two steps of half tile size to make things smoother */
3108 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3110 Delay(wait_delay_value);
3112 /* scroll second step to align at full tile size */
3114 Delay(wait_delay_value);
3120 Delay(wait_delay_value);
3124 void RelocatePlayer(int jx, int jy, int el_player_raw)
3127 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3129 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3131 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3132 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3133 boolean no_delay = (tape.warp_forward);
3134 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3135 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3136 int old_jx = player->jx;
3137 int old_jy = player->jy;
3138 int old_element = Feld[old_jx][old_jy];
3139 int element = Feld[jx][jy];
3140 boolean player_relocated = (old_jx != jx || old_jy != jy);
3142 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3143 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3145 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3146 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3147 int leave_side_horiz = move_dir_horiz;
3148 int leave_side_vert = move_dir_vert;
3150 static int trigger_sides[4][2] =
3152 /* enter side leave side */
3153 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
3154 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
3155 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
3156 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
3158 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3159 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3160 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3161 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3163 int enter_side = enter_side_horiz | enter_side_vert;
3164 int leave_side = leave_side_horiz | leave_side_vert;
3166 if (player->GameOver) /* do not reanimate dead player */
3169 if (!player_relocated) /* no need to relocate the player */
3172 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3174 RemoveField(jx, jy); /* temporarily remove newly placed player */
3175 DrawLevelField(jx, jy);
3178 if (player->present)
3180 while (player->MovPos)
3182 ScrollPlayer(player, SCROLL_GO_ON);
3183 ScrollScreen(NULL, SCROLL_GO_ON);
3185 #if USE_NEW_MOVE_DELAY
3186 AdvanceFrameAndPlayerCounters(player->index_nr);
3194 Delay(wait_delay_value);
3197 DrawPlayer(player); /* needed here only to cleanup last field */
3198 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3200 player->is_moving = FALSE;
3204 if (IS_CUSTOM_ELEMENT(old_element))
3205 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3207 player->index_bit, leave_side);
3209 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3211 player->index_bit, leave_side);
3214 Feld[jx][jy] = el_player;
3215 InitPlayerField(jx, jy, el_player, TRUE);
3217 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3219 Feld[jx][jy] = element;
3220 InitField(jx, jy, FALSE);
3224 if (player == local_player) /* only visually relocate local player */
3225 DrawRelocatePlayer(player);
3229 TestIfHeroTouchesBadThing(jx, jy);
3230 TestIfPlayerTouchesCustomElement(jx, jy);
3234 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3239 /* needed to allow change of walkable custom element by entering player */
3240 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3241 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3243 /* needed to allow change of walkable custom element by entering player */
3244 Changed[jx][jy] = 0; /* allow another change */
3249 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3250 enter_side == MV_LEFT ? "left" :
3251 enter_side == MV_RIGHT ? "right" :
3252 enter_side == MV_UP ? "top" :
3253 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3257 if (IS_CUSTOM_ELEMENT(element))
3258 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3259 player->index_bit, enter_side);
3261 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3262 CE_OTHER_GETS_ENTERED,
3263 player->index_bit, enter_side);
3267 void Explode(int ex, int ey, int phase, int mode)
3274 /* !!! eliminate this variable !!! */
3275 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3280 int last_phase = num_phase * delay;
3281 int half_phase = (num_phase / 2) * delay;
3282 int first_phase_after_start = EX_PHASE_START + 1;
3286 if (game.explosions_delayed)
3288 ExplodeField[ex][ey] = mode;
3292 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3294 int center_element = Feld[ex][ey];
3297 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3301 /* --- This is only really needed (and now handled) in "Impact()". --- */
3302 /* do not explode moving elements that left the explode field in time */
3303 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3304 center_element == EL_EMPTY &&
3305 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3310 if (mode == EX_TYPE_NORMAL ||
3311 mode == EX_TYPE_CENTER ||
3312 mode == EX_TYPE_CROSS)
3313 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3315 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3316 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3319 /* remove things displayed in background while burning dynamite */
3320 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3323 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3325 /* put moving element to center field (and let it explode there) */
3326 center_element = MovingOrBlocked2Element(ex, ey);
3327 RemoveMovingField(ex, ey);
3328 Feld[ex][ey] = center_element;
3334 last_phase = element_info[center_element].explosion_delay + 1;
3336 last_phase = element_info[center_element].explosion_delay;
3340 printf("::: %d -> %d\n", center_element, last_phase);
3344 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3346 int xx = x - ex + 1;
3347 int yy = y - ey + 1;
3352 if (!IN_LEV_FIELD(x, y) ||
3353 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3354 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3357 if (!IN_LEV_FIELD(x, y) ||
3358 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3362 if (!IN_LEV_FIELD(x, y) ||
3363 ((mode != EX_TYPE_NORMAL ||
3364 center_element == EL_AMOEBA_TO_DIAMOND) &&
3365 (x != ex || y != ey)))
3369 element = Feld[x][y];
3371 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3373 element = MovingOrBlocked2Element(x, y);
3375 if (!IS_EXPLOSION_PROOF(element))
3376 RemoveMovingField(x, y);
3382 if (IS_EXPLOSION_PROOF(element))
3385 /* indestructible elements can only explode in center (but not flames) */
3387 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3388 mode == EX_TYPE_BORDER)) ||
3389 element == EL_FLAMES)
3392 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3393 element == EL_FLAMES)
3399 if ((IS_INDESTRUCTIBLE(element) &&
3400 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3401 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3402 element == EL_FLAMES)
3406 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3407 behaviour, for example when touching a yamyam that explodes to rocks
3408 with active deadly shield, a rock is created under the player !!! */
3409 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3411 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3412 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3413 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3415 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3418 if (IS_ACTIVE_BOMB(element))
3420 /* re-activate things under the bomb like gate or penguin */
3422 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3425 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3430 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3431 element_info[Feld[x][y]].token_name,
3432 Store[x][y], Store2[x][y]);
3439 /* save walkable background elements while explosion on same tile */
3441 if (IS_INDESTRUCTIBLE(element))
3442 Back[x][y] = element;
3446 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3447 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3448 Back[x][y] = element;
3450 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3451 (x != ex || y != ey))
3452 Back[x][y] = element;
3455 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3456 Back[x][y] = element;
3460 /* ignite explodable elements reached by other explosion */
3461 if (element == EL_EXPLOSION)
3462 element = Store2[x][y];
3465 if (AmoebaNr[x][y] &&
3466 (element == EL_AMOEBA_FULL ||
3467 element == EL_BD_AMOEBA ||
3468 element == EL_AMOEBA_GROWING))
3470 AmoebaCnt[AmoebaNr[x][y]]--;
3471 AmoebaCnt2[AmoebaNr[x][y]]--;
3477 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3479 switch(StorePlayer[ex][ey])
3482 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3485 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3488 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3492 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3497 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3498 Store[x][y] = EL_EMPTY;
3500 if (game.emulation == EMU_SUPAPLEX)
3501 Store[x][y] = EL_EMPTY;
3504 else if (center_element == EL_MOLE)
3505 Store[x][y] = EL_EMERALD_RED;
3506 else if (center_element == EL_PENGUIN)
3507 Store[x][y] = EL_EMERALD_PURPLE;
3508 else if (center_element == EL_BUG)
3509 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3510 else if (center_element == EL_BD_BUTTERFLY)
3511 Store[x][y] = EL_BD_DIAMOND;
3512 else if (center_element == EL_SP_ELECTRON)
3513 Store[x][y] = EL_SP_INFOTRON;
3514 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3515 Store[x][y] = level.amoeba_content;
3516 else if (center_element == EL_YAMYAM)
3517 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3518 else if (IS_CUSTOM_ELEMENT(center_element) &&
3519 element_info[center_element].content[xx][yy] != EL_EMPTY)
3520 Store[x][y] = element_info[center_element].content[xx][yy];
3521 else if (element == EL_WALL_EMERALD)
3522 Store[x][y] = EL_EMERALD;
3523 else if (element == EL_WALL_DIAMOND)
3524 Store[x][y] = EL_DIAMOND;
3525 else if (element == EL_WALL_BD_DIAMOND)
3526 Store[x][y] = EL_BD_DIAMOND;
3527 else if (element == EL_WALL_EMERALD_YELLOW)
3528 Store[x][y] = EL_EMERALD_YELLOW;
3529 else if (element == EL_WALL_EMERALD_RED)
3530 Store[x][y] = EL_EMERALD_RED;
3531 else if (element == EL_WALL_EMERALD_PURPLE)
3532 Store[x][y] = EL_EMERALD_PURPLE;
3533 else if (element == EL_WALL_PEARL)
3534 Store[x][y] = EL_PEARL;
3535 else if (element == EL_WALL_CRYSTAL)
3536 Store[x][y] = EL_CRYSTAL;
3537 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3538 Store[x][y] = element_info[element].content[1][1];
3540 Store[x][y] = EL_EMPTY;
3542 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3543 center_element == EL_AMOEBA_TO_DIAMOND)
3544 Store2[x][y] = element;
3547 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3548 element_info[Store2[x][y]].token_name);
3552 if (AmoebaNr[x][y] &&
3553 (element == EL_AMOEBA_FULL ||
3554 element == EL_BD_AMOEBA ||
3555 element == EL_AMOEBA_GROWING))
3557 AmoebaCnt[AmoebaNr[x][y]]--;
3558 AmoebaCnt2[AmoebaNr[x][y]]--;
3564 MovDir[x][y] = MovPos[x][y] = 0;
3565 GfxDir[x][y] = MovDir[x][y];
3570 Feld[x][y] = EL_EXPLOSION;
3572 GfxElement[x][y] = center_element;
3574 GfxElement[x][y] = EL_UNDEFINED;
3577 ExplodePhase[x][y] = 1;
3579 ExplodeDelay[x][y] = last_phase;
3584 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3586 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3593 if (center_element == EL_YAMYAM)
3594 game.yamyam_content_nr =
3595 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3598 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3599 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3613 GfxFrame[x][y] = 0; /* restart explosion animation */
3617 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3621 last_phase = ExplodeDelay[x][y];
3624 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3628 /* activate this even in non-DEBUG version until cause for crash in
3629 getGraphicAnimationFrame() (see below) is found and eliminated */
3633 if (GfxElement[x][y] == EL_UNDEFINED)
3636 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3637 printf("Explode(): This should never happen!\n");
3640 GfxElement[x][y] = EL_EMPTY;
3646 border_element = Store2[x][y];
3648 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3649 border_element = StorePlayer[x][y];
3651 if (IS_PLAYER(x, y))
3652 border_element = StorePlayer[x][y];
3656 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3657 element_info[border_element].token_name, Store2[x][y]);
3661 printf("::: phase == %d\n", phase);
3664 if (phase == element_info[border_element].ignition_delay ||
3665 phase == last_phase)
3667 boolean border_explosion = FALSE;
3671 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3672 !PLAYER_EXPLOSION_PROTECTED(x, y))
3674 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3677 if (IS_PLAYER(x, y))
3680 KillHeroUnlessExplosionProtected(x, y);
3681 border_explosion = TRUE;
3684 if (phase == last_phase)
3685 printf("::: IS_PLAYER\n");
3688 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3691 printf("::: %d,%d: %d %s\n", x, y, border_element,
3692 element_info[border_element].token_name);
3695 Feld[x][y] = Store2[x][y];
3698 border_explosion = TRUE;
3701 if (phase == last_phase)
3702 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3705 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3707 AmoebeUmwandeln(x, y);
3709 border_explosion = TRUE;
3712 if (phase == last_phase)
3713 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3714 element_info[border_element].explosion_delay,
3715 element_info[border_element].ignition_delay,
3721 /* if an element just explodes due to another explosion (chain-reaction),
3722 do not immediately end the new explosion when it was the last frame of
3723 the explosion (as it would be done in the following "if"-statement!) */
3724 if (border_explosion && phase == last_phase)
3731 if (phase == first_phase_after_start)
3733 int element = Store2[x][y];
3735 if (element == EL_BLACK_ORB)
3737 Feld[x][y] = Store2[x][y];
3742 else if (phase == half_phase)
3744 int element = Store2[x][y];
3746 if (IS_PLAYER(x, y))
3747 KillHeroUnlessExplosionProtected(x, y);
3748 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3750 Feld[x][y] = Store2[x][y];
3754 else if (element == EL_AMOEBA_TO_DIAMOND)
3755 AmoebeUmwandeln(x, y);
3759 if (phase == last_phase)
3764 printf("::: done: phase == %d\n", phase);
3768 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3771 element = Feld[x][y] = Store[x][y];
3772 Store[x][y] = Store2[x][y] = 0;
3773 GfxElement[x][y] = EL_UNDEFINED;
3775 /* player can escape from explosions and might therefore be still alive */
3776 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3777 element <= EL_PLAYER_IS_EXPLODING_4)
3778 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3780 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3781 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3782 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3785 /* restore probably existing indestructible background element */
3786 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3787 element = Feld[x][y] = Back[x][y];
3790 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3791 GfxDir[x][y] = MV_NO_MOVING;
3792 ChangeDelay[x][y] = 0;
3793 ChangePage[x][y] = -1;
3796 InitField_WithBug2(x, y, FALSE);
3798 InitField(x, y, FALSE);
3800 /* !!! not needed !!! */
3802 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3803 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3806 if (CAN_MOVE(element))
3811 DrawLevelField(x, y);
3813 TestIfElementTouchesCustomElement(x, y);
3815 if (GFX_CRUMBLED(element))
3816 DrawLevelFieldCrumbledSandNeighbours(x, y);
3818 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3819 StorePlayer[x][y] = 0;
3821 if (ELEM_IS_PLAYER(element))
3822 RelocatePlayer(x, y, element);
3825 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3827 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3831 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3833 int stored = Store[x][y];
3834 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3835 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3839 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3841 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3845 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3849 printf("::: %d / %d [%d - %d]\n",
3850 GfxFrame[x][y], phase - delay, phase, delay);
3854 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3855 element_info[GfxElement[x][y]].token_name,
3860 DrawLevelFieldCrumbledSand(x, y);
3862 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3864 DrawLevelElement(x, y, Back[x][y]);
3865 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3867 else if (IS_WALKABLE_UNDER(Back[x][y]))
3869 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3870 DrawLevelElementThruMask(x, y, Back[x][y]);
3872 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3873 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3877 void DynaExplode(int ex, int ey)
3880 int dynabomb_element = Feld[ex][ey];
3881 int dynabomb_size = 1;
3882 boolean dynabomb_xl = FALSE;
3883 struct PlayerInfo *player;
3884 static int xy[4][2] =
3892 if (IS_ACTIVE_BOMB(dynabomb_element))
3894 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3895 dynabomb_size = player->dynabomb_size;
3896 dynabomb_xl = player->dynabomb_xl;
3897 player->dynabombs_left++;
3900 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3902 for (i = 0; i < NUM_DIRECTIONS; i++)
3904 for (j = 1; j <= dynabomb_size; j++)
3906 int x = ex + j * xy[i][0];
3907 int y = ey + j * xy[i][1];
3910 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3913 element = Feld[x][y];
3915 /* do not restart explosions of fields with active bombs */
3916 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3919 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3923 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3924 !IS_DIGGABLE(element) && !dynabomb_xl)
3927 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3928 !CAN_GROW_INTO(element) && !dynabomb_xl)
3932 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3933 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3934 element != EL_SAND && !dynabomb_xl)
3941 void Bang(int x, int y)
3944 int element = MovingOrBlocked2Element(x, y);
3946 int element = Feld[x][y];
3950 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3952 if (IS_PLAYER(x, y))
3955 struct PlayerInfo *player = PLAYERINFO(x, y);
3957 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3958 player->element_nr);
3963 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3965 if (game.emulation == EMU_SUPAPLEX)
3966 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3968 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3973 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3981 case EL_BD_BUTTERFLY:
3984 case EL_DARK_YAMYAM:
3988 RaiseScoreElement(element);
3989 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3991 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3992 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3993 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3994 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3995 case EL_DYNABOMB_INCREASE_NUMBER:
3996 case EL_DYNABOMB_INCREASE_SIZE:
3997 case EL_DYNABOMB_INCREASE_POWER:
4002 case EL_LAMP_ACTIVE:
4004 case EL_AMOEBA_TO_DIAMOND:
4006 if (IS_PLAYER(x, y))
4007 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4009 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4013 if (element_info[element].explosion_type == EXPLODES_CROSS)
4015 if (CAN_EXPLODE_CROSS(element))
4018 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4023 else if (element_info[element].explosion_type == EXPLODES_1X1)
4025 else if (CAN_EXPLODE_1X1(element))
4027 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4029 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4033 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
4036 void SplashAcid(int x, int y)
4039 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4040 (!IN_LEV_FIELD(x - 1, y - 2) ||
4041 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4042 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4044 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4045 (!IN_LEV_FIELD(x + 1, y - 2) ||
4046 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4047 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4049 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4051 /* input: position of element entering acid (obsolete) */
4053 int element = Feld[x][y];
4055 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4058 if (element != EL_ACID_SPLASH_LEFT &&
4059 element != EL_ACID_SPLASH_RIGHT)
4061 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4063 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4064 (!IN_LEV_FIELD(x - 1, y - 1) ||
4065 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4066 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4068 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4069 (!IN_LEV_FIELD(x + 1, y - 1) ||
4070 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4071 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4076 static void InitBeltMovement()
4078 static int belt_base_element[4] =
4080 EL_CONVEYOR_BELT_1_LEFT,
4081 EL_CONVEYOR_BELT_2_LEFT,
4082 EL_CONVEYOR_BELT_3_LEFT,
4083 EL_CONVEYOR_BELT_4_LEFT
4085 static int belt_base_active_element[4] =
4087 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4088 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4089 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4090 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4095 /* set frame order for belt animation graphic according to belt direction */
4096 for (i = 0; i < NUM_BELTS; i++)
4100 for (j = 0; j < NUM_BELT_PARTS; j++)
4102 int element = belt_base_active_element[belt_nr] + j;
4103 int graphic = el2img(element);
4105 if (game.belt_dir[i] == MV_LEFT)
4106 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4108 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4112 for (y = 0; y < lev_fieldy; y++)
4114 for (x = 0; x < lev_fieldx; x++)
4116 int element = Feld[x][y];
4118 for (i = 0; i < NUM_BELTS; i++)
4120 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4122 int e_belt_nr = getBeltNrFromBeltElement(element);
4125 if (e_belt_nr == belt_nr)
4127 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4129 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4137 static void ToggleBeltSwitch(int x, int y)
4139 static int belt_base_element[4] =
4141 EL_CONVEYOR_BELT_1_LEFT,
4142 EL_CONVEYOR_BELT_2_LEFT,
4143 EL_CONVEYOR_BELT_3_LEFT,
4144 EL_CONVEYOR_BELT_4_LEFT
4146 static int belt_base_active_element[4] =
4148 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4149 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4150 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4151 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4153 static int belt_base_switch_element[4] =
4155 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4156 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4157 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4158 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4160 static int belt_move_dir[4] =
4168 int element = Feld[x][y];
4169 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4170 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4171 int belt_dir = belt_move_dir[belt_dir_nr];
4174 if (!IS_BELT_SWITCH(element))
4177 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4178 game.belt_dir[belt_nr] = belt_dir;
4180 if (belt_dir_nr == 3)
4183 /* set frame order for belt animation graphic according to belt direction */
4184 for (i = 0; i < NUM_BELT_PARTS; i++)
4186 int element = belt_base_active_element[belt_nr] + i;
4187 int graphic = el2img(element);
4189 if (belt_dir == MV_LEFT)
4190 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4192 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4195 for (yy = 0; yy < lev_fieldy; yy++)
4197 for (xx = 0; xx < lev_fieldx; xx++)
4199 int element = Feld[xx][yy];
4201 if (IS_BELT_SWITCH(element))
4203 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4205 if (e_belt_nr == belt_nr)
4207 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4208 DrawLevelField(xx, yy);
4211 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4213 int e_belt_nr = getBeltNrFromBeltElement(element);
4215 if (e_belt_nr == belt_nr)
4217 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4219 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4220 DrawLevelField(xx, yy);
4223 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4225 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4227 if (e_belt_nr == belt_nr)
4229 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4231 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4232 DrawLevelField(xx, yy);
4239 static void ToggleSwitchgateSwitch(int x, int y)
4243 game.switchgate_pos = !game.switchgate_pos;
4245 for (yy = 0; yy < lev_fieldy; yy++)
4247 for (xx = 0; xx < lev_fieldx; xx++)
4249 int element = Feld[xx][yy];
4251 if (element == EL_SWITCHGATE_SWITCH_UP ||
4252 element == EL_SWITCHGATE_SWITCH_DOWN)
4254 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4255 DrawLevelField(xx, yy);
4257 else if (element == EL_SWITCHGATE_OPEN ||
4258 element == EL_SWITCHGATE_OPENING)
4260 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4262 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4264 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4267 else if (element == EL_SWITCHGATE_CLOSED ||
4268 element == EL_SWITCHGATE_CLOSING)
4270 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4272 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4274 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4281 static int getInvisibleActiveFromInvisibleElement(int element)
4283 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4284 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4285 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4289 static int getInvisibleFromInvisibleActiveElement(int element)
4291 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4292 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4293 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4297 static void RedrawAllLightSwitchesAndInvisibleElements()
4301 for (y = 0; y < lev_fieldy; y++)
4303 for (x = 0; x < lev_fieldx; x++)
4305 int element = Feld[x][y];
4307 if (element == EL_LIGHT_SWITCH &&
4308 game.light_time_left > 0)
4310 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4311 DrawLevelField(x, y);
4313 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4314 game.light_time_left == 0)
4316 Feld[x][y] = EL_LIGHT_SWITCH;
4317 DrawLevelField(x, y);
4319 else if (element == EL_INVISIBLE_STEELWALL ||
4320 element == EL_INVISIBLE_WALL ||
4321 element == EL_INVISIBLE_SAND)
4323 if (game.light_time_left > 0)
4324 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4326 DrawLevelField(x, y);
4328 /* uncrumble neighbour fields, if needed */
4329 if (element == EL_INVISIBLE_SAND)
4330 DrawLevelFieldCrumbledSandNeighbours(x, y);
4332 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4333 element == EL_INVISIBLE_WALL_ACTIVE ||
4334 element == EL_INVISIBLE_SAND_ACTIVE)
4336 if (game.light_time_left == 0)
4337 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4339 DrawLevelField(x, y);
4341 /* re-crumble neighbour fields, if needed */
4342 if (element == EL_INVISIBLE_SAND)
4343 DrawLevelFieldCrumbledSandNeighbours(x, y);
4349 static void ToggleLightSwitch(int x, int y)
4351 int element = Feld[x][y];
4353 game.light_time_left =
4354 (element == EL_LIGHT_SWITCH ?
4355 level.time_light * FRAMES_PER_SECOND : 0);
4357 RedrawAllLightSwitchesAndInvisibleElements();
4360 static void ActivateTimegateSwitch(int x, int y)
4364 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4366 for (yy = 0; yy < lev_fieldy; yy++)
4368 for (xx = 0; xx < lev_fieldx; xx++)
4370 int element = Feld[xx][yy];
4372 if (element == EL_TIMEGATE_CLOSED ||
4373 element == EL_TIMEGATE_CLOSING)
4375 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4376 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4380 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4382 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4383 DrawLevelField(xx, yy);
4390 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4393 void Impact(int x, int y)
4395 boolean lastline = (y == lev_fieldy-1);
4396 boolean object_hit = FALSE;
4397 boolean impact = (lastline || object_hit);
4398 int element = Feld[x][y];
4399 int smashed = EL_STEELWALL;
4401 if (!lastline) /* check if element below was hit */
4403 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4406 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4407 MovDir[x][y + 1] != MV_DOWN ||
4408 MovPos[x][y + 1] <= TILEY / 2));
4411 object_hit = !IS_FREE(x, y + 1);
4414 /* do not smash moving elements that left the smashed field in time */
4415 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4416 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4420 smashed = MovingOrBlocked2Element(x, y + 1);
4422 impact = (lastline || object_hit);
4425 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4427 SplashAcid(x, y + 1);
4431 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4432 /* only reset graphic animation if graphic really changes after impact */
4434 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4436 ResetGfxAnimation(x, y);
4437 DrawLevelField(x, y);
4440 if (impact && CAN_EXPLODE_IMPACT(element))
4445 else if (impact && element == EL_PEARL)
4447 ResetGfxAnimation(x, y);
4449 Feld[x][y] = EL_PEARL_BREAKING;
4450 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4453 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4455 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4460 if (impact && element == EL_AMOEBA_DROP)
4462 if (object_hit && IS_PLAYER(x, y + 1))
4463 KillHeroUnlessEnemyProtected(x, y + 1);
4464 else if (object_hit && smashed == EL_PENGUIN)
4468 Feld[x][y] = EL_AMOEBA_GROWING;
4469 Store[x][y] = EL_AMOEBA_WET;
4471 ResetRandomAnimationValue(x, y);
4476 if (object_hit) /* check which object was hit */
4478 if (CAN_PASS_MAGIC_WALL(element) &&
4479 (smashed == EL_MAGIC_WALL ||
4480 smashed == EL_BD_MAGIC_WALL))
4483 int activated_magic_wall =
4484 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4485 EL_BD_MAGIC_WALL_ACTIVE);
4487 /* activate magic wall / mill */
4488 for (yy = 0; yy < lev_fieldy; yy++)
4489 for (xx = 0; xx < lev_fieldx; xx++)
4490 if (Feld[xx][yy] == smashed)
4491 Feld[xx][yy] = activated_magic_wall;
4493 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4494 game.magic_wall_active = TRUE;
4496 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4497 SND_MAGIC_WALL_ACTIVATING :
4498 SND_BD_MAGIC_WALL_ACTIVATING));
4501 if (IS_PLAYER(x, y + 1))
4503 if (CAN_SMASH_PLAYER(element))
4505 KillHeroUnlessEnemyProtected(x, y + 1);
4509 else if (smashed == EL_PENGUIN)
4511 if (CAN_SMASH_PLAYER(element))
4517 else if (element == EL_BD_DIAMOND)
4519 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4525 else if (((element == EL_SP_INFOTRON ||
4526 element == EL_SP_ZONK) &&
4527 (smashed == EL_SP_SNIKSNAK ||
4528 smashed == EL_SP_ELECTRON ||
4529 smashed == EL_SP_DISK_ORANGE)) ||
4530 (element == EL_SP_INFOTRON &&
4531 smashed == EL_SP_DISK_YELLOW))
4537 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4543 else if (CAN_SMASH_EVERYTHING(element))
4545 if (IS_CLASSIC_ENEMY(smashed) ||
4546 CAN_EXPLODE_SMASHED(smashed))
4551 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4553 if (smashed == EL_LAMP ||
4554 smashed == EL_LAMP_ACTIVE)
4559 else if (smashed == EL_NUT)
4561 Feld[x][y + 1] = EL_NUT_BREAKING;
4562 PlayLevelSound(x, y, SND_NUT_BREAKING);
4563 RaiseScoreElement(EL_NUT);
4566 else if (smashed == EL_PEARL)
4568 ResetGfxAnimation(x, y);
4570 Feld[x][y + 1] = EL_PEARL_BREAKING;
4571 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4574 else if (smashed == EL_DIAMOND)
4576 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4577 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4580 else if (IS_BELT_SWITCH(smashed))
4582 ToggleBeltSwitch(x, y + 1);
4584 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4585 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4587 ToggleSwitchgateSwitch(x, y + 1);
4589 else if (smashed == EL_LIGHT_SWITCH ||
4590 smashed == EL_LIGHT_SWITCH_ACTIVE)
4592 ToggleLightSwitch(x, y + 1);
4597 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4600 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4603 /* !!! TEST ONLY !!! */
4604 CheckElementChangeBySide(x, y + 1, smashed, element,
4605 CE_SWITCHED, CH_SIDE_TOP);
4606 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4607 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4609 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4610 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4611 CheckElementChangeBySide(x, y + 1, smashed, element,
4612 CE_SWITCHED, CH_SIDE_TOP);
4618 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4623 /* play sound of magic wall / mill */
4625 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4626 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4628 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4629 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4630 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4631 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4636 /* play sound of object that hits the ground */
4637 if (lastline || object_hit)
4638 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4641 inline static void TurnRoundExt(int x, int y)
4653 { 0, 0 }, { 0, 0 }, { 0, 0 },
4658 int left, right, back;
4662 { MV_DOWN, MV_UP, MV_RIGHT },
4663 { MV_UP, MV_DOWN, MV_LEFT },
4665 { MV_LEFT, MV_RIGHT, MV_DOWN },
4669 { MV_RIGHT, MV_LEFT, MV_UP }
4672 int element = Feld[x][y];
4673 int move_pattern = element_info[element].move_pattern;
4675 int old_move_dir = MovDir[x][y];
4676 int left_dir = turn[old_move_dir].left;
4677 int right_dir = turn[old_move_dir].right;
4678 int back_dir = turn[old_move_dir].back;
4680 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4681 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4682 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4683 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4685 int left_x = x + left_dx, left_y = y + left_dy;
4686 int right_x = x + right_dx, right_y = y + right_dy;
4687 int move_x = x + move_dx, move_y = y + move_dy;
4691 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4693 TestIfBadThingTouchesOtherBadThing(x, y);
4695 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4696 MovDir[x][y] = right_dir;
4697 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4698 MovDir[x][y] = left_dir;
4700 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4702 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4706 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4707 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4709 TestIfBadThingTouchesOtherBadThing(x, y);
4711 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4712 MovDir[x][y] = left_dir;
4713 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4714 MovDir[x][y] = right_dir;
4716 if ((element == EL_SPACESHIP ||
4717 element == EL_SP_SNIKSNAK ||
4718 element == EL_SP_ELECTRON)
4719 && MovDir[x][y] != old_move_dir)
4721 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4725 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4727 TestIfBadThingTouchesOtherBadThing(x, y);
4729 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4730 MovDir[x][y] = left_dir;
4731 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4732 MovDir[x][y] = right_dir;
4734 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4736 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4739 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4741 TestIfBadThingTouchesOtherBadThing(x, y);
4743 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4744 MovDir[x][y] = left_dir;
4745 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4746 MovDir[x][y] = right_dir;
4748 if (MovDir[x][y] != old_move_dir)
4752 else if (element == EL_YAMYAM)
4754 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4755 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4757 if (can_turn_left && can_turn_right)
4758 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4759 else if (can_turn_left)
4760 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4761 else if (can_turn_right)
4762 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4764 MovDir[x][y] = back_dir;
4766 MovDelay[x][y] = 16 + 16 * RND(3);
4768 else if (element == EL_DARK_YAMYAM)
4770 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4772 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4775 if (can_turn_left && can_turn_right)
4776 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4777 else if (can_turn_left)
4778 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4779 else if (can_turn_right)
4780 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4782 MovDir[x][y] = back_dir;
4784 MovDelay[x][y] = 16 + 16 * RND(3);
4786 else if (element == EL_PACMAN)
4788 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4789 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4791 if (can_turn_left && can_turn_right)
4792 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4793 else if (can_turn_left)
4794 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4795 else if (can_turn_right)
4796 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4798 MovDir[x][y] = back_dir;
4800 MovDelay[x][y] = 6 + RND(40);
4802 else if (element == EL_PIG)
4804 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4805 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4806 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4807 boolean should_turn_left, should_turn_right, should_move_on;
4809 int rnd = RND(rnd_value);
4811 should_turn_left = (can_turn_left &&
4813 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4814 y + back_dy + left_dy)));
4815 should_turn_right = (can_turn_right &&
4817 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4818 y + back_dy + right_dy)));
4819 should_move_on = (can_move_on &&
4822 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4823 y + move_dy + left_dy) ||
4824 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4825 y + move_dy + right_dy)));
4827 if (should_turn_left || should_turn_right || should_move_on)
4829 if (should_turn_left && should_turn_right && should_move_on)
4830 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4831 rnd < 2 * rnd_value / 3 ? right_dir :
4833 else if (should_turn_left && should_turn_right)
4834 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4835 else if (should_turn_left && should_move_on)
4836 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4837 else if (should_turn_right && should_move_on)
4838 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4839 else if (should_turn_left)
4840 MovDir[x][y] = left_dir;
4841 else if (should_turn_right)
4842 MovDir[x][y] = right_dir;
4843 else if (should_move_on)
4844 MovDir[x][y] = old_move_dir;
4846 else if (can_move_on && rnd > rnd_value / 8)
4847 MovDir[x][y] = old_move_dir;
4848 else if (can_turn_left && can_turn_right)
4849 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4850 else if (can_turn_left && rnd > rnd_value / 8)
4851 MovDir[x][y] = left_dir;
4852 else if (can_turn_right && rnd > rnd_value/8)
4853 MovDir[x][y] = right_dir;
4855 MovDir[x][y] = back_dir;
4857 xx = x + move_xy[MovDir[x][y]].x;
4858 yy = y + move_xy[MovDir[x][y]].y;
4861 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4862 if (!IN_LEV_FIELD(xx, yy) ||
4863 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4864 MovDir[x][y] = old_move_dir;
4866 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4867 MovDir[x][y] = old_move_dir;
4872 else if (element == EL_DRAGON)
4874 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4875 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4876 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4878 int rnd = RND(rnd_value);
4881 if (FrameCounter < 1 && x == 0 && y == 29)
4882 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4885 if (can_move_on && rnd > rnd_value / 8)
4886 MovDir[x][y] = old_move_dir;
4887 else if (can_turn_left && can_turn_right)
4888 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4889 else if (can_turn_left && rnd > rnd_value / 8)
4890 MovDir[x][y] = left_dir;
4891 else if (can_turn_right && rnd > rnd_value / 8)
4892 MovDir[x][y] = right_dir;
4894 MovDir[x][y] = back_dir;
4896 xx = x + move_xy[MovDir[x][y]].x;
4897 yy = y + move_xy[MovDir[x][y]].y;
4900 if (FrameCounter < 1 && x == 0 && y == 29)
4901 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4902 xx, yy, Feld[xx][yy],
4907 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4908 MovDir[x][y] = old_move_dir;
4910 if (!IS_FREE(xx, yy))
4911 MovDir[x][y] = old_move_dir;
4915 if (FrameCounter < 1 && x == 0 && y == 29)
4916 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4921 else if (element == EL_MOLE)
4923 boolean can_move_on =
4924 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4925 IS_AMOEBOID(Feld[move_x][move_y]) ||
4926 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4929 boolean can_turn_left =
4930 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4931 IS_AMOEBOID(Feld[left_x][left_y])));
4933 boolean can_turn_right =
4934 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4935 IS_AMOEBOID(Feld[right_x][right_y])));
4937 if (can_turn_left && can_turn_right)
4938 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4939 else if (can_turn_left)
4940 MovDir[x][y] = left_dir;
4942 MovDir[x][y] = right_dir;
4945 if (MovDir[x][y] != old_move_dir)
4948 else if (element == EL_BALLOON)
4950 MovDir[x][y] = game.balloon_dir;
4953 else if (element == EL_SPRING)
4956 if (MovDir[x][y] & MV_HORIZONTAL &&
4957 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4958 MovDir[x][y] = MV_NO_MOVING;
4960 if (MovDir[x][y] & MV_HORIZONTAL &&
4961 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4962 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4963 MovDir[x][y] = MV_NO_MOVING;
4968 else if (element == EL_ROBOT ||
4969 element == EL_SATELLITE ||
4970 element == EL_PENGUIN)
4972 int attr_x = -1, attr_y = -1;
4983 for (i = 0; i < MAX_PLAYERS; i++)
4985 struct PlayerInfo *player = &stored_player[i];
4986 int jx = player->jx, jy = player->jy;
4988 if (!player->active)
4992 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5001 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5002 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5003 game.engine_version < VERSION_IDENT(3,1,0,0)))
5005 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5012 if (element == EL_PENGUIN)
5015 static int xy[4][2] =
5023 for (i = 0; i < NUM_DIRECTIONS; i++)
5025 int ex = x + xy[i][0];
5026 int ey = y + xy[i][1];
5028 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5037 MovDir[x][y] = MV_NO_MOVING;
5039 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5040 else if (attr_x > x)
5041 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5043 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5044 else if (attr_y > y)
5045 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5047 if (element == EL_ROBOT)
5051 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5052 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5053 Moving2Blocked(x, y, &newx, &newy);
5055 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5056 MovDelay[x][y] = 8 + 8 * !RND(3);
5058 MovDelay[x][y] = 16;
5060 else if (element == EL_PENGUIN)
5066 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5068 boolean first_horiz = RND(2);
5069 int new_move_dir = MovDir[x][y];
5072 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5073 Moving2Blocked(x, y, &newx, &newy);
5075 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
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))
5085 MovDir[x][y] = old_move_dir;
5089 else /* (element == EL_SATELLITE) */
5095 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5097 boolean first_horiz = RND(2);
5098 int new_move_dir = MovDir[x][y];
5101 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5102 Moving2Blocked(x, y, &newx, &newy);
5104 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5108 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5109 Moving2Blocked(x, y, &newx, &newy);
5111 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5114 MovDir[x][y] = old_move_dir;
5119 else if (move_pattern == MV_TURNING_LEFT ||
5120 move_pattern == MV_TURNING_RIGHT ||
5121 move_pattern == MV_TURNING_LEFT_RIGHT ||
5122 move_pattern == MV_TURNING_RIGHT_LEFT ||
5123 move_pattern == MV_TURNING_RANDOM ||
5124 move_pattern == MV_ALL_DIRECTIONS)
5126 boolean can_turn_left =
5127 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5128 boolean can_turn_right =
5129 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5131 #if USE_CAN_MOVE_NOT_MOVING
5132 if (element_info[element].move_stepsize == 0) /* not moving */
5136 if (move_pattern == MV_TURNING_LEFT)
5137 MovDir[x][y] = left_dir;
5138 else if (move_pattern == MV_TURNING_RIGHT)
5139 MovDir[x][y] = right_dir;
5140 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5141 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5142 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5143 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5144 else if (move_pattern == MV_TURNING_RANDOM)
5145 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5146 can_turn_right && !can_turn_left ? right_dir :
5147 RND(2) ? left_dir : right_dir);
5148 else if (can_turn_left && can_turn_right)
5149 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5150 else if (can_turn_left)
5151 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5152 else if (can_turn_right)
5153 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5155 MovDir[x][y] = back_dir;
5157 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5159 else if (move_pattern == MV_HORIZONTAL ||
5160 move_pattern == MV_VERTICAL)
5162 if (move_pattern & old_move_dir)
5163 MovDir[x][y] = back_dir;
5164 else if (move_pattern == MV_HORIZONTAL)
5165 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5166 else if (move_pattern == MV_VERTICAL)
5167 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5169 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5171 else if (move_pattern & MV_ANY_DIRECTION)
5173 MovDir[x][y] = move_pattern;
5174 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5176 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5178 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5179 MovDir[x][y] = left_dir;
5180 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5181 MovDir[x][y] = right_dir;
5183 if (MovDir[x][y] != old_move_dir)
5184 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5186 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5188 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5189 MovDir[x][y] = right_dir;
5190 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5191 MovDir[x][y] = left_dir;
5193 if (MovDir[x][y] != old_move_dir)
5194 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5196 else if (move_pattern == MV_TOWARDS_PLAYER ||
5197 move_pattern == MV_AWAY_FROM_PLAYER)
5199 int attr_x = -1, attr_y = -1;
5201 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5212 for (i = 0; i < MAX_PLAYERS; i++)
5214 struct PlayerInfo *player = &stored_player[i];
5215 int jx = player->jx, jy = player->jy;
5217 if (!player->active)
5221 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5229 MovDir[x][y] = MV_NO_MOVING;
5231 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5232 else if (attr_x > x)
5233 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5235 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5236 else if (attr_y > y)
5237 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5239 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5241 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5243 boolean first_horiz = RND(2);
5244 int new_move_dir = MovDir[x][y];
5246 #if USE_CAN_MOVE_NOT_MOVING
5247 if (element_info[element].move_stepsize == 0) /* not moving */
5249 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5250 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5257 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5258 Moving2Blocked(x, y, &newx, &newy);
5260 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
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))
5270 MovDir[x][y] = old_move_dir;
5273 else if (move_pattern == MV_WHEN_PUSHED ||
5274 move_pattern == MV_WHEN_DROPPED)
5276 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5277 MovDir[x][y] = MV_NO_MOVING;
5281 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5283 static int test_xy[7][2] =
5293 static int test_dir[7] =
5303 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5304 int move_preference = -1000000; /* start with very low preference */
5305 int new_move_dir = MV_NO_MOVING;
5306 int start_test = RND(4);
5309 for (i = 0; i < NUM_DIRECTIONS; i++)
5311 int move_dir = test_dir[start_test + i];
5312 int move_dir_preference;
5314 xx = x + test_xy[start_test + i][0];
5315 yy = y + test_xy[start_test + i][1];
5317 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5318 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5320 new_move_dir = move_dir;
5325 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5328 move_dir_preference = -1 * RunnerVisit[xx][yy];
5329 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5330 move_dir_preference = PlayerVisit[xx][yy];
5332 if (move_dir_preference > move_preference)
5334 /* prefer field that has not been visited for the longest time */
5335 move_preference = move_dir_preference;
5336 new_move_dir = move_dir;
5338 else if (move_dir_preference == move_preference &&
5339 move_dir == old_move_dir)
5341 /* prefer last direction when all directions are preferred equally */
5342 move_preference = move_dir_preference;
5343 new_move_dir = move_dir;
5347 MovDir[x][y] = new_move_dir;
5348 if (old_move_dir != new_move_dir)
5351 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5359 static void TurnRound(int x, int y)
5361 int direction = MovDir[x][y];
5364 GfxDir[x][y] = MovDir[x][y];
5370 GfxDir[x][y] = MovDir[x][y];
5373 if (direction != MovDir[x][y])
5378 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5381 GfxAction[x][y] = ACTION_WAITING;
5385 static boolean JustBeingPushed(int x, int y)
5389 for (i = 0; i < MAX_PLAYERS; i++)
5391 struct PlayerInfo *player = &stored_player[i];
5393 if (player->active && player->is_pushing && player->MovPos)
5395 int next_jx = player->jx + (player->jx - player->last_jx);
5396 int next_jy = player->jy + (player->jy - player->last_jy);
5398 if (x == next_jx && y == next_jy)
5406 void StartMoving(int x, int y)
5409 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5411 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5412 int element = Feld[x][y];
5418 if (MovDelay[x][y] == 0)
5419 GfxAction[x][y] = ACTION_DEFAULT;
5421 /* !!! this should be handled more generic (not only for mole) !!! */
5422 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5423 GfxAction[x][y] = ACTION_DEFAULT;
5426 if (CAN_FALL(element) && y < lev_fieldy - 1)
5428 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5429 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5430 if (JustBeingPushed(x, y))
5433 if (element == EL_QUICKSAND_FULL)
5435 if (IS_FREE(x, y + 1))
5437 InitMovingField(x, y, MV_DOWN);
5438 started_moving = TRUE;
5440 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5441 Store[x][y] = EL_ROCK;
5443 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5445 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5448 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5450 if (!MovDelay[x][y])
5451 MovDelay[x][y] = TILEY + 1;
5460 Feld[x][y] = EL_QUICKSAND_EMPTY;
5461 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5462 Store[x][y + 1] = Store[x][y];
5465 PlayLevelSoundAction(x, y, ACTION_FILLING);
5467 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5471 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5472 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5474 InitMovingField(x, y, MV_DOWN);
5475 started_moving = TRUE;
5477 Feld[x][y] = EL_QUICKSAND_FILLING;
5478 Store[x][y] = element;
5480 PlayLevelSoundAction(x, y, ACTION_FILLING);
5482 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5485 else if (element == EL_MAGIC_WALL_FULL)
5487 if (IS_FREE(x, y + 1))
5489 InitMovingField(x, y, MV_DOWN);
5490 started_moving = TRUE;
5492 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5493 Store[x][y] = EL_CHANGED(Store[x][y]);
5495 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5497 if (!MovDelay[x][y])
5498 MovDelay[x][y] = TILEY/4 + 1;
5507 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5508 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5509 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5513 else if (element == EL_BD_MAGIC_WALL_FULL)
5515 if (IS_FREE(x, y + 1))
5517 InitMovingField(x, y, MV_DOWN);
5518 started_moving = TRUE;
5520 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5521 Store[x][y] = EL_CHANGED2(Store[x][y]);
5523 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5525 if (!MovDelay[x][y])
5526 MovDelay[x][y] = TILEY/4 + 1;
5535 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5536 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5537 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5541 else if (CAN_PASS_MAGIC_WALL(element) &&
5542 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5543 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5545 InitMovingField(x, y, MV_DOWN);
5546 started_moving = TRUE;
5549 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5550 EL_BD_MAGIC_WALL_FILLING);
5551 Store[x][y] = element;
5554 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5556 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5559 SplashAcid(x, y + 1);
5561 InitMovingField(x, y, MV_DOWN);
5562 started_moving = TRUE;
5564 Store[x][y] = EL_ACID;
5566 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5567 GfxAction[x][y + 1] = ACTION_ACTIVE;
5571 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5572 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5574 #if USE_IMPACT_BUGFIX
5575 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5576 CAN_FALL(element) && WasJustFalling[x][y] &&
5577 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5579 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5580 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5581 (Feld[x][y + 1] == EL_BLOCKED)))
5583 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5584 CAN_SMASH(element) && WasJustFalling[x][y] &&
5585 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5587 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5588 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5589 (Feld[x][y + 1] == EL_BLOCKED)))
5594 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5595 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5596 WasJustMoving[x][y] && !Pushed[x][y + 1])
5598 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5599 WasJustMoving[x][y])
5604 /* this is needed for a special case not covered by calling "Impact()"
5605 from "ContinueMoving()": if an element moves to a tile directly below
5606 another element which was just falling on that tile (which was empty
5607 in the previous frame), the falling element above would just stop
5608 instead of smashing the element below (in previous version, the above
5609 element was just checked for "moving" instead of "falling", resulting
5610 in incorrect smashes caused by horizontal movement of the above
5611 element; also, the case of the player being the element to smash was
5612 simply not covered here... :-/ ) */
5615 WasJustMoving[x][y] = 0;
5616 WasJustFalling[x][y] = 0;
5619 CheckCollision[x][y] = 0;
5622 if (IS_PLAYER(x, y + 1))
5623 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5628 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5630 if (MovDir[x][y] == MV_NO_MOVING)
5632 InitMovingField(x, y, MV_DOWN);
5633 started_moving = TRUE;
5636 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5638 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5639 MovDir[x][y] = MV_DOWN;
5641 InitMovingField(x, y, MV_DOWN);
5642 started_moving = TRUE;
5644 else if (element == EL_AMOEBA_DROP)
5646 Feld[x][y] = EL_AMOEBA_GROWING;
5647 Store[x][y] = EL_AMOEBA_WET;
5649 /* Store[x][y + 1] must be zero, because:
5650 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5653 #if OLD_GAME_BEHAVIOUR
5654 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5656 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5657 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5658 element != EL_DX_SUPABOMB)
5661 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5662 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5663 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5664 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5667 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5668 (IS_FREE(x - 1, y + 1) ||
5669 Feld[x - 1][y + 1] == EL_ACID));
5670 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5671 (IS_FREE(x + 1, y + 1) ||
5672 Feld[x + 1][y + 1] == EL_ACID));
5673 boolean can_fall_any = (can_fall_left || can_fall_right);
5674 boolean can_fall_both = (can_fall_left && can_fall_right);
5676 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5678 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5680 if (slippery_type == SLIPPERY_ONLY_LEFT)
5681 can_fall_right = FALSE;
5682 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5683 can_fall_left = FALSE;
5684 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5685 can_fall_right = FALSE;
5686 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5687 can_fall_left = FALSE;
5689 can_fall_any = (can_fall_left || can_fall_right);
5690 can_fall_both = (can_fall_left && can_fall_right);
5693 #if USE_NEW_SP_SLIPPERY
5694 /* !!! better use the same properties as for custom elements here !!! */
5695 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5696 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5698 can_fall_right = FALSE; /* slip down on left side */
5699 can_fall_both = FALSE;
5706 if (game.emulation == EMU_BOULDERDASH ||
5707 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5708 can_fall_right = FALSE; /* slip down on left side */
5710 can_fall_left = !(can_fall_right = RND(2));
5712 can_fall_both = FALSE;
5719 if (can_fall_both &&
5720 (game.emulation != EMU_BOULDERDASH &&
5721 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5722 can_fall_left = !(can_fall_right = RND(2));
5725 /* if not determined otherwise, prefer left side for slipping down */
5726 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5727 started_moving = TRUE;
5731 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5733 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5736 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5737 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5738 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5739 int belt_dir = game.belt_dir[belt_nr];
5741 if ((belt_dir == MV_LEFT && left_is_free) ||
5742 (belt_dir == MV_RIGHT && right_is_free))
5745 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5748 InitMovingField(x, y, belt_dir);
5749 started_moving = TRUE;
5752 Pushed[x][y] = TRUE;
5753 Pushed[nextx][y] = TRUE;
5756 GfxAction[x][y] = ACTION_DEFAULT;
5760 MovDir[x][y] = 0; /* if element was moving, stop it */
5765 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5767 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5769 if (CAN_MOVE(element) && !started_moving)
5772 int move_pattern = element_info[element].move_pattern;
5777 if (MovDir[x][y] == MV_NO_MOVING)
5779 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5780 x, y, element, element_info[element].token_name);
5781 printf("StartMoving(): This should never happen!\n");
5786 Moving2Blocked(x, y, &newx, &newy);
5789 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5792 if ((element == EL_SATELLITE ||
5793 element == EL_BALLOON ||
5794 element == EL_SPRING)
5795 && JustBeingPushed(x, y))
5802 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5803 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5805 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5806 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5807 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5811 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5812 element, element_info[element].token_name,
5813 WasJustMoving[x][y],
5814 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5815 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5816 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5817 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5821 WasJustMoving[x][y] = 0;
5824 CheckCollision[x][y] = 0;
5826 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5829 if (Feld[x][y] != element) /* element has changed */
5831 element = Feld[x][y];
5832 move_pattern = element_info[element].move_pattern;
5834 if (!CAN_MOVE(element))
5838 if (Feld[x][y] != element) /* element has changed */
5846 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5847 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5849 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5851 Moving2Blocked(x, y, &newx, &newy);
5852 if (Feld[newx][newy] == EL_BLOCKED)
5853 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5859 if (FrameCounter < 1 && x == 0 && y == 29)
5860 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5863 if (!MovDelay[x][y]) /* start new movement phase */
5865 /* all objects that can change their move direction after each step
5866 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5868 if (element != EL_YAMYAM &&
5869 element != EL_DARK_YAMYAM &&
5870 element != EL_PACMAN &&
5871 !(move_pattern & MV_ANY_DIRECTION) &&
5872 move_pattern != MV_TURNING_LEFT &&
5873 move_pattern != MV_TURNING_RIGHT &&
5874 move_pattern != MV_TURNING_LEFT_RIGHT &&
5875 move_pattern != MV_TURNING_RIGHT_LEFT &&
5876 move_pattern != MV_TURNING_RANDOM)
5881 if (FrameCounter < 1 && x == 0 && y == 29)
5882 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5885 if (MovDelay[x][y] && (element == EL_BUG ||
5886 element == EL_SPACESHIP ||
5887 element == EL_SP_SNIKSNAK ||
5888 element == EL_SP_ELECTRON ||
5889 element == EL_MOLE))
5890 DrawLevelField(x, y);
5894 if (MovDelay[x][y]) /* wait some time before next movement */
5899 if (element == EL_YAMYAM)
5902 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5903 DrawLevelElementAnimation(x, y, element);
5907 if (MovDelay[x][y]) /* element still has to wait some time */
5910 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5911 ResetGfxAnimation(x, y);
5915 if (GfxAction[x][y] != ACTION_WAITING)
5916 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5918 GfxAction[x][y] = ACTION_WAITING;
5922 if (element == EL_ROBOT ||
5924 element == EL_PACMAN ||
5926 element == EL_YAMYAM ||
5927 element == EL_DARK_YAMYAM)
5930 DrawLevelElementAnimation(x, y, element);
5932 DrawLevelElementAnimationIfNeeded(x, y, element);
5934 PlayLevelSoundAction(x, y, ACTION_WAITING);
5936 else if (element == EL_SP_ELECTRON)
5937 DrawLevelElementAnimationIfNeeded(x, y, element);
5938 else if (element == EL_DRAGON)
5941 int dir = MovDir[x][y];
5942 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5943 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5944 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5945 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5946 dir == MV_UP ? IMG_FLAMES_1_UP :
5947 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5948 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5951 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5954 GfxAction[x][y] = ACTION_ATTACKING;
5956 if (IS_PLAYER(x, y))
5957 DrawPlayerField(x, y);
5959 DrawLevelField(x, y);
5961 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5963 for (i = 1; i <= 3; i++)
5965 int xx = x + i * dx;
5966 int yy = y + i * dy;
5967 int sx = SCREENX(xx);
5968 int sy = SCREENY(yy);
5969 int flame_graphic = graphic + (i - 1);
5971 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5976 int flamed = MovingOrBlocked2Element(xx, yy);
5980 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5982 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5983 RemoveMovingField(xx, yy);
5985 RemoveField(xx, yy);
5987 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5990 RemoveMovingField(xx, yy);
5994 if (ChangeDelay[xx][yy])
5995 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5996 Feld[xx][yy] == EL_BLOCKED));
6000 ChangeDelay[xx][yy] = 0;
6002 Feld[xx][yy] = EL_FLAMES;
6003 if (IN_SCR_FIELD(sx, sy))
6005 DrawLevelFieldCrumbledSand(xx, yy);
6006 DrawGraphic(sx, sy, flame_graphic, frame);
6011 if (Feld[xx][yy] == EL_FLAMES)
6012 Feld[xx][yy] = EL_EMPTY;
6013 DrawLevelField(xx, yy);
6018 if (MovDelay[x][y]) /* element still has to wait some time */
6020 PlayLevelSoundAction(x, y, ACTION_WAITING);
6026 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6027 for all other elements GfxAction will be set by InitMovingField() */
6028 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6029 GfxAction[x][y] = ACTION_MOVING;
6033 /* now make next step */
6035 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6037 if (DONT_COLLIDE_WITH(element) &&
6038 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6039 !PLAYER_ENEMY_PROTECTED(newx, newy))
6042 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6046 /* player killed by element which is deadly when colliding with */
6048 KillHero(PLAYERINFO(newx, newy));
6055 else if (CAN_MOVE_INTO_ACID(element) &&
6056 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6057 (MovDir[x][y] == MV_DOWN ||
6058 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6060 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6061 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6065 else if ((element == EL_PENGUIN ||
6066 element == EL_ROBOT ||
6067 element == EL_SATELLITE ||
6068 element == EL_BALLOON ||
6069 IS_CUSTOM_ELEMENT(element)) &&
6070 IN_LEV_FIELD(newx, newy) &&
6071 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6074 SplashAcid(newx, newy);
6075 Store[x][y] = EL_ACID;
6077 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6079 if (Feld[newx][newy] == EL_EXIT_OPEN)
6083 DrawLevelField(x, y);
6085 Feld[x][y] = EL_EMPTY;
6086 DrawLevelField(x, y);
6089 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6090 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6091 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6093 local_player->friends_still_needed--;
6094 if (!local_player->friends_still_needed &&
6095 !local_player->GameOver && AllPlayersGone)
6096 local_player->LevelSolved = local_player->GameOver = TRUE;
6100 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6102 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6103 DrawLevelField(newx, newy);
6105 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6107 else if (!IS_FREE(newx, newy))
6109 GfxAction[x][y] = ACTION_WAITING;
6111 if (IS_PLAYER(x, y))
6112 DrawPlayerField(x, y);
6114 DrawLevelField(x, y);
6119 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6121 if (IS_FOOD_PIG(Feld[newx][newy]))
6123 if (IS_MOVING(newx, newy))
6124 RemoveMovingField(newx, newy);
6127 Feld[newx][newy] = EL_EMPTY;
6128 DrawLevelField(newx, newy);
6131 PlayLevelSound(x, y, SND_PIG_DIGGING);
6133 else if (!IS_FREE(newx, newy))
6135 if (IS_PLAYER(x, y))
6136 DrawPlayerField(x, y);
6138 DrawLevelField(x, y);
6147 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6150 else if (IS_CUSTOM_ELEMENT(element) &&
6151 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6155 !IS_FREE(newx, newy)
6160 int new_element = Feld[newx][newy];
6163 printf("::: '%s' digs '%s' [%d]\n",
6164 element_info[element].token_name,
6165 element_info[Feld[newx][newy]].token_name,
6166 StorePlayer[newx][newy]);
6169 if (!IS_FREE(newx, newy))
6171 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6172 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6175 /* no element can dig solid indestructible elements */
6176 if (IS_INDESTRUCTIBLE(new_element) &&
6177 !IS_DIGGABLE(new_element) &&
6178 !IS_COLLECTIBLE(new_element))
6181 if (AmoebaNr[newx][newy] &&
6182 (new_element == EL_AMOEBA_FULL ||
6183 new_element == EL_BD_AMOEBA ||
6184 new_element == EL_AMOEBA_GROWING))
6186 AmoebaCnt[AmoebaNr[newx][newy]]--;
6187 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6190 if (IS_MOVING(newx, newy))
6191 RemoveMovingField(newx, newy);
6194 RemoveField(newx, newy);
6195 DrawLevelField(newx, newy);
6198 /* if digged element was about to explode, prevent the explosion */
6199 ExplodeField[newx][newy] = EX_TYPE_NONE;
6201 PlayLevelSoundAction(x, y, action);
6206 Store[newx][newy] = EL_EMPTY;
6207 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6208 Store[newx][newy] = element_info[element].move_leave_element;
6210 Store[newx][newy] = EL_EMPTY;
6211 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6212 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6213 Store[newx][newy] = element_info[element].move_leave_element;
6216 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6217 element_info[element].can_leave_element = TRUE;
6220 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6222 RunnerVisit[x][y] = FrameCounter;
6223 PlayerVisit[x][y] /= 8; /* expire player visit path */
6229 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6231 if (!IS_FREE(newx, newy))
6233 if (IS_PLAYER(x, y))
6234 DrawPlayerField(x, y);
6236 DrawLevelField(x, y);
6242 boolean wanna_flame = !RND(10);
6243 int dx = newx - x, dy = newy - y;
6244 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6245 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6246 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6247 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6248 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6249 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6252 IS_CLASSIC_ENEMY(element1) ||
6253 IS_CLASSIC_ENEMY(element2)) &&
6254 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6255 element1 != EL_FLAMES && element2 != EL_FLAMES)
6258 ResetGfxAnimation(x, y);
6259 GfxAction[x][y] = ACTION_ATTACKING;
6262 if (IS_PLAYER(x, y))
6263 DrawPlayerField(x, y);
6265 DrawLevelField(x, y);
6267 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6269 MovDelay[x][y] = 50;
6273 RemoveField(newx, newy);
6275 Feld[newx][newy] = EL_FLAMES;
6276 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6279 RemoveField(newx1, newy1);
6281 Feld[newx1][newy1] = EL_FLAMES;
6283 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6286 RemoveField(newx2, newy2);
6288 Feld[newx2][newy2] = EL_FLAMES;
6295 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6296 Feld[newx][newy] == EL_DIAMOND)
6298 if (IS_MOVING(newx, newy))
6299 RemoveMovingField(newx, newy);
6302 Feld[newx][newy] = EL_EMPTY;
6303 DrawLevelField(newx, newy);
6306 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6308 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6309 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6311 if (AmoebaNr[newx][newy])
6313 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6314 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6315 Feld[newx][newy] == EL_BD_AMOEBA)
6316 AmoebaCnt[AmoebaNr[newx][newy]]--;
6321 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6323 if (IS_MOVING(newx, newy))
6326 RemoveMovingField(newx, newy);
6330 Feld[newx][newy] = EL_EMPTY;
6331 DrawLevelField(newx, newy);
6334 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6336 else if ((element == EL_PACMAN || element == EL_MOLE)
6337 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6339 if (AmoebaNr[newx][newy])
6341 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6342 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6343 Feld[newx][newy] == EL_BD_AMOEBA)
6344 AmoebaCnt[AmoebaNr[newx][newy]]--;
6347 if (element == EL_MOLE)
6349 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6350 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6352 ResetGfxAnimation(x, y);
6353 GfxAction[x][y] = ACTION_DIGGING;
6354 DrawLevelField(x, y);
6356 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6358 return; /* wait for shrinking amoeba */
6360 else /* element == EL_PACMAN */
6362 Feld[newx][newy] = EL_EMPTY;
6363 DrawLevelField(newx, newy);
6364 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6367 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6368 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6369 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6371 /* wait for shrinking amoeba to completely disappear */
6374 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6376 /* object was running against a wall */
6381 if (move_pattern & MV_ANY_DIRECTION &&
6382 move_pattern == MovDir[x][y])
6384 int blocking_element =
6385 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6388 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6389 element_info[element].token_name,
6390 element_info[blocking_element].token_name,
6394 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6397 element = Feld[x][y]; /* element might have changed */
6402 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6403 DrawLevelElementAnimation(x, y, element);
6405 if (element == EL_BUG ||
6406 element == EL_SPACESHIP ||
6407 element == EL_SP_SNIKSNAK)
6408 DrawLevelField(x, y);
6409 else if (element == EL_MOLE)
6410 DrawLevelField(x, y);
6411 else if (element == EL_BD_BUTTERFLY ||
6412 element == EL_BD_FIREFLY)
6413 DrawLevelElementAnimationIfNeeded(x, y, element);
6414 else if (element == EL_SATELLITE)
6415 DrawLevelElementAnimationIfNeeded(x, y, element);
6416 else if (element == EL_SP_ELECTRON)
6417 DrawLevelElementAnimationIfNeeded(x, y, element);
6420 if (DONT_TOUCH(element))
6421 TestIfBadThingTouchesHero(x, y);
6424 PlayLevelSoundAction(x, y, ACTION_WAITING);
6430 InitMovingField(x, y, MovDir[x][y]);
6432 PlayLevelSoundAction(x, y, ACTION_MOVING);
6436 ContinueMoving(x, y);
6439 void ContinueMoving(int x, int y)
6441 int element = Feld[x][y];
6442 int stored = Store[x][y];
6443 struct ElementInfo *ei = &element_info[element];
6444 int direction = MovDir[x][y];
6445 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6446 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6447 int newx = x + dx, newy = y + dy;
6449 int nextx = newx + dx, nexty = newy + dy;
6452 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6453 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6455 boolean pushed_by_player = Pushed[x][y];
6458 MovPos[x][y] += getElementMoveStepsize(x, y);
6461 if (pushed_by_player && IS_PLAYER(x, y))
6463 /* special case: moving object pushed by player */
6464 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6467 if (pushed_by_player) /* special case: moving object pushed by player */
6468 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6471 if (ABS(MovPos[x][y]) < TILEX)
6473 DrawLevelField(x, y);
6475 return; /* element is still moving */
6478 /* element reached destination field */
6480 Feld[x][y] = EL_EMPTY;
6481 Feld[newx][newy] = element;
6482 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6485 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6487 element = Feld[newx][newy] = EL_ACID;
6490 else if (element == EL_MOLE)
6492 Feld[x][y] = EL_SAND;
6494 DrawLevelFieldCrumbledSandNeighbours(x, y);
6496 else if (element == EL_QUICKSAND_FILLING)
6498 element = Feld[newx][newy] = get_next_element(element);
6499 Store[newx][newy] = Store[x][y];
6501 else if (element == EL_QUICKSAND_EMPTYING)
6503 Feld[x][y] = get_next_element(element);
6504 element = Feld[newx][newy] = Store[x][y];
6506 else if (element == EL_MAGIC_WALL_FILLING)
6508 element = Feld[newx][newy] = get_next_element(element);
6509 if (!game.magic_wall_active)
6510 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6511 Store[newx][newy] = Store[x][y];
6513 else if (element == EL_MAGIC_WALL_EMPTYING)
6515 Feld[x][y] = get_next_element(element);
6516 if (!game.magic_wall_active)
6517 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6518 element = Feld[newx][newy] = Store[x][y];
6520 else if (element == EL_BD_MAGIC_WALL_FILLING)
6522 element = Feld[newx][newy] = get_next_element(element);
6523 if (!game.magic_wall_active)
6524 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6525 Store[newx][newy] = Store[x][y];
6527 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6529 Feld[x][y] = get_next_element(element);
6530 if (!game.magic_wall_active)
6531 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6532 element = Feld[newx][newy] = Store[x][y];
6534 else if (element == EL_AMOEBA_DROPPING)
6536 Feld[x][y] = get_next_element(element);
6537 element = Feld[newx][newy] = Store[x][y];
6539 else if (element == EL_SOKOBAN_OBJECT)
6542 Feld[x][y] = Back[x][y];
6544 if (Back[newx][newy])
6545 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6547 Back[x][y] = Back[newx][newy] = 0;
6550 else if (Store[x][y] == EL_ACID)
6552 element = Feld[newx][newy] = EL_ACID;
6556 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6557 ei->move_leave_element != EL_EMPTY &&
6558 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6559 Store[x][y] != EL_EMPTY))
6561 /* some elements can leave other elements behind after moving */
6563 Feld[x][y] = ei->move_leave_element;
6564 InitField(x, y, FALSE);
6566 if (GFX_CRUMBLED(Feld[x][y]))
6567 DrawLevelFieldCrumbledSandNeighbours(x, y);
6571 Store[x][y] = EL_EMPTY;
6575 MovDelay[newx][newy] = 0;
6577 if (CAN_CHANGE(element))
6579 /* copy element change control values to new field */
6580 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6581 ChangePage[newx][newy] = ChangePage[x][y];
6582 Changed[newx][newy] = Changed[x][y];
6583 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6586 ChangeDelay[x][y] = 0;
6587 ChangePage[x][y] = -1;
6588 Changed[x][y] = FALSE;
6589 ChangeEvent[x][y] = -1;
6591 /* copy animation control values to new field */
6592 GfxFrame[newx][newy] = GfxFrame[x][y];
6593 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6594 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6595 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6597 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6600 /* do this after checking for left-behind element */
6601 ResetGfxAnimation(x, y); /* reset animation values for old field */
6605 /* some elements can leave other elements behind after moving */
6607 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6608 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6609 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6611 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6612 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6616 int move_leave_element = ei->move_leave_element;
6618 Feld[x][y] = move_leave_element;
6620 #if USE_PREVIOUS_MOVE_DIR
6621 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6622 MovDir[x][y] = direction;
6625 InitField(x, y, FALSE);
6627 if (GFX_CRUMBLED(Feld[x][y]))
6628 DrawLevelFieldCrumbledSandNeighbours(x, y);
6630 if (ELEM_IS_PLAYER(move_leave_element))
6631 RelocatePlayer(x, y, move_leave_element);
6636 /* some elements can leave other elements behind after moving */
6637 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6638 ei->move_leave_element != EL_EMPTY &&
6639 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6640 ei->can_leave_element_last))
6642 Feld[x][y] = ei->move_leave_element;
6643 InitField(x, y, FALSE);
6645 if (GFX_CRUMBLED(Feld[x][y]))
6646 DrawLevelFieldCrumbledSandNeighbours(x, y);
6649 ei->can_leave_element_last = ei->can_leave_element;
6650 ei->can_leave_element = FALSE;
6654 /* do this after checking for left-behind element */
6655 ResetGfxAnimation(x, y); /* reset animation values for old field */
6659 /* 2.1.1 (does not work correctly for spring) */
6660 if (!CAN_MOVE(element))
6661 MovDir[newx][newy] = 0;
6665 /* (does not work for falling objects that slide horizontally) */
6666 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6667 MovDir[newx][newy] = 0;
6670 if (!CAN_MOVE(element) ||
6671 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6672 MovDir[newx][newy] = 0;
6676 if (!CAN_MOVE(element) ||
6677 (CAN_FALL(element) && direction == MV_DOWN))
6678 GfxDir[x][y] = MovDir[newx][newy] = 0;
6680 if (!CAN_MOVE(element) ||
6681 (CAN_FALL(element) && direction == MV_DOWN &&
6682 (element == EL_SPRING ||
6683 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6684 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6685 GfxDir[x][y] = MovDir[newx][newy] = 0;
6691 DrawLevelField(x, y);
6692 DrawLevelField(newx, newy);
6694 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6696 /* prevent pushed element from moving on in pushed direction */
6697 if (pushed_by_player && CAN_MOVE(element) &&
6698 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6699 !(element_info[element].move_pattern & direction))
6700 TurnRound(newx, newy);
6703 /* prevent elements on conveyor belt from moving on in last direction */
6704 if (pushed_by_conveyor && CAN_FALL(element) &&
6705 direction & MV_HORIZONTAL)
6708 if (CAN_MOVE(element))
6709 InitMovDir(newx, newy);
6711 MovDir[newx][newy] = 0;
6713 MovDir[newx][newy] = 0;
6718 if (!pushed_by_player)
6720 int nextx = newx + dx, nexty = newy + dy;
6721 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6723 WasJustMoving[newx][newy] = 3;
6725 if (CAN_FALL(element) && direction == MV_DOWN)
6726 WasJustFalling[newx][newy] = 3;
6728 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6729 CheckCollision[newx][newy] = 2;
6732 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6734 TestIfBadThingTouchesHero(newx, newy);
6735 TestIfBadThingTouchesFriend(newx, newy);
6737 if (!IS_CUSTOM_ELEMENT(element))
6738 TestIfBadThingTouchesOtherBadThing(newx, newy);
6740 else if (element == EL_PENGUIN)
6741 TestIfFriendTouchesBadThing(newx, newy);
6743 #if USE_NEW_MOVE_STYLE
6745 if (CAN_FALL(element) && direction == MV_DOWN &&
6746 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6747 IS_PLAYER(x, newy + 1))
6748 printf("::: we would now kill the player [%d]\n", FrameCounter);
6751 /* give the player one last chance (one more frame) to move away */
6752 if (CAN_FALL(element) && direction == MV_DOWN &&
6753 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6754 ((newy < lev_fieldy - 1 && !IS_PLAYER(x, newy + 1)) ||
6755 game.engine_version < VERSION_IDENT(3,1,1,0)))
6758 if (CAN_FALL(element) && direction == MV_DOWN &&
6759 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6767 if (pushed_by_player && !game.use_change_when_pushing_bug)
6769 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6772 if (pushed_by_player)
6777 int dig_side = MV_DIR_OPPOSITE(direction);
6779 static int trigger_sides[4] =
6781 CH_SIDE_RIGHT, /* moving left */
6782 CH_SIDE_LEFT, /* moving right */
6783 CH_SIDE_BOTTOM, /* moving up */
6784 CH_SIDE_TOP, /* moving down */
6786 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6788 struct PlayerInfo *player = PLAYERINFO(x, y);
6790 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6791 player->index_bit, dig_side);
6792 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6793 player->index_bit, dig_side);
6798 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6802 if (ChangePage[newx][newy] != -1) /* delayed change */
6803 ChangeElement(newx, newy, ChangePage[newx][newy]);
6808 TestIfElementHitsCustomElement(newx, newy, direction);
6812 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6814 int hitting_element = Feld[newx][newy];
6816 /* !!! fix side (direction) orientation here and elsewhere !!! */
6817 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6821 if (IN_LEV_FIELD(nextx, nexty))
6823 int opposite_direction = MV_DIR_OPPOSITE(direction);
6824 int hitting_side = direction;
6825 int touched_side = opposite_direction;
6826 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6827 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6828 MovDir[nextx][nexty] != direction ||
6829 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6835 CheckElementChangeBySide(nextx, nexty, touched_element,
6836 CE_HIT_BY_SOMETHING, opposite_direction);
6838 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6839 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6841 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6843 struct ElementChangeInfo *change =
6844 &element_info[hitting_element].change_page[i];
6846 if (change->can_change &&
6847 change->has_event[CE_OTHER_IS_HITTING] &&
6848 change->trigger_side & touched_side &&
6849 change->trigger_element == touched_element)
6851 CheckElementChangeByPage(newx, newy, hitting_element,
6852 touched_element, CE_OTHER_IS_HITTING,i);
6858 if (IS_CUSTOM_ELEMENT(touched_element) &&
6859 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6861 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6863 struct ElementChangeInfo *change =
6864 &element_info[touched_element].change_page[i];
6866 if (change->can_change &&
6867 change->has_event[CE_OTHER_GETS_HIT] &&
6868 change->trigger_side & hitting_side &&
6869 change->trigger_element == hitting_element)
6871 CheckElementChangeByPage(nextx, nexty, touched_element,
6872 hitting_element, CE_OTHER_GETS_HIT, i);
6883 TestIfPlayerTouchesCustomElement(newx, newy);
6884 TestIfElementTouchesCustomElement(newx, newy);
6887 int AmoebeNachbarNr(int ax, int ay)
6890 int element = Feld[ax][ay];
6892 static int xy[4][2] =
6900 for (i = 0; i < NUM_DIRECTIONS; i++)
6902 int x = ax + xy[i][0];
6903 int y = ay + xy[i][1];
6905 if (!IN_LEV_FIELD(x, y))
6908 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6909 group_nr = AmoebaNr[x][y];
6915 void AmoebenVereinigen(int ax, int ay)
6917 int i, x, y, xx, yy;
6918 int new_group_nr = AmoebaNr[ax][ay];
6919 static int xy[4][2] =
6927 if (new_group_nr == 0)
6930 for (i = 0; i < NUM_DIRECTIONS; i++)
6935 if (!IN_LEV_FIELD(x, y))
6938 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6939 Feld[x][y] == EL_BD_AMOEBA ||
6940 Feld[x][y] == EL_AMOEBA_DEAD) &&
6941 AmoebaNr[x][y] != new_group_nr)
6943 int old_group_nr = AmoebaNr[x][y];
6945 if (old_group_nr == 0)
6948 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6949 AmoebaCnt[old_group_nr] = 0;
6950 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6951 AmoebaCnt2[old_group_nr] = 0;
6953 for (yy = 0; yy < lev_fieldy; yy++)
6955 for (xx = 0; xx < lev_fieldx; xx++)
6957 if (AmoebaNr[xx][yy] == old_group_nr)
6958 AmoebaNr[xx][yy] = new_group_nr;
6965 void AmoebeUmwandeln(int ax, int ay)
6969 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6971 int group_nr = AmoebaNr[ax][ay];
6976 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6977 printf("AmoebeUmwandeln(): This should never happen!\n");
6982 for (y = 0; y < lev_fieldy; y++)
6984 for (x = 0; x < lev_fieldx; x++)
6986 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6989 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6993 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6994 SND_AMOEBA_TURNING_TO_GEM :
6995 SND_AMOEBA_TURNING_TO_ROCK));
7000 static int xy[4][2] =
7008 for (i = 0; i < NUM_DIRECTIONS; i++)
7013 if (!IN_LEV_FIELD(x, y))
7016 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7018 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7019 SND_AMOEBA_TURNING_TO_GEM :
7020 SND_AMOEBA_TURNING_TO_ROCK));
7027 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7030 int group_nr = AmoebaNr[ax][ay];
7031 boolean done = FALSE;
7036 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7037 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7042 for (y = 0; y < lev_fieldy; y++)
7044 for (x = 0; x < lev_fieldx; x++)
7046 if (AmoebaNr[x][y] == group_nr &&
7047 (Feld[x][y] == EL_AMOEBA_DEAD ||
7048 Feld[x][y] == EL_BD_AMOEBA ||
7049 Feld[x][y] == EL_AMOEBA_GROWING))
7052 Feld[x][y] = new_element;
7053 InitField(x, y, FALSE);
7054 DrawLevelField(x, y);
7061 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7062 SND_BD_AMOEBA_TURNING_TO_ROCK :
7063 SND_BD_AMOEBA_TURNING_TO_GEM));
7066 void AmoebeWaechst(int x, int y)
7068 static unsigned long sound_delay = 0;
7069 static unsigned long sound_delay_value = 0;
7071 if (!MovDelay[x][y]) /* start new growing cycle */
7075 if (DelayReached(&sound_delay, sound_delay_value))
7078 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7080 if (Store[x][y] == EL_BD_AMOEBA)
7081 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7083 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7085 sound_delay_value = 30;
7089 if (MovDelay[x][y]) /* wait some time before growing bigger */
7092 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7094 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7095 6 - MovDelay[x][y]);
7097 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7100 if (!MovDelay[x][y])
7102 Feld[x][y] = Store[x][y];
7104 DrawLevelField(x, y);
7109 void AmoebaDisappearing(int x, int y)
7111 static unsigned long sound_delay = 0;
7112 static unsigned long sound_delay_value = 0;
7114 if (!MovDelay[x][y]) /* start new shrinking cycle */
7118 if (DelayReached(&sound_delay, sound_delay_value))
7119 sound_delay_value = 30;
7122 if (MovDelay[x][y]) /* wait some time before shrinking */
7125 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7127 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7128 6 - MovDelay[x][y]);
7130 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7133 if (!MovDelay[x][y])
7135 Feld[x][y] = EL_EMPTY;
7136 DrawLevelField(x, y);
7138 /* don't let mole enter this field in this cycle;
7139 (give priority to objects falling to this field from above) */
7145 void AmoebeAbleger(int ax, int ay)
7148 int element = Feld[ax][ay];
7149 int graphic = el2img(element);
7150 int newax = ax, neway = ay;
7151 static int xy[4][2] =
7159 if (!level.amoeba_speed)
7161 Feld[ax][ay] = EL_AMOEBA_DEAD;
7162 DrawLevelField(ax, ay);
7166 if (IS_ANIMATED(graphic))
7167 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7169 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7170 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7172 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7175 if (MovDelay[ax][ay])
7179 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7182 int x = ax + xy[start][0];
7183 int y = ay + xy[start][1];
7185 if (!IN_LEV_FIELD(x, y))
7189 if (IS_FREE(x, y) ||
7190 CAN_GROW_INTO(Feld[x][y]) ||
7191 Feld[x][y] == EL_QUICKSAND_EMPTY)
7197 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7198 if (IS_FREE(x, y) ||
7199 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7206 if (newax == ax && neway == ay)
7209 else /* normal or "filled" (BD style) amoeba */
7212 boolean waiting_for_player = FALSE;
7214 for (i = 0; i < NUM_DIRECTIONS; i++)
7216 int j = (start + i) % 4;
7217 int x = ax + xy[j][0];
7218 int y = ay + xy[j][1];
7220 if (!IN_LEV_FIELD(x, y))
7224 if (IS_FREE(x, y) ||
7225 CAN_GROW_INTO(Feld[x][y]) ||
7226 Feld[x][y] == EL_QUICKSAND_EMPTY)
7233 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7234 if (IS_FREE(x, y) ||
7235 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7242 else if (IS_PLAYER(x, y))
7243 waiting_for_player = TRUE;
7246 if (newax == ax && neway == ay) /* amoeba cannot grow */
7249 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7251 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7254 Feld[ax][ay] = EL_AMOEBA_DEAD;
7255 DrawLevelField(ax, ay);
7256 AmoebaCnt[AmoebaNr[ax][ay]]--;
7258 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7260 if (element == EL_AMOEBA_FULL)
7261 AmoebeUmwandeln(ax, ay);
7262 else if (element == EL_BD_AMOEBA)
7263 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7268 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7270 /* amoeba gets larger by growing in some direction */
7272 int new_group_nr = AmoebaNr[ax][ay];
7275 if (new_group_nr == 0)
7277 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7278 printf("AmoebeAbleger(): This should never happen!\n");
7283 AmoebaNr[newax][neway] = new_group_nr;
7284 AmoebaCnt[new_group_nr]++;
7285 AmoebaCnt2[new_group_nr]++;
7287 /* if amoeba touches other amoeba(s) after growing, unify them */
7288 AmoebenVereinigen(newax, neway);
7290 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7292 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7298 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7299 (neway == lev_fieldy - 1 && newax != ax))
7301 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7302 Store[newax][neway] = element;
7304 else if (neway == ay)
7306 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7308 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7310 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7315 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7316 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7317 Store[ax][ay] = EL_AMOEBA_DROP;
7318 ContinueMoving(ax, ay);
7322 DrawLevelField(newax, neway);
7325 void Life(int ax, int ay)
7328 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7330 int element = Feld[ax][ay];
7331 int graphic = el2img(element);
7332 boolean changed = FALSE;
7334 if (IS_ANIMATED(graphic))
7335 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7340 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7341 MovDelay[ax][ay] = life_time;
7343 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7346 if (MovDelay[ax][ay])
7350 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7352 int xx = ax+x1, yy = ay+y1;
7355 if (!IN_LEV_FIELD(xx, yy))
7358 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7360 int x = xx+x2, y = yy+y2;
7362 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7365 if (((Feld[x][y] == element ||
7366 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7368 (IS_FREE(x, y) && Stop[x][y]))
7372 if (xx == ax && yy == ay) /* field in the middle */
7374 if (nachbarn < life[0] || nachbarn > life[1])
7376 Feld[xx][yy] = EL_EMPTY;
7378 DrawLevelField(xx, yy);
7379 Stop[xx][yy] = TRUE;
7384 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7385 { /* free border field */
7386 if (nachbarn >= life[2] && nachbarn <= life[3])
7388 Feld[xx][yy] = element;
7389 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7391 DrawLevelField(xx, yy);
7392 Stop[xx][yy] = TRUE;
7397 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7398 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7399 { /* free border field */
7400 if (nachbarn >= life[2] && nachbarn <= life[3])
7402 Feld[xx][yy] = element;
7403 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7405 DrawLevelField(xx, yy);
7406 Stop[xx][yy] = TRUE;
7414 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7415 SND_GAME_OF_LIFE_GROWING);
7418 static void InitRobotWheel(int x, int y)
7420 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7423 static void RunRobotWheel(int x, int y)
7425 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7428 static void StopRobotWheel(int x, int y)
7430 if (ZX == x && ZY == y)
7434 static void InitTimegateWheel(int x, int y)
7437 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7439 /* another brainless, "type style" bug ... :-( */
7440 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7444 static void RunTimegateWheel(int x, int y)
7446 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7449 void CheckExit(int x, int y)
7451 if (local_player->gems_still_needed > 0 ||
7452 local_player->sokobanfields_still_needed > 0 ||
7453 local_player->lights_still_needed > 0)
7455 int element = Feld[x][y];
7456 int graphic = el2img(element);
7458 if (IS_ANIMATED(graphic))
7459 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7464 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7467 Feld[x][y] = EL_EXIT_OPENING;
7469 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7472 void CheckExitSP(int x, int y)
7474 if (local_player->gems_still_needed > 0)
7476 int element = Feld[x][y];
7477 int graphic = el2img(element);
7479 if (IS_ANIMATED(graphic))
7480 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7485 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7488 Feld[x][y] = EL_SP_EXIT_OPENING;
7490 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7493 static void CloseAllOpenTimegates()
7497 for (y = 0; y < lev_fieldy; y++)
7499 for (x = 0; x < lev_fieldx; x++)
7501 int element = Feld[x][y];
7503 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7505 Feld[x][y] = EL_TIMEGATE_CLOSING;
7507 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7509 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7516 void EdelsteinFunkeln(int x, int y)
7518 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7521 if (Feld[x][y] == EL_BD_DIAMOND)
7524 if (MovDelay[x][y] == 0) /* next animation frame */
7525 MovDelay[x][y] = 11 * !SimpleRND(500);
7527 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7531 if (setup.direct_draw && MovDelay[x][y])
7532 SetDrawtoField(DRAW_BUFFERED);
7534 DrawLevelElementAnimation(x, y, Feld[x][y]);
7536 if (MovDelay[x][y] != 0)
7538 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7539 10 - MovDelay[x][y]);
7541 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7543 if (setup.direct_draw)
7547 dest_x = FX + SCREENX(x) * TILEX;
7548 dest_y = FY + SCREENY(y) * TILEY;
7550 BlitBitmap(drawto_field, window,
7551 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7552 SetDrawtoField(DRAW_DIRECT);
7558 void MauerWaechst(int x, int y)
7562 if (!MovDelay[x][y]) /* next animation frame */
7563 MovDelay[x][y] = 3 * delay;
7565 if (MovDelay[x][y]) /* wait some time before next frame */
7569 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7571 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7572 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7574 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7577 if (!MovDelay[x][y])
7579 if (MovDir[x][y] == MV_LEFT)
7581 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7582 DrawLevelField(x - 1, y);
7584 else if (MovDir[x][y] == MV_RIGHT)
7586 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7587 DrawLevelField(x + 1, y);
7589 else if (MovDir[x][y] == MV_UP)
7591 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7592 DrawLevelField(x, y - 1);
7596 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7597 DrawLevelField(x, y + 1);
7600 Feld[x][y] = Store[x][y];
7602 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7603 DrawLevelField(x, y);
7608 void MauerAbleger(int ax, int ay)
7610 int element = Feld[ax][ay];
7611 int graphic = el2img(element);
7612 boolean oben_frei = FALSE, unten_frei = FALSE;
7613 boolean links_frei = FALSE, rechts_frei = FALSE;
7614 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7615 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7616 boolean new_wall = FALSE;
7618 if (IS_ANIMATED(graphic))
7619 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7621 if (!MovDelay[ax][ay]) /* start building new wall */
7622 MovDelay[ax][ay] = 6;
7624 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7627 if (MovDelay[ax][ay])
7631 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7633 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7635 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7637 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7640 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7641 element == EL_EXPANDABLE_WALL_ANY)
7645 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7646 Store[ax][ay-1] = element;
7647 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7648 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7649 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7650 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7655 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7656 Store[ax][ay+1] = element;
7657 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7658 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7659 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7660 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7665 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7666 element == EL_EXPANDABLE_WALL_ANY ||
7667 element == EL_EXPANDABLE_WALL)
7671 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7672 Store[ax-1][ay] = element;
7673 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7674 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7675 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7676 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7682 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7683 Store[ax+1][ay] = element;
7684 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7685 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7686 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7687 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7692 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7693 DrawLevelField(ax, ay);
7695 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7697 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7698 unten_massiv = TRUE;
7699 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7700 links_massiv = TRUE;
7701 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7702 rechts_massiv = TRUE;
7704 if (((oben_massiv && unten_massiv) ||
7705 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7706 element == EL_EXPANDABLE_WALL) &&
7707 ((links_massiv && rechts_massiv) ||
7708 element == EL_EXPANDABLE_WALL_VERTICAL))
7709 Feld[ax][ay] = EL_WALL;
7713 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7715 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7719 void CheckForDragon(int x, int y)
7722 boolean dragon_found = FALSE;
7723 static int xy[4][2] =
7731 for (i = 0; i < NUM_DIRECTIONS; i++)
7733 for (j = 0; j < 4; j++)
7735 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7737 if (IN_LEV_FIELD(xx, yy) &&
7738 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7740 if (Feld[xx][yy] == EL_DRAGON)
7741 dragon_found = TRUE;
7750 for (i = 0; i < NUM_DIRECTIONS; i++)
7752 for (j = 0; j < 3; j++)
7754 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7756 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7758 Feld[xx][yy] = EL_EMPTY;
7759 DrawLevelField(xx, yy);
7768 static void InitBuggyBase(int x, int y)
7770 int element = Feld[x][y];
7771 int activating_delay = FRAMES_PER_SECOND / 4;
7774 (element == EL_SP_BUGGY_BASE ?
7775 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7776 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7778 element == EL_SP_BUGGY_BASE_ACTIVE ?
7779 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7782 static void WarnBuggyBase(int x, int y)
7785 static int xy[4][2] =
7793 for (i = 0; i < NUM_DIRECTIONS; i++)
7795 int xx = x + xy[i][0], yy = y + xy[i][1];
7797 if (IS_PLAYER(xx, yy))
7799 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7806 static void InitTrap(int x, int y)
7808 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7811 static void ActivateTrap(int x, int y)
7813 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7816 static void ChangeActiveTrap(int x, int y)
7818 int graphic = IMG_TRAP_ACTIVE;
7820 /* if new animation frame was drawn, correct crumbled sand border */
7821 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7822 DrawLevelFieldCrumbledSand(x, y);
7825 static void ChangeElementNowExt(int x, int y, int target_element)
7827 int previous_move_direction = MovDir[x][y];
7829 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7830 IS_WALKABLE(Feld[x][y]));
7832 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7833 IS_WALKABLE(Feld[x][y]) &&
7837 /* check if element under player changes from accessible to unaccessible
7838 (needed for special case of dropping element which then changes) */
7839 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7840 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7843 printf("::: BOOOM! [%d, '%s']\n", target_element,
7844 element_info[target_element].token_name);
7856 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7857 RemoveMovingField(x, y);
7861 Feld[x][y] = target_element;
7864 Feld[x][y] = target_element;
7867 ResetGfxAnimation(x, y);
7868 ResetRandomAnimationValue(x, y);
7870 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7871 MovDir[x][y] = previous_move_direction;
7874 InitField_WithBug1(x, y, FALSE);
7876 InitField(x, y, FALSE);
7877 if (CAN_MOVE(Feld[x][y]))
7881 DrawLevelField(x, y);
7883 if (GFX_CRUMBLED(Feld[x][y]))
7884 DrawLevelFieldCrumbledSandNeighbours(x, y);
7888 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7892 TestIfBadThingTouchesHero(x, y);
7893 TestIfPlayerTouchesCustomElement(x, y);
7894 TestIfElementTouchesCustomElement(x, y);
7897 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7898 if (ELEM_IS_PLAYER(target_element))
7899 RelocatePlayer(x, y, target_element);
7902 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7904 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7908 TestIfBadThingTouchesHero(x, y);
7909 TestIfPlayerTouchesCustomElement(x, y);
7910 TestIfElementTouchesCustomElement(x, y);
7914 static boolean ChangeElementNow(int x, int y, int element, int page)
7916 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7918 int old_element = Feld[x][y];
7920 /* always use default change event to prevent running into a loop */
7921 if (ChangeEvent[x][y] == -1)
7922 ChangeEvent[x][y] = CE_DELAY;
7924 if (ChangeEvent[x][y] == CE_DELAY)
7926 /* reset actual trigger element and player */
7927 change->actual_trigger_element = EL_EMPTY;
7928 change->actual_trigger_player = EL_PLAYER_1;
7932 /* do not change any elements that have already changed in this frame */
7936 /* do not change already changed elements with same change event */
7937 if (Changed[x][y] & ChangeEvent[x][y])
7942 Changed[x][y] = TRUE; /* ignore all further changes in this frame */
7944 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7948 /* !!! indirect change before direct change !!! */
7949 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7952 if (change->explode)
7959 if (change->use_target_content)
7961 boolean complete_replace = TRUE;
7962 boolean can_replace[3][3];
7965 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7968 boolean is_walkable;
7969 boolean is_diggable;
7970 boolean is_collectible;
7971 boolean is_removable;
7972 boolean is_destructible;
7973 int ex = x + xx - 1;
7974 int ey = y + yy - 1;
7975 int content_element = change->target_content[xx][yy];
7978 can_replace[xx][yy] = TRUE;
7980 if (ex == x && ey == y) /* do not check changing element itself */
7983 if (content_element == EL_EMPTY_SPACE)
7985 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7990 if (!IN_LEV_FIELD(ex, ey))
7992 can_replace[xx][yy] = FALSE;
7993 complete_replace = FALSE;
7999 if (Changed[ex][ey]) /* do not change already changed elements */
8001 can_replace[xx][yy] = FALSE;
8002 complete_replace = FALSE;
8010 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8011 e = MovingOrBlocked2Element(ex, ey);
8016 is_empty = (IS_FREE(ex, ey) ||
8017 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8018 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8019 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8023 is_empty = (IS_FREE(ex, ey) ||
8024 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8026 is_empty = (IS_FREE(ex, ey) ||
8027 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8032 is_walkable = (is_empty || IS_WALKABLE(e));
8033 is_diggable = (is_empty || IS_DIGGABLE(e));
8034 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8035 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8036 is_removable = (is_diggable || is_collectible);
8038 can_replace[xx][yy] =
8039 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8040 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8041 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8042 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8043 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8044 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8045 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8047 if (!can_replace[xx][yy])
8048 complete_replace = FALSE;
8050 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8051 IS_WALKABLE(content_element)));
8053 half_destructible = (empty_for_element || IS_DIGGABLE(e));
8055 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8058 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
8059 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8060 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8062 can_replace[xx][yy] = FALSE;
8063 complete_replace = FALSE;
8068 if (!change->only_if_complete || complete_replace)
8070 boolean something_has_changed = FALSE;
8072 if (change->only_if_complete && change->use_random_replace &&
8073 RND(100) < change->random_percentage)
8076 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8078 int ex = x + xx - 1;
8079 int ey = y + yy - 1;
8080 int content_element;
8082 if (can_replace[xx][yy] && (!change->use_random_replace ||
8083 RND(100) < change->random_percentage))
8085 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8086 RemoveMovingField(ex, ey);
8088 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8090 content_element = change->target_content[xx][yy];
8091 target_element = GET_TARGET_ELEMENT(content_element, change);
8093 ChangeElementNowExt(ex, ey, target_element);
8095 something_has_changed = TRUE;
8097 /* for symmetry reasons, freeze newly created border elements */
8098 if (ex != x || ey != y)
8099 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8103 if (something_has_changed)
8104 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8109 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8111 ChangeElementNowExt(x, y, target_element);
8113 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8117 /* this uses direct change before indirect change */
8118 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
8124 static void ChangeElement(int x, int y, int page)
8126 int element = MovingOrBlocked2Element(x, y);
8127 struct ElementInfo *ei = &element_info[element];
8128 struct ElementChangeInfo *change = &ei->change_page[page];
8131 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8134 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8135 x, y, element, element_info[element].token_name);
8136 printf("ChangeElement(): This should never happen!\n");
8141 /* this can happen with classic bombs on walkable, changing elements */
8142 if (!CAN_CHANGE(element))
8145 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8146 ChangeDelay[x][y] = 0;
8152 if (ChangeDelay[x][y] == 0) /* initialize element change */
8154 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
8155 RND(change->delay_random * change->delay_frames)) + 1;
8157 ResetGfxAnimation(x, y);
8158 ResetRandomAnimationValue(x, y);
8160 if (change->pre_change_function)
8161 change->pre_change_function(x, y);
8164 ChangeDelay[x][y]--;
8166 if (ChangeDelay[x][y] != 0) /* continue element change */
8168 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8170 if (IS_ANIMATED(graphic))
8171 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8173 if (change->change_function)
8174 change->change_function(x, y);
8176 else /* finish element change */
8178 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8180 page = ChangePage[x][y];
8181 ChangePage[x][y] = -1;
8183 change = &ei->change_page[page];
8187 if (IS_MOVING(x, y) && !change->explode)
8189 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8192 ChangeDelay[x][y] = 1; /* try change after next move step */
8193 ChangePage[x][y] = page; /* remember page to use for change */
8198 if (ChangeElementNow(x, y, element, page))
8200 if (change->post_change_function)
8201 change->post_change_function(x, y);
8206 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8207 int trigger_element,
8214 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8216 if (!(trigger_events[trigger_element][trigger_event]))
8219 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8221 int element = EL_CUSTOM_START + i;
8223 boolean change_element = FALSE;
8226 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8229 for (j = 0; j < element_info[element].num_change_pages; j++)
8231 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8233 if (change->can_change &&
8234 change->has_event[trigger_event] &&
8235 change->trigger_side & trigger_side &&
8236 change->trigger_player & trigger_player &&
8237 change->trigger_page & trigger_page_bits &&
8238 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8241 if (!(change->has_event[trigger_event]))
8242 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8243 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8246 change_element = TRUE;
8249 change->actual_trigger_element = trigger_element;
8250 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8256 if (!change_element)
8259 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8262 if (x == lx && y == ly) /* do not change trigger element itself */
8266 if (Feld[x][y] == element)
8268 ChangeDelay[x][y] = 1;
8269 ChangeEvent[x][y] = trigger_event;
8270 ChangeElement(x, y, page);
8278 static boolean CheckElementChangeExt(int x, int y,
8280 int trigger_element,
8286 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8289 if (Feld[x][y] == EL_BLOCKED)
8291 Blocked2Moving(x, y, &x, &y);
8292 element = Feld[x][y];
8296 if (Feld[x][y] != element) /* check if element has already changed */
8299 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8300 Feld[x][y], element_info[Feld[x][y]].token_name,
8301 element, element_info[element].token_name,
8310 if (trigger_page < 0)
8312 boolean change_element = FALSE;
8315 for (i = 0; i < element_info[element].num_change_pages; i++)
8317 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8319 if (change->can_change &&
8320 change->has_event[trigger_event] &&
8321 change->trigger_side & trigger_side &&
8322 change->trigger_player & trigger_player)
8324 change_element = TRUE;
8327 change->actual_trigger_element = trigger_element;
8328 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8334 if (!change_element)
8339 struct ElementInfo *ei = &element_info[element];
8340 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8342 change->actual_trigger_element = trigger_element;
8343 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8348 /* !!! this check misses pages with same event, but different side !!! */
8350 if (trigger_page < 0)
8351 trigger_page = element_info[element].event_page_nr[trigger_event];
8353 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8357 ChangeDelay[x][y] = 1;
8358 ChangeEvent[x][y] = trigger_event;
8359 ChangeElement(x, y, trigger_page);
8364 static void PlayPlayerSound(struct PlayerInfo *player)
8366 int jx = player->jx, jy = player->jy;
8367 int element = player->element_nr;
8368 int last_action = player->last_action_waiting;
8369 int action = player->action_waiting;
8371 if (player->is_waiting)
8373 if (action != last_action)
8374 PlayLevelSoundElementAction(jx, jy, element, action);
8376 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8380 if (action != last_action)
8381 StopSound(element_info[element].sound[last_action]);
8383 if (last_action == ACTION_SLEEPING)
8384 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8388 static void PlayAllPlayersSound()
8392 for (i = 0; i < MAX_PLAYERS; i++)
8393 if (stored_player[i].active)
8394 PlayPlayerSound(&stored_player[i]);
8397 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8399 boolean last_waiting = player->is_waiting;
8400 int move_dir = player->MovDir;
8402 player->last_action_waiting = player->action_waiting;
8406 if (!last_waiting) /* not waiting -> waiting */
8408 player->is_waiting = TRUE;
8410 player->frame_counter_bored =
8412 game.player_boring_delay_fixed +
8413 SimpleRND(game.player_boring_delay_random);
8414 player->frame_counter_sleeping =
8416 game.player_sleeping_delay_fixed +
8417 SimpleRND(game.player_sleeping_delay_random);
8419 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8422 if (game.player_sleeping_delay_fixed +
8423 game.player_sleeping_delay_random > 0 &&
8424 player->anim_delay_counter == 0 &&
8425 player->post_delay_counter == 0 &&
8426 FrameCounter >= player->frame_counter_sleeping)
8427 player->is_sleeping = TRUE;
8428 else if (game.player_boring_delay_fixed +
8429 game.player_boring_delay_random > 0 &&
8430 FrameCounter >= player->frame_counter_bored)
8431 player->is_bored = TRUE;
8433 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8434 player->is_bored ? ACTION_BORING :
8437 if (player->is_sleeping)
8439 if (player->num_special_action_sleeping > 0)
8441 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8443 int last_special_action = player->special_action_sleeping;
8444 int num_special_action = player->num_special_action_sleeping;
8445 int special_action =
8446 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8447 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8448 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8449 last_special_action + 1 : ACTION_SLEEPING);
8450 int special_graphic =
8451 el_act_dir2img(player->element_nr, special_action, move_dir);
8453 player->anim_delay_counter =
8454 graphic_info[special_graphic].anim_delay_fixed +
8455 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8456 player->post_delay_counter =
8457 graphic_info[special_graphic].post_delay_fixed +
8458 SimpleRND(graphic_info[special_graphic].post_delay_random);
8460 player->special_action_sleeping = special_action;
8463 if (player->anim_delay_counter > 0)
8465 player->action_waiting = player->special_action_sleeping;
8466 player->anim_delay_counter--;
8468 else if (player->post_delay_counter > 0)
8470 player->post_delay_counter--;
8474 else if (player->is_bored)
8476 if (player->num_special_action_bored > 0)
8478 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8480 int special_action =
8481 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8482 int special_graphic =
8483 el_act_dir2img(player->element_nr, special_action, move_dir);
8485 player->anim_delay_counter =
8486 graphic_info[special_graphic].anim_delay_fixed +
8487 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8488 player->post_delay_counter =
8489 graphic_info[special_graphic].post_delay_fixed +
8490 SimpleRND(graphic_info[special_graphic].post_delay_random);
8492 player->special_action_bored = special_action;
8495 if (player->anim_delay_counter > 0)
8497 player->action_waiting = player->special_action_bored;
8498 player->anim_delay_counter--;
8500 else if (player->post_delay_counter > 0)
8502 player->post_delay_counter--;
8507 else if (last_waiting) /* waiting -> not waiting */
8509 player->is_waiting = FALSE;
8510 player->is_bored = FALSE;
8511 player->is_sleeping = FALSE;
8513 player->frame_counter_bored = -1;
8514 player->frame_counter_sleeping = -1;
8516 player->anim_delay_counter = 0;
8517 player->post_delay_counter = 0;
8519 player->action_waiting = ACTION_DEFAULT;
8521 player->special_action_bored = ACTION_DEFAULT;
8522 player->special_action_sleeping = ACTION_DEFAULT;
8527 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8530 static byte stored_player_action[MAX_PLAYERS];
8531 static int num_stored_actions = 0;
8533 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8534 int left = player_action & JOY_LEFT;
8535 int right = player_action & JOY_RIGHT;
8536 int up = player_action & JOY_UP;
8537 int down = player_action & JOY_DOWN;
8538 int button1 = player_action & JOY_BUTTON_1;
8539 int button2 = player_action & JOY_BUTTON_2;
8540 int dx = (left ? -1 : right ? 1 : 0);
8541 int dy = (up ? -1 : down ? 1 : 0);
8544 stored_player_action[player->index_nr] = 0;
8545 num_stored_actions++;
8549 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8552 if (!player->active || tape.pausing)
8556 printf("::: [%d %d %d %d] [%d %d]\n",
8557 left, right, up, down, button1, button2);
8563 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8568 if (player->MovPos == 0)
8569 CheckGravityMovement(player);
8572 snapped = SnapField(player, dx, dy);
8576 dropped = DropElement(player);
8578 moved = MovePlayer(player, dx, dy);
8581 if (tape.single_step && tape.recording && !tape.pausing)
8583 if (button1 || (dropped && !moved))
8585 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8586 SnapField(player, 0, 0); /* stop snapping */
8590 SetPlayerWaiting(player, FALSE);
8593 return player_action;
8595 stored_player_action[player->index_nr] = player_action;
8601 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8604 /* no actions for this player (no input at player's configured device) */
8606 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8607 SnapField(player, 0, 0);
8608 CheckGravityMovementWhenNotMoving(player);
8610 if (player->MovPos == 0)
8611 SetPlayerWaiting(player, TRUE);
8613 if (player->MovPos == 0) /* needed for tape.playing */
8614 player->is_moving = FALSE;
8616 player->is_dropping = FALSE;
8622 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8624 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8626 TapeRecordAction(stored_player_action);
8627 num_stored_actions = 0;
8634 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8636 static byte stored_player_action[MAX_PLAYERS];
8637 static int num_stored_actions = 0;
8638 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8639 int left = player_action & JOY_LEFT;
8640 int right = player_action & JOY_RIGHT;
8641 int up = player_action & JOY_UP;
8642 int down = player_action & JOY_DOWN;
8643 int button1 = player_action & JOY_BUTTON_1;
8644 int button2 = player_action & JOY_BUTTON_2;
8645 int dx = (left ? -1 : right ? 1 : 0);
8646 int dy = (up ? -1 : down ? 1 : 0);
8648 stored_player_action[player->index_nr] = 0;
8649 num_stored_actions++;
8651 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8653 if (!player->active || tape.pausing)
8658 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8661 snapped = SnapField(player, dx, dy);
8665 dropped = DropElement(player);
8667 moved = MovePlayer(player, dx, dy);
8670 if (tape.single_step && tape.recording && !tape.pausing)
8672 if (button1 || (dropped && !moved))
8674 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8675 SnapField(player, 0, 0); /* stop snapping */
8679 stored_player_action[player->index_nr] = player_action;
8683 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8685 /* no actions for this player (no input at player's configured device) */
8687 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8688 SnapField(player, 0, 0);
8689 CheckGravityMovementWhenNotMoving(player);
8691 if (player->MovPos == 0)
8692 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8694 if (player->MovPos == 0) /* needed for tape.playing */
8695 player->is_moving = FALSE;
8698 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8700 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8702 TapeRecordAction(stored_player_action);
8703 num_stored_actions = 0;
8708 void AdvanceFrameAndPlayerCounters(int player_nr)
8712 /* advance frame counters (global frame counter and time frame counter) */
8716 /* advance player counters (counters for move delay, move animation etc.) */
8717 for (i = 0; i < MAX_PLAYERS; i++)
8719 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8721 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8723 if (!advance_player_counters) /* not all players may be affected */
8726 stored_player[i].Frame += move_frames;
8728 if (stored_player[i].MovPos != 0)
8729 stored_player[i].StepFrame += move_frames;
8731 #if USE_NEW_MOVE_DELAY
8732 if (stored_player[i].move_delay > 0)
8733 stored_player[i].move_delay--;
8736 #if USE_NEW_PUSH_DELAY
8737 /* due to bugs in previous versions, counter must count up, not down */
8738 if (stored_player[i].push_delay != -1)
8739 stored_player[i].push_delay++;
8742 if (stored_player[i].drop_delay > 0)
8743 stored_player[i].drop_delay--;
8749 static unsigned long game_frame_delay = 0;
8750 unsigned long game_frame_delay_value;
8751 int magic_wall_x = 0, magic_wall_y = 0;
8752 int i, x, y, element, graphic;
8753 byte *recorded_player_action;
8754 byte summarized_player_action = 0;
8756 byte tape_action[MAX_PLAYERS];
8759 if (game_status != GAME_MODE_PLAYING)
8762 game_frame_delay_value =
8763 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8765 if (tape.playing && tape.warp_forward && !tape.pausing)
8766 game_frame_delay_value = 0;
8768 /* ---------- main game synchronization point ---------- */
8770 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8772 if (network_playing && !network_player_action_received)
8776 printf("DEBUG: try to get network player actions in time\n");
8780 #if defined(NETWORK_AVALIABLE)
8781 /* last chance to get network player actions without main loop delay */
8785 if (game_status != GAME_MODE_PLAYING)
8788 if (!network_player_action_received)
8792 printf("DEBUG: failed to get network player actions in time\n");
8803 printf("::: getting new tape action [%d]\n", FrameCounter);
8806 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8809 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8810 if (recorded_player_action == NULL && tape.pausing)
8815 printf("::: %d\n", stored_player[0].action);
8819 if (recorded_player_action != NULL)
8820 for (i = 0; i < MAX_PLAYERS; i++)
8821 stored_player[i].action = recorded_player_action[i];
8824 for (i = 0; i < MAX_PLAYERS; i++)
8826 summarized_player_action |= stored_player[i].action;
8828 if (!network_playing)
8829 stored_player[i].effective_action = stored_player[i].action;
8832 #if defined(NETWORK_AVALIABLE)
8833 if (network_playing)
8834 SendToServer_MovePlayer(summarized_player_action);
8837 if (!options.network && !setup.team_mode)
8838 local_player->effective_action = summarized_player_action;
8841 if (recorded_player_action != NULL)
8842 for (i = 0; i < MAX_PLAYERS; i++)
8843 stored_player[i].effective_action = recorded_player_action[i];
8847 for (i = 0; i < MAX_PLAYERS; i++)
8849 tape_action[i] = stored_player[i].effective_action;
8851 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8852 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8855 /* only save actions from input devices, but not programmed actions */
8857 TapeRecordAction(tape_action);
8860 for (i = 0; i < MAX_PLAYERS; i++)
8862 int actual_player_action = stored_player[i].effective_action;
8865 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8866 - rnd_equinox_tetrachloride 048
8867 - rnd_equinox_tetrachloride_ii 096
8868 - rnd_emanuel_schmieg 002
8869 - doctor_sloan_ww 001, 020
8871 if (stored_player[i].MovPos == 0)
8872 CheckGravityMovement(&stored_player[i]);
8876 /* overwrite programmed action with tape action */
8877 if (stored_player[i].programmed_action)
8878 actual_player_action = stored_player[i].programmed_action;
8882 if (stored_player[i].programmed_action)
8883 printf("::: %d\n", stored_player[i].programmed_action);
8886 if (recorded_player_action)
8889 if (stored_player[i].programmed_action &&
8890 stored_player[i].programmed_action != recorded_player_action[i])
8891 printf("::: %d: %d <-> %d\n", i,
8892 stored_player[i].programmed_action, recorded_player_action[i]);
8896 actual_player_action = recorded_player_action[i];
8901 /* overwrite tape action with programmed action */
8902 if (stored_player[i].programmed_action)
8903 actual_player_action = stored_player[i].programmed_action;
8908 printf("::: action: %d: %x [%d]\n",
8909 stored_player[i].MovPos, actual_player_action, FrameCounter);
8913 PlayerActions(&stored_player[i], actual_player_action);
8915 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8917 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8918 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8921 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8926 TapeRecordAction(tape_action);
8929 network_player_action_received = FALSE;
8931 ScrollScreen(NULL, SCROLL_GO_ON);
8937 for (i = 0; i < MAX_PLAYERS; i++)
8938 stored_player[i].Frame++;
8942 /* for backwards compatibility, the following code emulates a fixed bug that
8943 occured when pushing elements (causing elements that just made their last
8944 pushing step to already (if possible) make their first falling step in the
8945 same game frame, which is bad); this code is also needed to use the famous
8946 "spring push bug" which is used in older levels and might be wanted to be
8947 used also in newer levels, but in this case the buggy pushing code is only
8948 affecting the "spring" element and no other elements */
8951 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8953 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8956 for (i = 0; i < MAX_PLAYERS; i++)
8958 struct PlayerInfo *player = &stored_player[i];
8963 if (player->active && player->is_pushing && player->is_moving &&
8965 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8966 Feld[x][y] == EL_SPRING))
8968 if (player->active && player->is_pushing && player->is_moving &&
8972 ContinueMoving(x, y);
8974 /* continue moving after pushing (this is actually a bug) */
8975 if (!IS_MOVING(x, y))
8984 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8986 Changed[x][y] = FALSE;
8987 ChangeEvent[x][y] = -1;
8989 #if USE_NEW_BLOCK_STYLE
8990 /* this must be handled before main playfield loop */
8991 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8994 if (MovDelay[x][y] <= 0)
9000 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9002 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9003 printf("GameActions(): This should never happen!\n");
9005 ChangePage[x][y] = -1;
9010 if (WasJustMoving[x][y] > 0)
9011 WasJustMoving[x][y]--;
9012 if (WasJustFalling[x][y] > 0)
9013 WasJustFalling[x][y]--;
9014 if (CheckCollision[x][y] > 0)
9015 CheckCollision[x][y]--;
9020 /* reset finished pushing action (not done in ContinueMoving() to allow
9021 continous pushing animation for elements with zero push delay) */
9022 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9024 ResetGfxAnimation(x, y);
9025 DrawLevelField(x, y);
9030 if (IS_BLOCKED(x, y))
9034 Blocked2Moving(x, y, &oldx, &oldy);
9035 if (!IS_MOVING(oldx, oldy))
9037 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9038 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9039 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9040 printf("GameActions(): This should never happen!\n");
9046 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9048 element = Feld[x][y];
9050 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9052 graphic = el2img(element);
9058 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9060 element = graphic = 0;
9064 if (graphic_info[graphic].anim_global_sync)
9065 GfxFrame[x][y] = FrameCounter;
9067 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9068 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9069 ResetRandomAnimationValue(x, y);
9071 SetRandomAnimationValue(x, y);
9074 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9077 if (IS_INACTIVE(element))
9079 if (IS_ANIMATED(graphic))
9080 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9086 /* this may take place after moving, so 'element' may have changed */
9088 if (IS_CHANGING(x, y))
9090 if (IS_CHANGING(x, y) &&
9091 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9095 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9096 element_info[element].event_page_nr[CE_DELAY]);
9098 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9101 element = Feld[x][y];
9102 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9106 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9111 element = Feld[x][y];
9112 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9114 if (element == EL_MOLE)
9115 printf("::: %d, %d, %d [%d]\n",
9116 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9120 if (element == EL_YAMYAM)
9121 printf("::: %d, %d, %d\n",
9122 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9126 if (IS_ANIMATED(graphic) &&
9130 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9133 if (element == EL_BUG)
9134 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9138 if (element == EL_MOLE)
9139 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9143 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9144 EdelsteinFunkeln(x, y);
9146 else if ((element == EL_ACID ||
9147 element == EL_EXIT_OPEN ||
9148 element == EL_SP_EXIT_OPEN ||
9149 element == EL_SP_TERMINAL ||
9150 element == EL_SP_TERMINAL_ACTIVE ||
9151 element == EL_EXTRA_TIME ||
9152 element == EL_SHIELD_NORMAL ||
9153 element == EL_SHIELD_DEADLY) &&
9154 IS_ANIMATED(graphic))
9155 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9156 else if (IS_MOVING(x, y))
9157 ContinueMoving(x, y);
9158 else if (IS_ACTIVE_BOMB(element))
9159 CheckDynamite(x, y);
9161 else if (element == EL_EXPLOSION && !game.explosions_delayed)
9162 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9164 else if (element == EL_AMOEBA_GROWING)
9165 AmoebeWaechst(x, y);
9166 else if (element == EL_AMOEBA_SHRINKING)
9167 AmoebaDisappearing(x, y);
9169 #if !USE_NEW_AMOEBA_CODE
9170 else if (IS_AMOEBALIVE(element))
9171 AmoebeAbleger(x, y);
9174 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9176 else if (element == EL_EXIT_CLOSED)
9178 else if (element == EL_SP_EXIT_CLOSED)
9180 else if (element == EL_EXPANDABLE_WALL_GROWING)
9182 else if (element == EL_EXPANDABLE_WALL ||
9183 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9184 element == EL_EXPANDABLE_WALL_VERTICAL ||
9185 element == EL_EXPANDABLE_WALL_ANY)
9187 else if (element == EL_FLAMES)
9188 CheckForDragon(x, y);
9190 else if (IS_AUTO_CHANGING(element))
9191 ChangeElement(x, y);
9193 else if (element == EL_EXPLOSION)
9194 ; /* drawing of correct explosion animation is handled separately */
9195 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9196 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9199 /* this may take place after moving, so 'element' may have changed */
9200 if (IS_AUTO_CHANGING(Feld[x][y]))
9201 ChangeElement(x, y);
9204 if (IS_BELT_ACTIVE(element))
9205 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9207 if (game.magic_wall_active)
9209 int jx = local_player->jx, jy = local_player->jy;
9211 /* play the element sound at the position nearest to the player */
9212 if ((element == EL_MAGIC_WALL_FULL ||
9213 element == EL_MAGIC_WALL_ACTIVE ||
9214 element == EL_MAGIC_WALL_EMPTYING ||
9215 element == EL_BD_MAGIC_WALL_FULL ||
9216 element == EL_BD_MAGIC_WALL_ACTIVE ||
9217 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9218 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9226 #if USE_NEW_AMOEBA_CODE
9227 /* new experimental amoeba growth stuff */
9229 if (!(FrameCounter % 8))
9232 static unsigned long random = 1684108901;
9234 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9237 x = (random >> 10) % lev_fieldx;
9238 y = (random >> 20) % lev_fieldy;
9240 x = RND(lev_fieldx);
9241 y = RND(lev_fieldy);
9243 element = Feld[x][y];
9246 if (!IS_PLAYER(x,y) &&
9247 (element == EL_EMPTY ||
9248 CAN_GROW_INTO(element) ||
9249 element == EL_QUICKSAND_EMPTY ||
9250 element == EL_ACID_SPLASH_LEFT ||
9251 element == EL_ACID_SPLASH_RIGHT))
9253 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9254 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9255 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9256 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9257 Feld[x][y] = EL_AMOEBA_DROP;
9260 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9261 if (!IS_PLAYER(x,y) &&
9262 (element == EL_EMPTY ||
9263 element == EL_SAND ||
9264 element == EL_QUICKSAND_EMPTY ||
9265 element == EL_ACID_SPLASH_LEFT ||
9266 element == EL_ACID_SPLASH_RIGHT))
9268 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9269 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9270 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9271 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9272 Feld[x][y] = EL_AMOEBA_DROP;
9276 random = random * 129 + 1;
9282 if (game.explosions_delayed)
9285 game.explosions_delayed = FALSE;
9287 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9289 element = Feld[x][y];
9291 if (ExplodeField[x][y])
9292 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9293 else if (element == EL_EXPLOSION)
9294 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9296 ExplodeField[x][y] = EX_TYPE_NONE;
9299 game.explosions_delayed = TRUE;
9302 if (game.magic_wall_active)
9304 if (!(game.magic_wall_time_left % 4))
9306 int element = Feld[magic_wall_x][magic_wall_y];
9308 if (element == EL_BD_MAGIC_WALL_FULL ||
9309 element == EL_BD_MAGIC_WALL_ACTIVE ||
9310 element == EL_BD_MAGIC_WALL_EMPTYING)
9311 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9313 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9316 if (game.magic_wall_time_left > 0)
9318 game.magic_wall_time_left--;
9319 if (!game.magic_wall_time_left)
9321 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9323 element = Feld[x][y];
9325 if (element == EL_MAGIC_WALL_ACTIVE ||
9326 element == EL_MAGIC_WALL_FULL)
9328 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9329 DrawLevelField(x, y);
9331 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9332 element == EL_BD_MAGIC_WALL_FULL)
9334 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9335 DrawLevelField(x, y);
9339 game.magic_wall_active = FALSE;
9344 if (game.light_time_left > 0)
9346 game.light_time_left--;
9348 if (game.light_time_left == 0)
9349 RedrawAllLightSwitchesAndInvisibleElements();
9352 if (game.timegate_time_left > 0)
9354 game.timegate_time_left--;
9356 if (game.timegate_time_left == 0)
9357 CloseAllOpenTimegates();
9360 for (i = 0; i < MAX_PLAYERS; i++)
9362 struct PlayerInfo *player = &stored_player[i];
9364 if (SHIELD_ON(player))
9366 if (player->shield_deadly_time_left)
9367 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9368 else if (player->shield_normal_time_left)
9369 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9373 if (TimeFrames >= FRAMES_PER_SECOND)
9378 for (i = 0; i < MAX_PLAYERS; i++)
9380 struct PlayerInfo *player = &stored_player[i];
9382 if (SHIELD_ON(player))
9384 player->shield_normal_time_left--;
9386 if (player->shield_deadly_time_left > 0)
9387 player->shield_deadly_time_left--;
9391 if (!level.use_step_counter)
9399 if (TimeLeft <= 10 && setup.time_limit)
9400 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9402 DrawGameValue_Time(TimeLeft);
9404 if (!TimeLeft && setup.time_limit)
9405 for (i = 0; i < MAX_PLAYERS; i++)
9406 KillHero(&stored_player[i]);
9408 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9409 DrawGameValue_Time(TimePlayed);
9412 if (tape.recording || tape.playing)
9413 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9417 PlayAllPlayersSound();
9419 if (options.debug) /* calculate frames per second */
9421 static unsigned long fps_counter = 0;
9422 static int fps_frames = 0;
9423 unsigned long fps_delay_ms = Counter() - fps_counter;
9427 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9429 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9432 fps_counter = Counter();
9435 redraw_mask |= REDRAW_FPS;
9439 if (stored_player[0].jx != stored_player[0].last_jx ||
9440 stored_player[0].jy != stored_player[0].last_jy)
9441 printf("::: %d, %d, %d, %d, %d\n",
9442 stored_player[0].MovDir,
9443 stored_player[0].MovPos,
9444 stored_player[0].GfxPos,
9445 stored_player[0].Frame,
9446 stored_player[0].StepFrame);
9449 #if USE_NEW_MOVE_DELAY
9450 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9455 for (i = 0; i < MAX_PLAYERS; i++)
9458 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9460 stored_player[i].Frame += move_frames;
9462 if (stored_player[i].MovPos != 0)
9463 stored_player[i].StepFrame += move_frames;
9465 #if USE_NEW_MOVE_DELAY
9466 if (stored_player[i].move_delay > 0)
9467 stored_player[i].move_delay--;
9470 if (stored_player[i].drop_delay > 0)
9471 stored_player[i].drop_delay--;
9476 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9478 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9480 local_player->show_envelope = 0;
9484 #if USE_NEW_RANDOMIZE
9485 /* use random number generator in every frame to make it less predictable */
9486 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9491 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9493 int min_x = x, min_y = y, max_x = x, max_y = y;
9496 for (i = 0; i < MAX_PLAYERS; i++)
9498 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9500 if (!stored_player[i].active || &stored_player[i] == player)
9503 min_x = MIN(min_x, jx);
9504 min_y = MIN(min_y, jy);
9505 max_x = MAX(max_x, jx);
9506 max_y = MAX(max_y, jy);
9509 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9512 static boolean AllPlayersInVisibleScreen()
9516 for (i = 0; i < MAX_PLAYERS; i++)
9518 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9520 if (!stored_player[i].active)
9523 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9530 void ScrollLevel(int dx, int dy)
9532 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9535 BlitBitmap(drawto_field, drawto_field,
9536 FX + TILEX * (dx == -1) - softscroll_offset,
9537 FY + TILEY * (dy == -1) - softscroll_offset,
9538 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9539 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9540 FX + TILEX * (dx == 1) - softscroll_offset,
9541 FY + TILEY * (dy == 1) - softscroll_offset);
9545 x = (dx == 1 ? BX1 : BX2);
9546 for (y = BY1; y <= BY2; y++)
9547 DrawScreenField(x, y);
9552 y = (dy == 1 ? BY1 : BY2);
9553 for (x = BX1; x <= BX2; x++)
9554 DrawScreenField(x, y);
9557 redraw_mask |= REDRAW_FIELD;
9561 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9563 int nextx = x + dx, nexty = y + dy;
9564 int element = Feld[x][y];
9567 element != EL_SP_PORT_LEFT &&
9568 element != EL_SP_GRAVITY_PORT_LEFT &&
9569 element != EL_SP_PORT_HORIZONTAL &&
9570 element != EL_SP_PORT_ANY) ||
9572 element != EL_SP_PORT_RIGHT &&
9573 element != EL_SP_GRAVITY_PORT_RIGHT &&
9574 element != EL_SP_PORT_HORIZONTAL &&
9575 element != EL_SP_PORT_ANY) ||
9577 element != EL_SP_PORT_UP &&
9578 element != EL_SP_GRAVITY_PORT_UP &&
9579 element != EL_SP_PORT_VERTICAL &&
9580 element != EL_SP_PORT_ANY) ||
9582 element != EL_SP_PORT_DOWN &&
9583 element != EL_SP_GRAVITY_PORT_DOWN &&
9584 element != EL_SP_PORT_VERTICAL &&
9585 element != EL_SP_PORT_ANY) ||
9586 !IN_LEV_FIELD(nextx, nexty) ||
9587 !IS_FREE(nextx, nexty))
9594 static boolean canFallDown(struct PlayerInfo *player)
9596 int jx = player->jx, jy = player->jy;
9598 return (IN_LEV_FIELD(jx, jy + 1) &&
9599 (IS_FREE(jx, jy + 1) ||
9600 #if USE_NEW_BLOCK_STYLE
9601 #if USE_GRAVITY_BUGFIX_OLD
9602 Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9605 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9606 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9607 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9610 static boolean canPassField(int x, int y, int move_dir)
9612 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9613 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9614 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9617 int element = Feld[x][y];
9619 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9620 !CAN_MOVE(element) &&
9621 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9622 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9623 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9626 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9628 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9629 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9630 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9634 int nextx = newx + dx;
9635 int nexty = newy + dy;
9639 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9640 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9642 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9644 (IS_DIGGABLE(Feld[newx][newy]) ||
9645 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9646 canPassField(newx, newy, move_dir)));
9649 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9650 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
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_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9658 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9659 canPassField(newx, newy, move_dir)));
9661 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9662 (IS_DIGGABLE(Feld[newx][newy]) ||
9663 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9664 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9665 !CAN_MOVE(Feld[newx][newy]) &&
9666 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9667 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9668 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9674 static void CheckGravityMovement(struct PlayerInfo *player)
9676 if (game.gravity && !player->programmed_action)
9679 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9680 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9682 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9683 int move_dir_vertical = player->action & MV_VERTICAL;
9687 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9689 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9692 int jx = player->jx, jy = player->jy;
9694 boolean player_is_moving_to_valid_field =
9695 (!player_is_snapping &&
9696 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9697 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9701 (player->last_move_dir & MV_HORIZONTAL ?
9702 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9703 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9707 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9708 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9709 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9710 int new_jx = jx + dx, new_jy = jy + dy;
9711 int nextx = new_jx + dx, nexty = new_jy + dy;
9717 boolean player_can_fall_down = canFallDown(player);
9719 boolean player_can_fall_down =
9720 (IN_LEV_FIELD(jx, jy + 1) &&
9721 (IS_FREE(jx, jy + 1) ||
9722 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9726 boolean player_can_fall_down =
9727 (IN_LEV_FIELD(jx, jy + 1) &&
9728 (IS_FREE(jx, jy + 1)));
9732 boolean player_is_moving_to_valid_field =
9735 !player_is_snapping &&
9739 IN_LEV_FIELD(new_jx, new_jy) &&
9740 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9741 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9742 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9743 IN_LEV_FIELD(nextx, nexty) &&
9744 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9746 IN_LEV_FIELD(new_jx, new_jy) &&
9747 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9748 Feld[new_jx][new_jy] == EL_SAND ||
9749 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9750 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9751 /* !!! extend EL_SAND to anything diggable !!! */
9757 boolean player_is_standing_on_valid_field =
9758 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9759 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9763 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9764 player_can_fall_down,
9765 player_is_standing_on_valid_field,
9766 player_is_moving_to_valid_field,
9767 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9768 player->effective_action,
9769 player->can_fall_into_acid);
9772 if (player_can_fall_down &&
9774 !player_is_standing_on_valid_field &&
9776 !player_is_moving_to_valid_field)
9779 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9780 jx, jy, FrameCounter);
9783 player->programmed_action = MV_DOWN;
9788 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9791 return CheckGravityMovement(player);
9794 if (game.gravity && !player->programmed_action)
9796 int jx = player->jx, jy = player->jy;
9797 boolean field_under_player_is_free =
9798 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9799 boolean player_is_standing_on_valid_field =
9800 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9801 (IS_WALKABLE(Feld[jx][jy]) &&
9802 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9804 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9805 player->programmed_action = MV_DOWN;
9811 -----------------------------------------------------------------------------
9812 dx, dy: direction (non-diagonal) to try to move the player to
9813 real_dx, real_dy: direction as read from input device (can be diagonal)
9816 boolean MovePlayerOneStep(struct PlayerInfo *player,
9817 int dx, int dy, int real_dx, int real_dy)
9820 static int trigger_sides[4][2] =
9822 /* enter side leave side */
9823 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9824 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9825 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9826 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9828 int move_direction = (dx == -1 ? MV_LEFT :
9829 dx == +1 ? MV_RIGHT :
9831 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9832 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9833 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9835 int jx = player->jx, jy = player->jy;
9836 int new_jx = jx + dx, new_jy = jy + dy;
9840 if (!player->active || (!dx && !dy))
9841 return MF_NO_ACTION;
9843 player->MovDir = (dx < 0 ? MV_LEFT :
9846 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9848 if (!IN_LEV_FIELD(new_jx, new_jy))
9849 return MF_NO_ACTION;
9851 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9852 return MF_NO_ACTION;
9855 element = MovingOrBlocked2Element(new_jx, new_jy);
9857 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9860 if (DONT_RUN_INTO(element))
9862 if (element == EL_ACID && dx == 0 && dy == 1)
9864 SplashAcid(new_jx, new_jy);
9865 Feld[jx][jy] = EL_PLAYER_1;
9866 InitMovingField(jx, jy, MV_DOWN);
9867 Store[jx][jy] = EL_ACID;
9868 ContinueMoving(jx, jy);
9872 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9877 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9878 if (can_move != MF_MOVING)
9881 /* check if DigField() has caused relocation of the player */
9882 if (player->jx != jx || player->jy != jy)
9883 return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9885 StorePlayer[jx][jy] = 0;
9886 player->last_jx = jx;
9887 player->last_jy = jy;
9888 player->jx = new_jx;
9889 player->jy = new_jy;
9890 StorePlayer[new_jx][new_jy] = player->element_nr;
9893 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9895 player->step_counter++;
9898 player->drop_delay = 0;
9901 PlayerVisit[jx][jy] = FrameCounter;
9903 ScrollPlayer(player, SCROLL_INIT);
9906 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9908 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9910 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9913 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9915 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9916 CE_OTHER_GETS_ENTERED, enter_side);
9917 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9918 CE_ENTERED_BY_PLAYER, enter_side);
9925 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9927 int jx = player->jx, jy = player->jy;
9928 int old_jx = jx, old_jy = jy;
9929 int moved = MF_NO_ACTION;
9932 if (!player->active)
9937 if (player->MovPos == 0)
9939 player->is_moving = FALSE;
9940 player->is_digging = FALSE;
9941 player->is_collecting = FALSE;
9942 player->is_snapping = FALSE;
9943 player->is_pushing = FALSE;
9949 if (!player->active || (!dx && !dy))
9954 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9962 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9963 player->move_delay + player->move_delay_value);
9966 #if USE_NEW_MOVE_DELAY
9967 if (player->move_delay > 0)
9969 if (!FrameReached(&player->move_delay, player->move_delay_value))
9973 printf("::: can NOT move\n");
9979 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9980 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9987 printf("::: COULD move now\n");
9990 #if USE_NEW_MOVE_DELAY
9991 player->move_delay = -1; /* set to "uninitialized" value */
9994 /* store if player is automatically moved to next field */
9995 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9997 /* remove the last programmed player action */
9998 player->programmed_action = 0;
10000 if (player->MovPos)
10002 /* should only happen if pre-1.2 tape recordings are played */
10003 /* this is only for backward compatibility */
10005 int original_move_delay_value = player->move_delay_value;
10008 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10012 /* scroll remaining steps with finest movement resolution */
10013 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10015 while (player->MovPos)
10017 ScrollPlayer(player, SCROLL_GO_ON);
10018 ScrollScreen(NULL, SCROLL_GO_ON);
10020 #if USE_NEW_MOVE_DELAY
10021 AdvanceFrameAndPlayerCounters(player->index_nr);
10030 player->move_delay_value = original_move_delay_value;
10033 if (player->last_move_dir & MV_HORIZONTAL)
10035 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10036 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10040 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10041 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10047 if (moved & MF_MOVING && !ScreenMovPos &&
10048 (player == local_player || !options.network))
10050 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10051 int offset = (setup.scroll_delay ? 3 : 0);
10053 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10055 /* actual player has left the screen -- scroll in that direction */
10056 if (jx != old_jx) /* player has moved horizontally */
10057 scroll_x += (jx - old_jx);
10058 else /* player has moved vertically */
10059 scroll_y += (jy - old_jy);
10063 if (jx != old_jx) /* player has moved horizontally */
10065 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10066 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10067 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10069 /* don't scroll over playfield boundaries */
10070 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10071 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10073 /* don't scroll more than one field at a time */
10074 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10076 /* don't scroll against the player's moving direction */
10077 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10078 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10079 scroll_x = old_scroll_x;
10081 else /* player has moved vertically */
10083 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10084 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10085 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10087 /* don't scroll over playfield boundaries */
10088 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10089 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10091 /* don't scroll more than one field at a time */
10092 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10094 /* don't scroll against the player's moving direction */
10095 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10096 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10097 scroll_y = old_scroll_y;
10101 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10103 if (!options.network && !AllPlayersInVisibleScreen())
10105 scroll_x = old_scroll_x;
10106 scroll_y = old_scroll_y;
10110 ScrollScreen(player, SCROLL_INIT);
10111 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10118 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10120 if (!(moved & MF_MOVING) && !player->is_pushing)
10125 player->StepFrame = 0;
10127 if (moved & MF_MOVING)
10130 printf("::: REALLY moves now\n");
10133 if (old_jx != jx && old_jy == jy)
10134 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10135 else if (old_jx == jx && old_jy != jy)
10136 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10138 DrawLevelField(jx, jy); /* for "crumbled sand" */
10140 player->last_move_dir = player->MovDir;
10141 player->is_moving = TRUE;
10143 player->is_snapping = FALSE;
10147 player->is_switching = FALSE;
10150 player->is_dropping = FALSE;
10154 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10157 if (game.engine_version < VERSION_IDENT(3,1,0,0))
10160 int move_direction = player->MovDir;
10162 int enter_side = MV_DIR_OPPOSITE(move_direction);
10163 int leave_side = move_direction;
10165 static int trigger_sides[4][2] =
10167 /* enter side leave side */
10168 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10169 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10170 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10171 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10173 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10174 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10176 int old_element = Feld[old_jx][old_jy];
10177 int new_element = Feld[jx][jy];
10180 /* !!! TEST ONLY !!! */
10181 if (IS_CUSTOM_ELEMENT(old_element))
10182 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10184 player->index_bit, leave_side);
10186 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10187 CE_OTHER_GETS_LEFT,
10188 player->index_bit, leave_side);
10190 if (IS_CUSTOM_ELEMENT(new_element))
10191 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10192 player->index_bit, enter_side);
10194 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10195 CE_OTHER_GETS_ENTERED,
10196 player->index_bit, enter_side);
10206 CheckGravityMovementWhenNotMoving(player);
10209 player->last_move_dir = MV_NO_MOVING;
10211 player->is_moving = FALSE;
10213 #if USE_NEW_MOVE_STYLE
10214 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10215 /* ensure that the player is also allowed to move in the next frame */
10216 /* (currently, the player is forced to wait eight frames before he can try
10219 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10220 player->move_delay = 0; /* allow direct movement in the next frame */
10224 #if USE_NEW_MOVE_DELAY
10225 if (player->move_delay == -1) /* not yet initialized by DigField() */
10226 player->move_delay = player->move_delay_value;
10229 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10231 TestIfHeroTouchesBadThing(jx, jy);
10232 TestIfPlayerTouchesCustomElement(jx, jy);
10235 if (!player->active)
10236 RemoveHero(player);
10241 void ScrollPlayer(struct PlayerInfo *player, int mode)
10243 int jx = player->jx, jy = player->jy;
10244 int last_jx = player->last_jx, last_jy = player->last_jy;
10245 int move_stepsize = TILEX / player->move_delay_value;
10247 if (!player->active || !player->MovPos)
10250 if (mode == SCROLL_INIT)
10252 player->actual_frame_counter = FrameCounter;
10253 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10256 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10258 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10259 player->block_delay);
10262 #if USE_NEW_BLOCK_STYLE
10265 if (player->block_delay <= 0)
10266 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10269 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10270 Feld[last_jx][last_jy] == EL_EMPTY)
10272 int last_field_block_delay = 0; /* start with no blocking at all */
10273 int block_delay_adjustment = player->block_delay_adjustment;
10275 /* if player blocks last field, add delay for exactly one move */
10276 if (player->block_last_field)
10278 last_field_block_delay += player->move_delay_value;
10280 #if USE_GRAVITY_BUGFIX_NEW
10281 /* when blocking enabled, prevent moving up despite gravity */
10282 if (game.gravity && player->MovDir == MV_UP)
10283 block_delay_adjustment = -1;
10287 /* add block delay adjustment (also possible when not blocking) */
10288 last_field_block_delay += block_delay_adjustment;
10291 #if USE_BLOCK_DELAY_BUGFIX
10292 /* when blocking enabled, correct block delay for fast movement */
10293 if (player->block_last_field &&
10294 player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10295 last_field_block_delay =
10296 player->move_delay_value + player->block_delay_adjustment;
10301 #if USE_GRAVITY_BUGFIX_NEW
10302 /* when blocking enabled, correct block delay for gravity movement */
10303 if (player->block_last_field &&
10304 game.gravity && player->MovDir == MV_UP)
10305 last_field_block_delay = player->move_delay_value - 1;
10309 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10310 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10313 #if USE_NEW_MOVE_STYLE
10314 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10315 player->block_last_field) &&
10316 Feld[last_jx][last_jy] == EL_EMPTY)
10317 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10319 if (Feld[last_jx][last_jy] == EL_EMPTY)
10320 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10325 DrawPlayer(player);
10330 else if (!FrameReached(&player->actual_frame_counter, 1))
10333 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10334 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10336 #if USE_NEW_BLOCK_STYLE
10338 if (!player->block_last_field &&
10339 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10341 RemoveField(last_jx, last_jy);
10343 Feld[last_jx][last_jy] = EL_EMPTY;
10347 /* before DrawPlayer() to draw correct player graphic for this case */
10348 if (player->MovPos == 0)
10349 CheckGravityMovement(player);
10352 DrawPlayer(player); /* needed here only to cleanup last field */
10355 if (player->MovPos == 0) /* player reached destination field */
10358 if (player->move_delay_reset_counter > 0)
10360 player->move_delay_reset_counter--;
10362 if (player->move_delay_reset_counter == 0)
10364 /* continue with normal speed after quickly moving through gate */
10365 HALVE_PLAYER_SPEED(player);
10367 /* be able to make the next move without delay */
10368 player->move_delay = 0;
10372 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10374 /* continue with normal speed after quickly moving through gate */
10375 HALVE_PLAYER_SPEED(player);
10377 /* be able to make the next move without delay */
10378 player->move_delay = 0;
10382 #if USE_NEW_BLOCK_STYLE
10384 if (player->block_last_field &&
10385 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10387 RemoveField(last_jx, last_jy);
10389 Feld[last_jx][last_jy] = EL_EMPTY;
10393 player->last_jx = jx;
10394 player->last_jy = jy;
10396 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10397 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10398 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10400 DrawPlayer(player); /* needed here only to cleanup last field */
10401 RemoveHero(player);
10403 if (local_player->friends_still_needed == 0 ||
10404 IS_SP_ELEMENT(Feld[jx][jy]))
10405 player->LevelSolved = player->GameOver = TRUE;
10409 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10410 /* this breaks one level: "machine", level 000 */
10412 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10415 int move_direction = player->MovDir;
10417 int enter_side = MV_DIR_OPPOSITE(move_direction);
10418 int leave_side = move_direction;
10420 static int trigger_sides[4][2] =
10422 /* enter side leave side */
10423 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10424 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10425 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10426 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10428 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10429 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10431 int old_jx = last_jx;
10432 int old_jy = last_jy;
10433 int old_element = Feld[old_jx][old_jy];
10434 int new_element = Feld[jx][jy];
10437 /* !!! TEST ONLY !!! */
10438 if (IS_CUSTOM_ELEMENT(old_element))
10439 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10441 player->index_bit, leave_side);
10443 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10444 CE_OTHER_GETS_LEFT,
10445 player->index_bit, leave_side);
10447 if (IS_CUSTOM_ELEMENT(new_element))
10448 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10449 player->index_bit, enter_side);
10451 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10452 CE_OTHER_GETS_ENTERED,
10453 player->index_bit, enter_side);
10459 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10461 TestIfHeroTouchesBadThing(jx, jy);
10462 TestIfPlayerTouchesCustomElement(jx, jy);
10465 /* needed because pushed element has not yet reached its destination,
10466 so it would trigger a change event at its previous field location */
10467 if (!player->is_pushing)
10469 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10472 if (!player->active)
10473 RemoveHero(player);
10476 if (level.use_step_counter)
10486 if (TimeLeft <= 10 && setup.time_limit)
10487 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10489 DrawGameValue_Time(TimeLeft);
10491 if (!TimeLeft && setup.time_limit)
10492 for (i = 0; i < MAX_PLAYERS; i++)
10493 KillHero(&stored_player[i]);
10495 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10496 DrawGameValue_Time(TimePlayed);
10499 if (tape.single_step && tape.recording && !tape.pausing &&
10500 !player->programmed_action)
10501 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10505 void ScrollScreen(struct PlayerInfo *player, int mode)
10507 static unsigned long screen_frame_counter = 0;
10509 if (mode == SCROLL_INIT)
10511 /* set scrolling step size according to actual player's moving speed */
10512 ScrollStepSize = TILEX / player->move_delay_value;
10514 screen_frame_counter = FrameCounter;
10515 ScreenMovDir = player->MovDir;
10516 ScreenMovPos = player->MovPos;
10517 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10520 else if (!FrameReached(&screen_frame_counter, 1))
10525 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10526 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10527 redraw_mask |= REDRAW_FIELD;
10530 ScreenMovDir = MV_NO_MOVING;
10533 void TestIfPlayerTouchesCustomElement(int x, int y)
10535 static int xy[4][2] =
10542 static int trigger_sides[4][2] =
10544 /* center side border side */
10545 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10546 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10547 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10548 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10550 static int touch_dir[4] =
10552 MV_LEFT | MV_RIGHT,
10557 int center_element = Feld[x][y]; /* should always be non-moving! */
10560 for (i = 0; i < NUM_DIRECTIONS; i++)
10562 int xx = x + xy[i][0];
10563 int yy = y + xy[i][1];
10564 int center_side = trigger_sides[i][0];
10565 int border_side = trigger_sides[i][1];
10566 int border_element;
10568 if (!IN_LEV_FIELD(xx, yy))
10571 if (IS_PLAYER(x, y))
10573 struct PlayerInfo *player = PLAYERINFO(x, y);
10575 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10576 border_element = Feld[xx][yy]; /* may be moving! */
10577 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10578 border_element = Feld[xx][yy];
10579 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10580 border_element = MovingOrBlocked2Element(xx, yy);
10582 continue; /* center and border element do not touch */
10585 /* !!! TEST ONLY !!! */
10586 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10587 player->index_bit, border_side);
10588 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10589 CE_OTHER_GETS_TOUCHED,
10590 player->index_bit, border_side);
10592 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10593 CE_OTHER_GETS_TOUCHED,
10594 player->index_bit, border_side);
10595 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10596 player->index_bit, border_side);
10599 else if (IS_PLAYER(xx, yy))
10601 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10603 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10605 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10606 continue; /* center and border element do not touch */
10610 /* !!! TEST ONLY !!! */
10611 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10612 player->index_bit, center_side);
10613 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10614 CE_OTHER_GETS_TOUCHED,
10615 player->index_bit, center_side);
10617 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10618 CE_OTHER_GETS_TOUCHED,
10619 player->index_bit, center_side);
10620 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10621 player->index_bit, center_side);
10629 void TestIfElementTouchesCustomElement(int x, int y)
10631 static int xy[4][2] =
10638 static int trigger_sides[4][2] =
10640 /* center side border side */
10641 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10642 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10643 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10644 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10646 static int touch_dir[4] =
10648 MV_LEFT | MV_RIGHT,
10653 boolean change_center_element = FALSE;
10654 int center_element_change_page = 0;
10655 int center_element = Feld[x][y]; /* should always be non-moving! */
10656 int border_trigger_element = EL_UNDEFINED;
10659 for (i = 0; i < NUM_DIRECTIONS; i++)
10661 int xx = x + xy[i][0];
10662 int yy = y + xy[i][1];
10663 int center_side = trigger_sides[i][0];
10664 int border_side = trigger_sides[i][1];
10665 int border_element;
10667 if (!IN_LEV_FIELD(xx, yy))
10670 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10671 border_element = Feld[xx][yy]; /* may be moving! */
10672 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10673 border_element = Feld[xx][yy];
10674 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10675 border_element = MovingOrBlocked2Element(xx, yy);
10677 continue; /* center and border element do not touch */
10679 /* check for change of center element (but change it only once) */
10680 if (IS_CUSTOM_ELEMENT(center_element) &&
10681 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10682 !change_center_element)
10684 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10686 struct ElementChangeInfo *change =
10687 &element_info[center_element].change_page[j];
10689 if (change->can_change &&
10690 change->has_event[CE_OTHER_IS_TOUCHING] &&
10691 change->trigger_side & border_side &&
10693 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10695 change->trigger_element == border_element
10699 change_center_element = TRUE;
10700 center_element_change_page = j;
10701 border_trigger_element = border_element;
10708 /* check for change of border element */
10709 if (IS_CUSTOM_ELEMENT(border_element) &&
10710 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10712 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10714 struct ElementChangeInfo *change =
10715 &element_info[border_element].change_page[j];
10717 if (change->can_change &&
10718 change->has_event[CE_OTHER_IS_TOUCHING] &&
10719 change->trigger_side & center_side &&
10721 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10723 change->trigger_element == center_element
10728 printf("::: border_element %d, %d\n", x, y);
10731 CheckElementChangeByPage(xx, yy, border_element, center_element,
10732 CE_OTHER_IS_TOUCHING, j);
10739 if (change_center_element)
10742 printf("::: center_element %d, %d\n", x, y);
10745 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10746 CE_OTHER_IS_TOUCHING, center_element_change_page);
10750 void TestIfElementHitsCustomElement(int x, int y, int direction)
10752 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10753 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10754 int hitx = x + dx, hity = y + dy;
10755 int hitting_element = Feld[x][y];
10756 int touched_element;
10758 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10759 !IS_FREE(hitx, hity) &&
10760 (!IS_MOVING(hitx, hity) ||
10761 MovDir[hitx][hity] != direction ||
10762 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10765 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10769 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10773 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10774 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10776 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10777 CE_HITTING_SOMETHING, direction);
10779 if (IN_LEV_FIELD(hitx, hity))
10781 int opposite_direction = MV_DIR_OPPOSITE(direction);
10782 int hitting_side = direction;
10783 int touched_side = opposite_direction;
10785 int touched_element = MovingOrBlocked2Element(hitx, hity);
10788 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10789 MovDir[hitx][hity] != direction ||
10790 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10799 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10800 CE_HIT_BY_SOMETHING, opposite_direction);
10802 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10803 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10805 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10807 struct ElementChangeInfo *change =
10808 &element_info[hitting_element].change_page[i];
10810 if (change->can_change &&
10811 change->has_event[CE_OTHER_IS_HITTING] &&
10812 change->trigger_side & touched_side &&
10815 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10817 change->trigger_element == touched_element
10821 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10822 CE_OTHER_IS_HITTING, i);
10828 if (IS_CUSTOM_ELEMENT(touched_element) &&
10829 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10831 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10833 struct ElementChangeInfo *change =
10834 &element_info[touched_element].change_page[i];
10836 if (change->can_change &&
10837 change->has_event[CE_OTHER_GETS_HIT] &&
10838 change->trigger_side & hitting_side &&
10840 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10842 change->trigger_element == hitting_element
10846 CheckElementChangeByPage(hitx, hity, touched_element,
10847 hitting_element, CE_OTHER_GETS_HIT, i);
10857 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10859 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10860 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10861 int hitx = x + dx, hity = y + dy;
10862 int hitting_element = Feld[x][y];
10863 int touched_element;
10865 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10866 !IS_FREE(hitx, hity) &&
10867 (!IS_MOVING(hitx, hity) ||
10868 MovDir[hitx][hity] != direction ||
10869 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10872 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10876 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10880 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10881 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10883 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10884 EP_CAN_SMASH_EVERYTHING, direction);
10886 if (IN_LEV_FIELD(hitx, hity))
10888 int opposite_direction = MV_DIR_OPPOSITE(direction);
10889 int hitting_side = direction;
10890 int touched_side = opposite_direction;
10892 int touched_element = MovingOrBlocked2Element(hitx, hity);
10895 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10896 MovDir[hitx][hity] != direction ||
10897 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10906 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10907 CE_SMASHED_BY_SOMETHING, opposite_direction);
10909 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10910 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10912 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10914 struct ElementChangeInfo *change =
10915 &element_info[hitting_element].change_page[i];
10917 if (change->can_change &&
10918 change->has_event[CE_OTHER_IS_SMASHING] &&
10919 change->trigger_side & touched_side &&
10922 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10924 change->trigger_element == touched_element
10928 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10929 CE_OTHER_IS_SMASHING, i);
10935 if (IS_CUSTOM_ELEMENT(touched_element) &&
10936 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10938 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10940 struct ElementChangeInfo *change =
10941 &element_info[touched_element].change_page[i];
10943 if (change->can_change &&
10944 change->has_event[CE_OTHER_GETS_SMASHED] &&
10945 change->trigger_side & hitting_side &&
10947 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10949 change->trigger_element == hitting_element
10953 CheckElementChangeByPage(hitx, hity, touched_element,
10954 hitting_element, CE_OTHER_GETS_SMASHED,i);
10964 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10966 int i, kill_x = -1, kill_y = -1;
10967 int bad_element = -1;
10968 static int test_xy[4][2] =
10975 static int test_dir[4] =
10983 for (i = 0; i < NUM_DIRECTIONS; i++)
10985 int test_x, test_y, test_move_dir, test_element;
10987 test_x = good_x + test_xy[i][0];
10988 test_y = good_y + test_xy[i][1];
10990 if (!IN_LEV_FIELD(test_x, test_y))
10994 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10997 test_element = Feld[test_x][test_y];
10999 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11002 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11003 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11005 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11006 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11010 bad_element = test_element;
11016 if (kill_x != -1 || kill_y != -1)
11018 if (IS_PLAYER(good_x, good_y))
11020 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11023 if (player->shield_deadly_time_left > 0 &&
11024 !IS_INDESTRUCTIBLE(bad_element))
11025 Bang(kill_x, kill_y);
11026 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11029 if (player->shield_deadly_time_left > 0)
11030 Bang(kill_x, kill_y);
11031 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11036 Bang(good_x, good_y);
11040 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11042 int i, kill_x = -1, kill_y = -1;
11043 int bad_element = Feld[bad_x][bad_y];
11044 static int test_xy[4][2] =
11051 static int touch_dir[4] =
11053 MV_LEFT | MV_RIGHT,
11058 static int test_dir[4] =
11066 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11069 for (i = 0; i < NUM_DIRECTIONS; i++)
11071 int test_x, test_y, test_move_dir, test_element;
11073 test_x = bad_x + test_xy[i][0];
11074 test_y = bad_y + test_xy[i][1];
11075 if (!IN_LEV_FIELD(test_x, test_y))
11079 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11081 test_element = Feld[test_x][test_y];
11083 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11084 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11086 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11087 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11089 /* good thing is player or penguin that does not move away */
11090 if (IS_PLAYER(test_x, test_y))
11092 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11094 if (bad_element == EL_ROBOT && player->is_moving)
11095 continue; /* robot does not kill player if he is moving */
11097 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11099 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11100 continue; /* center and border element do not touch */
11107 else if (test_element == EL_PENGUIN)
11116 if (kill_x != -1 || kill_y != -1)
11118 if (IS_PLAYER(kill_x, kill_y))
11120 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11123 if (player->shield_deadly_time_left > 0 &&
11124 !IS_INDESTRUCTIBLE(bad_element))
11125 Bang(bad_x, bad_y);
11126 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11129 if (player->shield_deadly_time_left > 0)
11130 Bang(bad_x, bad_y);
11131 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11136 Bang(kill_x, kill_y);
11140 void TestIfHeroTouchesBadThing(int x, int y)
11142 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11145 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11147 TestIfGoodThingHitsBadThing(x, y, move_dir);
11150 void TestIfBadThingTouchesHero(int x, int y)
11152 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11155 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11157 TestIfBadThingHitsGoodThing(x, y, move_dir);
11160 void TestIfFriendTouchesBadThing(int x, int y)
11162 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11165 void TestIfBadThingTouchesFriend(int x, int y)
11167 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11170 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11172 int i, kill_x = bad_x, kill_y = bad_y;
11173 static int xy[4][2] =
11181 for (i = 0; i < NUM_DIRECTIONS; i++)
11185 x = bad_x + xy[i][0];
11186 y = bad_y + xy[i][1];
11187 if (!IN_LEV_FIELD(x, y))
11190 element = Feld[x][y];
11191 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11192 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11200 if (kill_x != bad_x || kill_y != bad_y)
11201 Bang(bad_x, bad_y);
11204 void KillHero(struct PlayerInfo *player)
11206 int jx = player->jx, jy = player->jy;
11208 if (!player->active)
11211 /* remove accessible field at the player's position */
11212 Feld[jx][jy] = EL_EMPTY;
11214 /* deactivate shield (else Bang()/Explode() would not work right) */
11215 player->shield_normal_time_left = 0;
11216 player->shield_deadly_time_left = 0;
11222 static void KillHeroUnlessEnemyProtected(int x, int y)
11224 if (!PLAYER_ENEMY_PROTECTED(x, y))
11225 KillHero(PLAYERINFO(x, y));
11228 static void KillHeroUnlessExplosionProtected(int x, int y)
11230 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11231 KillHero(PLAYERINFO(x, y));
11234 void BuryHero(struct PlayerInfo *player)
11236 int jx = player->jx, jy = player->jy;
11238 if (!player->active)
11242 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11244 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11246 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11248 player->GameOver = TRUE;
11249 RemoveHero(player);
11252 void RemoveHero(struct PlayerInfo *player)
11254 int jx = player->jx, jy = player->jy;
11255 int i, found = FALSE;
11257 player->present = FALSE;
11258 player->active = FALSE;
11260 if (!ExplodeField[jx][jy])
11261 StorePlayer[jx][jy] = 0;
11263 for (i = 0; i < MAX_PLAYERS; i++)
11264 if (stored_player[i].active)
11268 AllPlayersGone = TRUE;
11275 =============================================================================
11276 checkDiagonalPushing()
11277 -----------------------------------------------------------------------------
11278 check if diagonal input device direction results in pushing of object
11279 (by checking if the alternative direction is walkable, diggable, ...)
11280 =============================================================================
11283 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11284 int x, int y, int real_dx, int real_dy)
11286 int jx, jy, dx, dy, xx, yy;
11288 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11291 /* diagonal direction: check alternative direction */
11296 xx = jx + (dx == 0 ? real_dx : 0);
11297 yy = jy + (dy == 0 ? real_dy : 0);
11299 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11303 =============================================================================
11305 -----------------------------------------------------------------------------
11306 x, y: field next to player (non-diagonal) to try to dig to
11307 real_dx, real_dy: direction as read from input device (can be diagonal)
11308 =============================================================================
11311 int DigField(struct PlayerInfo *player,
11312 int oldx, int oldy, int x, int y,
11313 int real_dx, int real_dy, int mode)
11316 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11318 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11319 boolean player_was_pushing = player->is_pushing;
11320 int jx = oldx, jy = oldy;
11321 int dx = x - jx, dy = y - jy;
11322 int nextx = x + dx, nexty = y + dy;
11323 int move_direction = (dx == -1 ? MV_LEFT :
11324 dx == +1 ? MV_RIGHT :
11326 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11327 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11329 int dig_side = MV_DIR_OPPOSITE(move_direction);
11331 static int trigger_sides[4] =
11333 CH_SIDE_RIGHT, /* moving left */
11334 CH_SIDE_LEFT, /* moving right */
11335 CH_SIDE_BOTTOM, /* moving up */
11336 CH_SIDE_TOP, /* moving down */
11338 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11340 int old_element = Feld[jx][jy];
11343 if (is_player) /* function can also be called by EL_PENGUIN */
11345 if (player->MovPos == 0)
11347 player->is_digging = FALSE;
11348 player->is_collecting = FALSE;
11351 if (player->MovPos == 0) /* last pushing move finished */
11352 player->is_pushing = FALSE;
11354 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11356 player->is_switching = FALSE;
11357 #if USE_NEW_PUSH_DELAY
11358 player->push_delay = -1;
11360 player->push_delay = 0;
11363 return MF_NO_ACTION;
11367 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11368 return MF_NO_ACTION;
11373 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11375 if (IS_TUBE(Feld[jx][jy]) ||
11376 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11380 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11381 int tube_leave_directions[][2] =
11383 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11384 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11385 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11386 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11387 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11388 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11389 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11390 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11391 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11392 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11393 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11394 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11397 while (tube_leave_directions[i][0] != tube_element)
11400 if (tube_leave_directions[i][0] == -1) /* should not happen */
11404 if (!(tube_leave_directions[i][1] & move_direction))
11405 return MF_NO_ACTION; /* tube has no opening in this direction */
11410 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11411 old_element = Back[jx][jy];
11415 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11416 return MF_NO_ACTION; /* field has no opening in this direction */
11418 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11419 return MF_NO_ACTION; /* field has no opening in this direction */
11421 element = Feld[x][y];
11423 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11424 return MF_NO_ACTION;
11426 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11427 game.engine_version >= VERSION_IDENT(2,2,0,0))
11428 return MF_NO_ACTION;
11431 if (game.gravity && is_player && !player->is_auto_moving &&
11432 canFallDown(player) && move_direction != MV_DOWN &&
11433 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11434 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11438 if (element == EL_EMPTY_SPACE &&
11439 game.gravity && !player->is_auto_moving &&
11440 canFallDown(player) && move_direction != MV_DOWN)
11441 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11447 case EL_SP_PORT_LEFT:
11448 case EL_SP_PORT_RIGHT:
11449 case EL_SP_PORT_UP:
11450 case EL_SP_PORT_DOWN:
11451 case EL_SP_PORT_HORIZONTAL:
11452 case EL_SP_PORT_VERTICAL:
11453 case EL_SP_PORT_ANY:
11454 case EL_SP_GRAVITY_PORT_LEFT:
11455 case EL_SP_GRAVITY_PORT_RIGHT:
11456 case EL_SP_GRAVITY_PORT_UP:
11457 case EL_SP_GRAVITY_PORT_DOWN:
11459 if (!canEnterSupaplexPort(x, y, dx, dy))
11460 return MF_NO_ACTION;
11463 element != EL_SP_PORT_LEFT &&
11464 element != EL_SP_GRAVITY_PORT_LEFT &&
11465 element != EL_SP_PORT_HORIZONTAL &&
11466 element != EL_SP_PORT_ANY) ||
11468 element != EL_SP_PORT_RIGHT &&
11469 element != EL_SP_GRAVITY_PORT_RIGHT &&
11470 element != EL_SP_PORT_HORIZONTAL &&
11471 element != EL_SP_PORT_ANY) ||
11473 element != EL_SP_PORT_UP &&
11474 element != EL_SP_GRAVITY_PORT_UP &&
11475 element != EL_SP_PORT_VERTICAL &&
11476 element != EL_SP_PORT_ANY) ||
11478 element != EL_SP_PORT_DOWN &&
11479 element != EL_SP_GRAVITY_PORT_DOWN &&
11480 element != EL_SP_PORT_VERTICAL &&
11481 element != EL_SP_PORT_ANY) ||
11482 !IN_LEV_FIELD(nextx, nexty) ||
11483 !IS_FREE(nextx, nexty))
11484 return MF_NO_ACTION;
11487 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11488 element == EL_SP_GRAVITY_PORT_RIGHT ||
11489 element == EL_SP_GRAVITY_PORT_UP ||
11490 element == EL_SP_GRAVITY_PORT_DOWN)
11491 game.gravity = !game.gravity;
11493 /* automatically move to the next field with double speed */
11494 player->programmed_action = move_direction;
11496 if (player->move_delay_reset_counter == 0)
11498 player->move_delay_reset_counter = 2; /* two double speed steps */
11500 DOUBLE_PLAYER_SPEED(player);
11503 player->move_delay_reset_counter = 2;
11505 DOUBLE_PLAYER_SPEED(player);
11509 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11512 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11518 case EL_TUBE_VERTICAL:
11519 case EL_TUBE_HORIZONTAL:
11520 case EL_TUBE_VERTICAL_LEFT:
11521 case EL_TUBE_VERTICAL_RIGHT:
11522 case EL_TUBE_HORIZONTAL_UP:
11523 case EL_TUBE_HORIZONTAL_DOWN:
11524 case EL_TUBE_LEFT_UP:
11525 case EL_TUBE_LEFT_DOWN:
11526 case EL_TUBE_RIGHT_UP:
11527 case EL_TUBE_RIGHT_DOWN:
11530 int tube_enter_directions[][2] =
11532 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11533 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11534 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11535 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11536 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11537 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11538 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11539 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11540 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11541 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11542 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11543 { -1, MV_NO_MOVING }
11546 while (tube_enter_directions[i][0] != element)
11549 if (tube_enter_directions[i][0] == -1) /* should not happen */
11553 if (!(tube_enter_directions[i][1] & move_direction))
11554 return MF_NO_ACTION; /* tube has no opening in this direction */
11556 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11564 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11566 if (IS_WALKABLE(element))
11569 int sound_element = SND_ELEMENT(element);
11570 int sound_action = ACTION_WALKING;
11573 if (!ACCESS_FROM(element, opposite_direction))
11574 return MF_NO_ACTION; /* field not accessible from this direction */
11578 if (element == EL_EMPTY_SPACE &&
11579 game.gravity && !player->is_auto_moving &&
11580 canFallDown(player) && move_direction != MV_DOWN)
11581 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11584 if (IS_RND_GATE(element))
11586 if (!player->key[RND_GATE_NR(element)])
11587 return MF_NO_ACTION;
11589 else if (IS_RND_GATE_GRAY(element))
11591 if (!player->key[RND_GATE_GRAY_NR(element)])
11592 return MF_NO_ACTION;
11594 else if (element == EL_EXIT_OPEN ||
11595 element == EL_SP_EXIT_OPEN ||
11596 element == EL_SP_EXIT_OPENING)
11598 sound_action = ACTION_PASSING; /* player is passing exit */
11600 else if (element == EL_EMPTY)
11602 sound_action = ACTION_MOVING; /* nothing to walk on */
11605 /* play sound from background or player, whatever is available */
11606 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11607 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11609 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11614 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11616 else if (IS_PASSABLE(element))
11620 if (!canPassField(x, y, move_direction))
11621 return MF_NO_ACTION;
11626 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11627 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11628 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11629 return MF_NO_ACTION;
11631 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11632 return MF_NO_ACTION;
11637 if (!ACCESS_FROM(element, opposite_direction))
11638 return MF_NO_ACTION; /* field not accessible from this direction */
11640 if (IS_CUSTOM_ELEMENT(element) &&
11641 !ACCESS_FROM(element, opposite_direction))
11642 return MF_NO_ACTION; /* field not accessible from this direction */
11646 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11647 return MF_NO_ACTION;
11652 if (IS_EM_GATE(element))
11654 if (!player->key[EM_GATE_NR(element)])
11655 return MF_NO_ACTION;
11657 else if (IS_EM_GATE_GRAY(element))
11659 if (!player->key[EM_GATE_GRAY_NR(element)])
11660 return MF_NO_ACTION;
11662 else if (IS_SP_PORT(element))
11664 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11665 element == EL_SP_GRAVITY_PORT_RIGHT ||
11666 element == EL_SP_GRAVITY_PORT_UP ||
11667 element == EL_SP_GRAVITY_PORT_DOWN)
11668 game.gravity = !game.gravity;
11669 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11670 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11671 element == EL_SP_GRAVITY_ON_PORT_UP ||
11672 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11673 game.gravity = TRUE;
11674 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11675 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11676 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11677 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11678 game.gravity = FALSE;
11681 /* automatically move to the next field with double speed */
11682 player->programmed_action = move_direction;
11684 if (player->move_delay_reset_counter == 0)
11686 player->move_delay_reset_counter = 2; /* two double speed steps */
11688 DOUBLE_PLAYER_SPEED(player);
11691 player->move_delay_reset_counter = 2;
11693 DOUBLE_PLAYER_SPEED(player);
11696 PlayLevelSoundAction(x, y, ACTION_PASSING);
11700 else if (IS_DIGGABLE(element))
11704 if (mode != DF_SNAP)
11707 GfxElement[x][y] = GFX_ELEMENT(element);
11710 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11712 player->is_digging = TRUE;
11715 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11717 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11718 player->index_bit, dig_side);
11721 if (mode == DF_SNAP)
11722 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11727 else if (IS_COLLECTIBLE(element))
11731 if (is_player && mode != DF_SNAP)
11733 GfxElement[x][y] = element;
11734 player->is_collecting = TRUE;
11737 if (element == EL_SPEED_PILL)
11738 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11739 else if (element == EL_EXTRA_TIME && level.time > 0)
11742 DrawGameValue_Time(TimeLeft);
11744 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11746 player->shield_normal_time_left += 10;
11747 if (element == EL_SHIELD_DEADLY)
11748 player->shield_deadly_time_left += 10;
11750 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11752 if (player->inventory_size < MAX_INVENTORY_SIZE)
11753 player->inventory_element[player->inventory_size++] = element;
11755 DrawGameValue_Dynamite(local_player->inventory_size);
11757 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11759 player->dynabomb_count++;
11760 player->dynabombs_left++;
11762 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11764 player->dynabomb_size++;
11766 else if (element == EL_DYNABOMB_INCREASE_POWER)
11768 player->dynabomb_xl = TRUE;
11770 else if (IS_KEY(element))
11772 player->key[KEY_NR(element)] = TRUE;
11774 DrawGameValue_Keys(player->key);
11776 redraw_mask |= REDRAW_DOOR_1;
11778 else if (IS_ENVELOPE(element))
11781 player->show_envelope = element;
11783 ShowEnvelope(element - EL_ENVELOPE_1);
11786 else if (IS_DROPPABLE(element) ||
11787 IS_THROWABLE(element)) /* can be collected and dropped */
11791 if (element_info[element].collect_count == 0)
11792 player->inventory_infinite_element = element;
11794 for (i = 0; i < element_info[element].collect_count; i++)
11795 if (player->inventory_size < MAX_INVENTORY_SIZE)
11796 player->inventory_element[player->inventory_size++] = element;
11798 DrawGameValue_Dynamite(local_player->inventory_size);
11800 else if (element_info[element].collect_count > 0)
11802 local_player->gems_still_needed -=
11803 element_info[element].collect_count;
11804 if (local_player->gems_still_needed < 0)
11805 local_player->gems_still_needed = 0;
11807 DrawGameValue_Emeralds(local_player->gems_still_needed);
11810 RaiseScoreElement(element);
11811 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11814 CheckTriggeredElementChangeByPlayer(x, y, element,
11815 CE_OTHER_GETS_COLLECTED,
11816 player->index_bit, dig_side);
11819 if (mode == DF_SNAP)
11820 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11825 else if (IS_PUSHABLE(element))
11827 if (mode == DF_SNAP && element != EL_BD_ROCK)
11828 return MF_NO_ACTION;
11830 if (CAN_FALL(element) && dy)
11831 return MF_NO_ACTION;
11833 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11834 !(element == EL_SPRING && level.use_spring_bug))
11835 return MF_NO_ACTION;
11838 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11839 ((move_direction & MV_VERTICAL &&
11840 ((element_info[element].move_pattern & MV_LEFT &&
11841 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11842 (element_info[element].move_pattern & MV_RIGHT &&
11843 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11844 (move_direction & MV_HORIZONTAL &&
11845 ((element_info[element].move_pattern & MV_UP &&
11846 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11847 (element_info[element].move_pattern & MV_DOWN &&
11848 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11849 return MF_NO_ACTION;
11853 /* do not push elements already moving away faster than player */
11854 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11855 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11856 return MF_NO_ACTION;
11858 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11859 return MF_NO_ACTION;
11865 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11867 if (player->push_delay_value == -1 || !player_was_pushing)
11868 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11870 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11872 if (player->push_delay_value == -1)
11873 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11876 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11878 if (player->push_delay_value == -1 || !player_was_pushing)
11879 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11882 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11884 if (!player->is_pushing)
11885 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11889 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11890 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11891 !player_is_pushing))
11892 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11895 if (!player->is_pushing &&
11896 game.engine_version >= VERSION_IDENT(2,2,0,7))
11897 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11901 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11902 player->push_delay, player->push_delay_value,
11903 FrameCounter, game.engine_version,
11904 player_was_pushing, player->is_pushing,
11905 element, element_info[element].token_name,
11906 GET_NEW_PUSH_DELAY(element));
11909 player->is_pushing = TRUE;
11911 if (!(IN_LEV_FIELD(nextx, nexty) &&
11912 (IS_FREE(nextx, nexty) ||
11913 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11914 IS_SB_ELEMENT(element)))))
11915 return MF_NO_ACTION;
11917 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11918 return MF_NO_ACTION;
11920 #if USE_NEW_PUSH_DELAY
11923 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11924 printf("::: ALERT: %d, %d [%d / %d]\n",
11925 player->push_delay, player->push_delay2,
11926 FrameCounter, FrameCounter / 50);
11929 if (player->push_delay == -1) /* new pushing; restart delay */
11930 player->push_delay = 0;
11932 if (player->push_delay == 0) /* new pushing; restart delay */
11933 player->push_delay = FrameCounter;
11936 #if USE_NEW_PUSH_DELAY
11938 if ( (player->push_delay > 0) != (!xxx_fr) )
11939 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11940 player->push_delay,
11941 xxx_pdv2, player->push_delay2, player->push_delay_value,
11942 FrameCounter, FrameCounter / 50);
11946 if (player->push_delay > 0 &&
11947 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11948 element != EL_SPRING && element != EL_BALLOON)
11951 if (player->push_delay < player->push_delay_value &&
11952 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11953 element != EL_SPRING && element != EL_BALLOON)
11957 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11958 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11959 element != EL_SPRING && element != EL_BALLOON)
11962 /* make sure that there is no move delay before next try to push */
11963 #if USE_NEW_MOVE_DELAY
11964 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11965 player->move_delay = 0;
11967 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11968 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11971 return MF_NO_ACTION;
11975 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11978 if (IS_SB_ELEMENT(element))
11980 if (element == EL_SOKOBAN_FIELD_FULL)
11982 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11983 local_player->sokobanfields_still_needed++;
11986 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11988 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11989 local_player->sokobanfields_still_needed--;
11992 Feld[x][y] = EL_SOKOBAN_OBJECT;
11994 if (Back[x][y] == Back[nextx][nexty])
11995 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11996 else if (Back[x][y] != 0)
11997 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12000 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12003 if (local_player->sokobanfields_still_needed == 0 &&
12004 game.emulation == EMU_SOKOBAN)
12006 player->LevelSolved = player->GameOver = TRUE;
12007 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12011 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12013 InitMovingField(x, y, move_direction);
12014 GfxAction[x][y] = ACTION_PUSHING;
12016 if (mode == DF_SNAP)
12017 ContinueMoving(x, y);
12019 MovPos[x][y] = (dx != 0 ? dx : dy);
12021 Pushed[x][y] = TRUE;
12022 Pushed[nextx][nexty] = TRUE;
12024 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12025 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12027 player->push_delay_value = -1; /* get new value later */
12029 #if USE_PUSH_BUGFIX
12030 /* now: check for element change _after_ element has been pushed! */
12032 if (game.use_change_when_pushing_bug)
12034 if (game.engine_version < VERSION_IDENT(3,1,0,0))
12037 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12038 player->index_bit, dig_side);
12039 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
12040 player->index_bit, dig_side);
12046 /* check for element change _after_ element has been pushed! */
12050 /* !!! TEST ONLY !!! */
12051 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12052 player->index_bit, dig_side);
12053 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12054 player->index_bit, dig_side);
12056 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
12057 player->index_bit, dig_side);
12058 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12059 player->index_bit, dig_side);
12067 else if (IS_SWITCHABLE(element))
12069 if (PLAYER_SWITCHING(player, x, y))
12071 CheckTriggeredElementChangeByPlayer(x,y, element,
12072 CE_OTHER_GETS_PRESSED,
12073 player->index_bit, dig_side);
12078 player->is_switching = TRUE;
12079 player->switch_x = x;
12080 player->switch_y = y;
12082 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12084 if (element == EL_ROBOT_WHEEL)
12086 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12090 DrawLevelField(x, y);
12092 else if (element == EL_SP_TERMINAL)
12096 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12098 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12100 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12101 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12104 else if (IS_BELT_SWITCH(element))
12106 ToggleBeltSwitch(x, y);
12108 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12109 element == EL_SWITCHGATE_SWITCH_DOWN)
12111 ToggleSwitchgateSwitch(x, y);
12113 else if (element == EL_LIGHT_SWITCH ||
12114 element == EL_LIGHT_SWITCH_ACTIVE)
12116 ToggleLightSwitch(x, y);
12119 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12120 SND_LIGHT_SWITCH_ACTIVATING :
12121 SND_LIGHT_SWITCH_DEACTIVATING);
12124 else if (element == EL_TIMEGATE_SWITCH)
12126 ActivateTimegateSwitch(x, y);
12128 else if (element == EL_BALLOON_SWITCH_LEFT ||
12129 element == EL_BALLOON_SWITCH_RIGHT ||
12130 element == EL_BALLOON_SWITCH_UP ||
12131 element == EL_BALLOON_SWITCH_DOWN ||
12132 element == EL_BALLOON_SWITCH_ANY)
12134 if (element == EL_BALLOON_SWITCH_ANY)
12135 game.balloon_dir = move_direction;
12137 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12138 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12139 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12140 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12143 else if (element == EL_LAMP)
12145 Feld[x][y] = EL_LAMP_ACTIVE;
12146 local_player->lights_still_needed--;
12148 ResetGfxAnimation(x, y);
12149 DrawLevelField(x, y);
12151 else if (element == EL_TIME_ORB_FULL)
12153 Feld[x][y] = EL_TIME_ORB_EMPTY;
12155 DrawGameValue_Time(TimeLeft);
12157 ResetGfxAnimation(x, y);
12158 DrawLevelField(x, y);
12161 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12165 CheckTriggeredElementChangeByPlayer(x, y, element,
12166 CE_OTHER_IS_SWITCHING,
12167 player->index_bit, dig_side);
12169 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12170 player->index_bit, dig_side);
12176 if (!PLAYER_SWITCHING(player, x, y))
12178 player->is_switching = TRUE;
12179 player->switch_x = x;
12180 player->switch_y = y;
12183 /* !!! TEST ONLY !!! */
12184 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12185 player->index_bit, dig_side);
12186 CheckTriggeredElementChangeByPlayer(x, y, element,
12187 CE_OTHER_IS_SWITCHING,
12188 player->index_bit, dig_side);
12190 CheckTriggeredElementChangeByPlayer(x, y, element,
12191 CE_OTHER_IS_SWITCHING,
12192 player->index_bit, dig_side);
12193 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12194 player->index_bit, dig_side);
12199 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12200 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12201 player->index_bit, dig_side);
12202 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12203 player->index_bit, dig_side);
12205 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
12206 player->index_bit, dig_side);
12207 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12208 player->index_bit, dig_side);
12212 return MF_NO_ACTION;
12215 #if USE_NEW_PUSH_DELAY
12216 player->push_delay = -1;
12218 player->push_delay = 0;
12221 #if USE_PENGUIN_COLLECT_BUGFIX
12222 if (is_player) /* function can also be called by EL_PENGUIN */
12225 if (Feld[x][y] != element) /* really digged/collected something */
12226 player->is_collecting = !player->is_digging;
12232 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12234 int jx = player->jx, jy = player->jy;
12235 int x = jx + dx, y = jy + dy;
12236 int snap_direction = (dx == -1 ? MV_LEFT :
12237 dx == +1 ? MV_RIGHT :
12239 dy == +1 ? MV_DOWN : MV_NO_MOVING);
12242 if (player->MovPos != 0)
12245 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12249 if (!player->active || !IN_LEV_FIELD(x, y))
12257 if (player->MovPos == 0)
12258 player->is_pushing = FALSE;
12260 player->is_snapping = FALSE;
12262 if (player->MovPos == 0)
12264 player->is_moving = FALSE;
12265 player->is_digging = FALSE;
12266 player->is_collecting = FALSE;
12272 if (player->is_snapping)
12275 player->MovDir = snap_direction;
12278 if (player->MovPos == 0)
12281 player->is_moving = FALSE;
12282 player->is_digging = FALSE;
12283 player->is_collecting = FALSE;
12286 player->is_dropping = FALSE;
12288 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12291 player->is_snapping = TRUE;
12294 if (player->MovPos == 0)
12297 player->is_moving = FALSE;
12298 player->is_digging = FALSE;
12299 player->is_collecting = FALSE;
12303 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12304 DrawLevelField(player->last_jx, player->last_jy);
12307 DrawLevelField(x, y);
12316 boolean DropElement(struct PlayerInfo *player)
12318 int old_element, new_element;
12319 int dropx = player->jx, dropy = player->jy;
12320 int drop_direction = player->MovDir;
12322 int drop_side = drop_direction;
12324 static int trigger_sides[4] =
12326 CH_SIDE_LEFT, /* dropping left */
12327 CH_SIDE_RIGHT, /* dropping right */
12328 CH_SIDE_TOP, /* dropping up */
12329 CH_SIDE_BOTTOM, /* dropping down */
12331 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12333 int drop_element = (player->inventory_size > 0 ?
12334 player->inventory_element[player->inventory_size - 1] :
12335 player->inventory_infinite_element != EL_UNDEFINED ?
12336 player->inventory_infinite_element :
12337 player->dynabombs_left > 0 ?
12338 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12341 if (IS_THROWABLE(drop_element))
12343 dropx += GET_DX_FROM_DIR(drop_direction);
12344 dropy += GET_DY_FROM_DIR(drop_direction);
12346 if (!IN_LEV_FIELD(dropx, dropy))
12350 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12351 new_element = drop_element; /* default: no change when dropping */
12353 /* check if player is active, not moving and ready to drop */
12354 if (!player->active || player->MovPos || player->drop_delay > 0)
12357 /* check if player has anything that can be dropped */
12359 if (new_element == EL_UNDEFINED)
12362 if (player->inventory_size == 0 &&
12363 player->inventory_infinite_element == EL_UNDEFINED &&
12364 player->dynabombs_left == 0)
12368 /* check if anything can be dropped at the current position */
12369 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12372 /* collected custom elements can only be dropped on empty fields */
12374 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12377 if (player->inventory_size > 0 &&
12378 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12379 && old_element != EL_EMPTY)
12383 if (old_element != EL_EMPTY)
12384 Back[dropx][dropy] = old_element; /* store old element on this field */
12386 ResetGfxAnimation(dropx, dropy);
12387 ResetRandomAnimationValue(dropx, dropy);
12389 if (player->inventory_size > 0 ||
12390 player->inventory_infinite_element != EL_UNDEFINED)
12392 if (player->inventory_size > 0)
12394 player->inventory_size--;
12397 new_element = player->inventory_element[player->inventory_size];
12400 DrawGameValue_Dynamite(local_player->inventory_size);
12402 if (new_element == EL_DYNAMITE)
12403 new_element = EL_DYNAMITE_ACTIVE;
12404 else if (new_element == EL_SP_DISK_RED)
12405 new_element = EL_SP_DISK_RED_ACTIVE;
12408 Feld[dropx][dropy] = new_element;
12410 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12411 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12412 el2img(Feld[dropx][dropy]), 0);
12414 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12417 /* needed if previous element just changed to "empty" in the last frame */
12418 Changed[dropx][dropy] = FALSE; /* allow another change */
12422 /* !!! TEST ONLY !!! */
12423 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12424 player->index_bit, drop_side);
12425 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12426 CE_OTHER_GETS_DROPPED,
12427 player->index_bit, drop_side);
12429 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12430 CE_OTHER_GETS_DROPPED,
12431 player->index_bit, drop_side);
12432 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12433 player->index_bit, drop_side);
12436 TestIfElementTouchesCustomElement(dropx, dropy);
12438 else /* player is dropping a dyna bomb */
12440 player->dynabombs_left--;
12443 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12446 Feld[dropx][dropy] = new_element;
12448 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12449 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12450 el2img(Feld[dropx][dropy]), 0);
12452 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12459 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12462 InitField_WithBug1(dropx, dropy, FALSE);
12464 InitField(dropx, dropy, FALSE);
12465 if (CAN_MOVE(Feld[dropx][dropy]))
12466 InitMovDir(dropx, dropy);
12470 new_element = Feld[dropx][dropy]; /* element might have changed */
12472 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12473 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12476 int move_stepsize = element_info[new_element].move_stepsize;
12478 int move_direction, nextx, nexty;
12480 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12481 MovDir[dropx][dropy] = drop_direction;
12483 move_direction = MovDir[dropx][dropy];
12484 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12485 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12488 Changed[dropx][dropy] = FALSE; /* allow another change */
12489 CheckCollision[dropx][dropy] = 2;
12492 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12495 WasJustMoving[dropx][dropy] = 3;
12498 InitMovingField(dropx, dropy, move_direction);
12499 ContinueMoving(dropx, dropy);
12504 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12507 Changed[dropx][dropy] = FALSE; /* allow another change */
12510 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12512 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12513 CE_HITTING_SOMETHING, move_direction);
12521 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12526 player->drop_delay = 8 + 8 + 8;
12530 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12535 player->is_dropping = TRUE;
12541 /* ------------------------------------------------------------------------- */
12542 /* game sound playing functions */
12543 /* ------------------------------------------------------------------------- */
12545 static int *loop_sound_frame = NULL;
12546 static int *loop_sound_volume = NULL;
12548 void InitPlayLevelSound()
12550 int num_sounds = getSoundListSize();
12552 checked_free(loop_sound_frame);
12553 checked_free(loop_sound_volume);
12555 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12556 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12559 static void PlayLevelSound(int x, int y, int nr)
12561 int sx = SCREENX(x), sy = SCREENY(y);
12562 int volume, stereo_position;
12563 int max_distance = 8;
12564 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12566 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12567 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12570 if (!IN_LEV_FIELD(x, y) ||
12571 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12572 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12575 volume = SOUND_MAX_VOLUME;
12577 if (!IN_SCR_FIELD(sx, sy))
12579 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12580 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12582 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12585 stereo_position = (SOUND_MAX_LEFT +
12586 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12587 (SCR_FIELDX + 2 * max_distance));
12589 if (IS_LOOP_SOUND(nr))
12591 /* This assures that quieter loop sounds do not overwrite louder ones,
12592 while restarting sound volume comparison with each new game frame. */
12594 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12597 loop_sound_volume[nr] = volume;
12598 loop_sound_frame[nr] = FrameCounter;
12601 PlaySoundExt(nr, volume, stereo_position, type);
12604 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12606 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12607 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12608 y < LEVELY(BY1) ? LEVELY(BY1) :
12609 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12613 static void PlayLevelSoundAction(int x, int y, int action)
12615 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12618 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12620 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12622 if (sound_effect != SND_UNDEFINED)
12623 PlayLevelSound(x, y, sound_effect);
12626 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12629 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12631 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12632 PlayLevelSound(x, y, sound_effect);
12635 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12637 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12639 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12640 PlayLevelSound(x, y, sound_effect);
12643 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12645 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12647 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12648 StopSound(sound_effect);
12651 static void PlayLevelMusic()
12653 if (levelset.music[level_nr] != MUS_UNDEFINED)
12654 PlayMusic(levelset.music[level_nr]); /* from config file */
12656 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12659 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12661 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12664 if (sample == SAMPLE_bug)
12665 printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12671 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12675 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12679 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12683 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12687 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12691 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12695 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12698 case SAMPLE_android_clone:
12699 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12702 case SAMPLE_android_move:
12703 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12706 case SAMPLE_spring:
12707 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12711 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12715 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12718 case SAMPLE_eater_eat:
12719 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12723 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12726 case SAMPLE_collect:
12727 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12730 case SAMPLE_diamond:
12731 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12734 case SAMPLE_squash:
12735 /* !!! CHECK THIS !!! */
12737 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12739 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12743 case SAMPLE_wonderfall:
12744 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12748 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12752 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12756 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12760 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12764 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12768 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12771 case SAMPLE_wonder:
12772 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12776 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12779 case SAMPLE_exit_open:
12780 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12783 case SAMPLE_exit_leave:
12784 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12787 case SAMPLE_dynamite:
12788 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12792 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12796 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12800 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12804 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12808 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12812 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12816 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12821 void RaiseScore(int value)
12823 local_player->score += value;
12825 DrawGameValue_Score(local_player->score);
12828 void RaiseScoreElement(int element)
12833 case EL_BD_DIAMOND:
12834 case EL_EMERALD_YELLOW:
12835 case EL_EMERALD_RED:
12836 case EL_EMERALD_PURPLE:
12837 case EL_SP_INFOTRON:
12838 RaiseScore(level.score[SC_EMERALD]);
12841 RaiseScore(level.score[SC_DIAMOND]);
12844 RaiseScore(level.score[SC_CRYSTAL]);
12847 RaiseScore(level.score[SC_PEARL]);
12850 case EL_BD_BUTTERFLY:
12851 case EL_SP_ELECTRON:
12852 RaiseScore(level.score[SC_BUG]);
12855 case EL_BD_FIREFLY:
12856 case EL_SP_SNIKSNAK:
12857 RaiseScore(level.score[SC_SPACESHIP]);
12860 case EL_DARK_YAMYAM:
12861 RaiseScore(level.score[SC_YAMYAM]);
12864 RaiseScore(level.score[SC_ROBOT]);
12867 RaiseScore(level.score[SC_PACMAN]);
12870 RaiseScore(level.score[SC_NUT]);
12873 case EL_SP_DISK_RED:
12874 case EL_DYNABOMB_INCREASE_NUMBER:
12875 case EL_DYNABOMB_INCREASE_SIZE:
12876 case EL_DYNABOMB_INCREASE_POWER:
12877 RaiseScore(level.score[SC_DYNAMITE]);
12879 case EL_SHIELD_NORMAL:
12880 case EL_SHIELD_DEADLY:
12881 RaiseScore(level.score[SC_SHIELD]);
12883 case EL_EXTRA_TIME:
12884 RaiseScore(level.score[SC_TIME_BONUS]);
12898 RaiseScore(level.score[SC_KEY]);
12901 RaiseScore(element_info[element].collect_score);
12906 void RequestQuitGame(boolean ask_if_really_quit)
12908 if (AllPlayersGone ||
12909 !ask_if_really_quit ||
12910 level_editor_test_game ||
12911 Request("Do you really want to quit the game ?",
12912 REQ_ASK | REQ_STAY_CLOSED))
12914 #if defined(NETWORK_AVALIABLE)
12915 if (options.network)
12916 SendToServer_StopPlaying();
12920 game_status = GAME_MODE_MAIN;
12928 if (tape.playing && tape.deactivate_display)
12929 TapeDeactivateDisplayOff(TRUE);
12932 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12935 if (tape.playing && tape.deactivate_display)
12936 TapeDeactivateDisplayOn();
12943 /* ---------- new game button stuff ---------------------------------------- */
12945 /* graphic position values for game buttons */
12946 #define GAME_BUTTON_XSIZE 30
12947 #define GAME_BUTTON_YSIZE 30
12948 #define GAME_BUTTON_XPOS 5
12949 #define GAME_BUTTON_YPOS 215
12950 #define SOUND_BUTTON_XPOS 5
12951 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12953 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12954 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12955 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12956 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12957 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12958 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12965 } gamebutton_info[NUM_GAME_BUTTONS] =
12968 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12973 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12974 GAME_CTRL_ID_PAUSE,
12978 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12983 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12984 SOUND_CTRL_ID_MUSIC,
12985 "background music on/off"
12988 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12989 SOUND_CTRL_ID_LOOPS,
12990 "sound loops on/off"
12993 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12994 SOUND_CTRL_ID_SIMPLE,
12995 "normal sounds on/off"
12999 void CreateGameButtons()
13003 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13005 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13006 struct GadgetInfo *gi;
13009 unsigned long event_mask;
13010 int gd_xoffset, gd_yoffset;
13011 int gd_x1, gd_x2, gd_y1, gd_y2;
13014 gd_xoffset = gamebutton_info[i].x;
13015 gd_yoffset = gamebutton_info[i].y;
13016 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13017 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13019 if (id == GAME_CTRL_ID_STOP ||
13020 id == GAME_CTRL_ID_PAUSE ||
13021 id == GAME_CTRL_ID_PLAY)
13023 button_type = GD_TYPE_NORMAL_BUTTON;
13025 event_mask = GD_EVENT_RELEASED;
13026 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13027 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13031 button_type = GD_TYPE_CHECK_BUTTON;
13033 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13034 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13035 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13036 event_mask = GD_EVENT_PRESSED;
13037 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13038 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13041 gi = CreateGadget(GDI_CUSTOM_ID, id,
13042 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13043 GDI_X, DX + gd_xoffset,
13044 GDI_Y, DY + gd_yoffset,
13045 GDI_WIDTH, GAME_BUTTON_XSIZE,
13046 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13047 GDI_TYPE, button_type,
13048 GDI_STATE, GD_BUTTON_UNPRESSED,
13049 GDI_CHECKED, checked,
13050 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13051 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13052 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13053 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13054 GDI_EVENT_MASK, event_mask,
13055 GDI_CALLBACK_ACTION, HandleGameButtons,
13059 Error(ERR_EXIT, "cannot create gadget");
13061 game_gadget[id] = gi;
13065 void FreeGameButtons()
13069 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13070 FreeGadget(game_gadget[i]);
13073 static void MapGameButtons()
13077 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13078 MapGadget(game_gadget[i]);
13081 void UnmapGameButtons()
13085 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13086 UnmapGadget(game_gadget[i]);
13089 static void HandleGameButtons(struct GadgetInfo *gi)
13091 int id = gi->custom_id;
13093 if (game_status != GAME_MODE_PLAYING)
13098 case GAME_CTRL_ID_STOP:
13099 RequestQuitGame(TRUE);
13102 case GAME_CTRL_ID_PAUSE:
13103 if (options.network)
13105 #if defined(NETWORK_AVALIABLE)
13107 SendToServer_ContinuePlaying();
13109 SendToServer_PausePlaying();
13113 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13116 case GAME_CTRL_ID_PLAY:
13119 #if defined(NETWORK_AVALIABLE)
13120 if (options.network)
13121 SendToServer_ContinuePlaying();
13125 tape.pausing = FALSE;
13126 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13131 case SOUND_CTRL_ID_MUSIC:
13132 if (setup.sound_music)
13134 setup.sound_music = FALSE;
13137 else if (audio.music_available)
13139 setup.sound = setup.sound_music = TRUE;
13141 SetAudioMode(setup.sound);
13147 case SOUND_CTRL_ID_LOOPS:
13148 if (setup.sound_loops)
13149 setup.sound_loops = FALSE;
13150 else if (audio.loops_available)
13152 setup.sound = setup.sound_loops = TRUE;
13153 SetAudioMode(setup.sound);
13157 case SOUND_CTRL_ID_SIMPLE:
13158 if (setup.sound_simple)
13159 setup.sound_simple = FALSE;
13160 else if (audio.sound_available)
13162 setup.sound = setup.sound_simple = TRUE;
13163 SetAudioMode(setup.sound);