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 * 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_PUSH_BUGFIX TRUE * USE_NEW_STUFF * 1
47 /* for MovePlayer() */
48 #define MF_NO_ACTION 0
52 /* for ScrollPlayer() */
54 #define SCROLL_GO_ON 1
57 #define EX_PHASE_START 0
58 #define EX_TYPE_NONE 0
59 #define EX_TYPE_NORMAL (1 << 0)
60 #define EX_TYPE_CENTER (1 << 1)
61 #define EX_TYPE_BORDER (1 << 2)
62 #define EX_TYPE_CROSS (1 << 3)
63 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
65 /* special positions in the game control window (relative to control window) */
68 #define XX_EMERALDS 29
69 #define YY_EMERALDS 54
70 #define XX_DYNAMITE 29
71 #define YY_DYNAMITE 89
80 /* special positions in the game control window (relative to main window) */
81 #define DX_LEVEL (DX + XX_LEVEL)
82 #define DY_LEVEL (DY + YY_LEVEL)
83 #define DX_EMERALDS (DX + XX_EMERALDS)
84 #define DY_EMERALDS (DY + YY_EMERALDS)
85 #define DX_DYNAMITE (DX + XX_DYNAMITE)
86 #define DY_DYNAMITE (DY + YY_DYNAMITE)
87 #define DX_KEYS (DX + XX_KEYS)
88 #define DY_KEYS (DY + YY_KEYS)
89 #define DX_SCORE (DX + XX_SCORE)
90 #define DY_SCORE (DY + YY_SCORE)
91 #define DX_TIME1 (DX + XX_TIME1)
92 #define DX_TIME2 (DX + XX_TIME2)
93 #define DY_TIME (DY + YY_TIME)
95 /* values for initial player move delay (initial delay counter value) */
96 #define INITIAL_MOVE_DELAY_OFF -1
97 #define INITIAL_MOVE_DELAY_ON 0
99 /* values for player movement speed (which is in fact a delay value) */
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED 4
103 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
104 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
106 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
108 /* values for other actions */
109 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
111 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
112 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
114 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
116 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
117 RND(element_info[e].push_delay_random))
118 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
119 RND(element_info[e].drop_delay_random))
120 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
121 RND(element_info[e].move_delay_random))
122 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
123 (element_info[e].move_delay_random))
125 #define GET_TARGET_ELEMENT(e, ch) \
126 ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
127 (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
129 #define GET_VALID_PLAYER_ELEMENT(e) \
130 ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
132 #define CAN_GROW_INTO(e) \
133 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
135 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
136 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
140 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
141 (CAN_MOVE_INTO_ACID(e) && \
142 Feld[x][y] == EL_ACID) || \
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
146 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
147 (CAN_MOVE_INTO_ACID(e) && \
148 Feld[x][y] == EL_ACID) || \
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
152 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
154 (CAN_MOVE_INTO_ACID(e) && \
155 Feld[x][y] == EL_ACID) || \
156 (DONT_COLLIDE_WITH(e) && \
158 !PLAYER_ENEMY_PROTECTED(x, y))))
161 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
164 (DONT_COLLIDE_WITH(e) && \
166 !PLAYER_ENEMY_PROTECTED(x, y))))
169 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
170 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
173 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
174 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
176 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
177 ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
181 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
184 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
185 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
189 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
192 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
195 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
196 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
198 #define PIG_CAN_ENTER_FIELD(e, x, y) \
199 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
201 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
202 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
203 IS_FOOD_PENGUIN(Feld[x][y])))
204 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
205 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
207 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
208 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
210 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
211 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
216 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
217 (CAN_MOVE_INTO_ACID(e) && \
218 Feld[x][y] == EL_ACID) || \
219 Feld[x][y] == EL_DIAMOND))
221 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
222 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
223 (CAN_MOVE_INTO_ACID(e) && \
224 Feld[x][y] == EL_ACID) || \
225 IS_FOOD_DARK_YAMYAM(Feld[x][y])))
227 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
228 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
229 (CAN_MOVE_INTO_ACID(e) && \
230 Feld[x][y] == EL_ACID) || \
231 IS_AMOEBOID(Feld[x][y])))
233 #define PIG_CAN_ENTER_FIELD(e, x, y) \
234 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
235 (CAN_MOVE_INTO_ACID(e) && \
236 Feld[x][y] == EL_ACID) || \
237 IS_FOOD_PIG(Feld[x][y])))
239 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
240 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
241 (CAN_MOVE_INTO_ACID(e) && \
242 Feld[x][y] == EL_ACID) || \
243 IS_FOOD_PENGUIN(Feld[x][y]) || \
244 Feld[x][y] == EL_EXIT_OPEN))
246 #define DRAGON_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)))
251 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
252 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
253 (CAN_MOVE_INTO_ACID(e) && \
254 Feld[x][y] == EL_ACID) || \
257 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
258 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
259 (CAN_MOVE_INTO_ACID(e) && \
260 Feld[x][y] == EL_ACID)))
264 #define GROUP_NR(e) ((e) - EL_GROUP_START)
265 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
266 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
267 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
269 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
270 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
273 #define CE_ENTER_FIELD_COND(e, x, y) \
274 (!IS_PLAYER(x, y) && \
275 (Feld[x][y] == EL_ACID || \
276 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
278 #define CE_ENTER_FIELD_COND(e, x, y) \
279 (!IS_PLAYER(x, y) && \
280 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
284 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
289 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP 0
296 #define GAME_CTRL_ID_PAUSE 1
297 #define GAME_CTRL_ID_PLAY 2
298 #define SOUND_CTRL_ID_MUSIC 3
299 #define SOUND_CTRL_ID_LOOPS 4
300 #define SOUND_CTRL_ID_SIMPLE 5
302 #define NUM_GAME_BUTTONS 6
305 /* forward declaration for internal use */
307 static void AdvanceFrameAndPlayerCounters(int);
309 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
310 static boolean MovePlayer(struct PlayerInfo *, int, int);
311 static void ScrollPlayer(struct PlayerInfo *, int);
312 static void ScrollScreen(struct PlayerInfo *, int);
314 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
316 static void InitBeltMovement(void);
317 static void CloseAllOpenTimegates(void);
318 static void CheckGravityMovement(struct PlayerInfo *);
319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
320 static void KillHeroUnlessEnemyProtected(int, int);
321 static void KillHeroUnlessExplosionProtected(int, int);
323 static void TestIfPlayerTouchesCustomElement(int, int);
324 static void TestIfElementTouchesCustomElement(int, int);
325 static void TestIfElementHitsCustomElement(int, int, int);
327 static void TestIfElementSmashesCustomElement(int, int, int);
330 static void ChangeElement(int, int, int);
332 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
333 #define CheckTriggeredElementChange(x, y, e, ev) \
334 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
336 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
337 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
338 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
339 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
340 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
341 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
344 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
345 #define CheckElementChange(x, y, e, te, ev) \
346 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
347 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
348 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
349 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
350 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
351 #define CheckElementChangeByPage(x, y, e, te, ev, p) \
352 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
354 static void PlayLevelSound(int, int, int);
355 static void PlayLevelSoundNearest(int, int, int);
356 static void PlayLevelSoundAction(int, int, int);
357 static void PlayLevelSoundElementAction(int, int, int, int);
358 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
359 static void PlayLevelSoundActionIfLoop(int, int, int);
360 static void StopLevelSoundActionIfLoop(int, int, int);
361 static void PlayLevelMusic();
363 static void MapGameButtons();
364 static void HandleGameButtons(struct GadgetInfo *);
366 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
369 /* ------------------------------------------------------------------------- */
370 /* definition of elements that automatically change to other elements after */
371 /* a specified time, eventually calling a function when changing */
372 /* ------------------------------------------------------------------------- */
374 /* forward declaration for changer functions */
375 static void InitBuggyBase(int x, int y);
376 static void WarnBuggyBase(int x, int y);
378 static void InitTrap(int x, int y);
379 static void ActivateTrap(int x, int y);
380 static void ChangeActiveTrap(int x, int y);
382 static void InitRobotWheel(int x, int y);
383 static void RunRobotWheel(int x, int y);
384 static void StopRobotWheel(int x, int y);
386 static void InitTimegateWheel(int x, int y);
387 static void RunTimegateWheel(int x, int y);
389 struct ChangingElementInfo
394 void (*pre_change_function)(int x, int y);
395 void (*change_function)(int x, int y);
396 void (*post_change_function)(int x, int y);
399 static struct ChangingElementInfo change_delay_list[] =
450 EL_SWITCHGATE_OPENING,
458 EL_SWITCHGATE_CLOSING,
459 EL_SWITCHGATE_CLOSED,
491 EL_ACID_SPLASH_RIGHT,
500 EL_SP_BUGGY_BASE_ACTIVATING,
507 EL_SP_BUGGY_BASE_ACTIVATING,
508 EL_SP_BUGGY_BASE_ACTIVE,
515 EL_SP_BUGGY_BASE_ACTIVE,
539 EL_ROBOT_WHEEL_ACTIVE,
547 EL_TIMEGATE_SWITCH_ACTIVE,
568 int push_delay_fixed, push_delay_random;
573 { EL_BALLOON, 0, 0 },
575 { EL_SOKOBAN_OBJECT, 2, 0 },
576 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
577 { EL_SATELLITE, 2, 0 },
578 { EL_SP_DISK_YELLOW, 2, 0 },
580 { EL_UNDEFINED, 0, 0 },
588 move_stepsize_list[] =
590 { EL_AMOEBA_DROP, 2 },
591 { EL_AMOEBA_DROPPING, 2 },
592 { EL_QUICKSAND_FILLING, 1 },
593 { EL_QUICKSAND_EMPTYING, 1 },
594 { EL_MAGIC_WALL_FILLING, 2 },
595 { EL_BD_MAGIC_WALL_FILLING, 2 },
596 { EL_MAGIC_WALL_EMPTYING, 2 },
597 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
607 collect_count_list[] =
610 { EL_BD_DIAMOND, 1 },
611 { EL_EMERALD_YELLOW, 1 },
612 { EL_EMERALD_RED, 1 },
613 { EL_EMERALD_PURPLE, 1 },
615 { EL_SP_INFOTRON, 1 },
627 access_direction_list[] =
629 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
630 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
631 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
632 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
633 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
634 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
635 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
636 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
637 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
638 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
639 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
641 { EL_SP_PORT_LEFT, MV_RIGHT },
642 { EL_SP_PORT_RIGHT, MV_LEFT },
643 { EL_SP_PORT_UP, MV_DOWN },
644 { EL_SP_PORT_DOWN, MV_UP },
645 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
646 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
647 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
649 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
650 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
651 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
652 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
653 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
654 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
655 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
656 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
657 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
658 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
659 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
661 { EL_UNDEFINED, MV_NO_MOVING }
664 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
666 #define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
667 CH_EVENT_BIT(CE_DELAY))
668 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
669 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
670 IS_JUST_CHANGING(x, y))
672 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
675 void GetPlayerConfig()
677 if (!audio.sound_available)
678 setup.sound_simple = FALSE;
680 if (!audio.loops_available)
681 setup.sound_loops = FALSE;
683 if (!audio.music_available)
684 setup.sound_music = FALSE;
686 if (!video.fullscreen_available)
687 setup.fullscreen = FALSE;
689 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
691 SetAudioMode(setup.sound);
695 static int getBeltNrFromBeltElement(int element)
697 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
698 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
699 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
702 static int getBeltNrFromBeltActiveElement(int element)
704 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
705 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
706 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
709 static int getBeltNrFromBeltSwitchElement(int element)
711 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
712 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
713 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
716 static int getBeltDirNrFromBeltSwitchElement(int element)
718 static int belt_base_element[4] =
720 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
721 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
722 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
723 EL_CONVEYOR_BELT_4_SWITCH_LEFT
726 int belt_nr = getBeltNrFromBeltSwitchElement(element);
727 int belt_dir_nr = element - belt_base_element[belt_nr];
729 return (belt_dir_nr % 3);
732 static int getBeltDirFromBeltSwitchElement(int element)
734 static int belt_move_dir[3] =
741 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
743 return belt_move_dir[belt_dir_nr];
746 static void InitPlayerField(int x, int y, int element, boolean init_game)
748 if (element == EL_SP_MURPHY)
752 if (stored_player[0].present)
754 Feld[x][y] = EL_SP_MURPHY_CLONE;
760 stored_player[0].use_murphy_graphic = TRUE;
763 Feld[x][y] = EL_PLAYER_1;
769 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
770 int jx = player->jx, jy = player->jy;
772 player->present = TRUE;
774 player->block_last_field = (element == EL_SP_MURPHY ?
775 level.sp_block_last_field :
776 level.block_last_field);
778 #if USE_NEW_BLOCK_STYLE
780 player->block_delay = (player->block_last_field ?
781 (element == EL_SP_MURPHY ?
782 level.sp_block_delay :
783 level.block_delay) : 0);
785 player->block_delay = (element == EL_SP_MURPHY ?
786 (player->block_last_field ? 7 : 1) :
787 (player->block_last_field ? 7 : 1));
791 printf("::: block_last_field == %d, block_delay = %d\n",
792 player->block_last_field, player->block_delay);
796 if (!options.network || player->connected)
798 player->active = TRUE;
800 /* remove potentially duplicate players */
801 if (StorePlayer[jx][jy] == Feld[x][y])
802 StorePlayer[jx][jy] = 0;
804 StorePlayer[x][y] = Feld[x][y];
808 printf("Player %d activated.\n", player->element_nr);
809 printf("[Local player is %d and currently %s.]\n",
810 local_player->element_nr,
811 local_player->active ? "active" : "not active");
815 Feld[x][y] = EL_EMPTY;
817 player->jx = player->last_jx = x;
818 player->jy = player->last_jy = y;
822 static void InitField(int x, int y, boolean init_game)
824 int element = Feld[x][y];
833 InitPlayerField(x, y, element, init_game);
836 case EL_SOKOBAN_FIELD_PLAYER:
837 element = Feld[x][y] = EL_PLAYER_1;
838 InitField(x, y, init_game);
840 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
841 InitField(x, y, init_game);
844 case EL_SOKOBAN_FIELD_EMPTY:
845 local_player->sokobanfields_still_needed++;
849 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
850 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
851 else if (x > 0 && Feld[x-1][y] == EL_ACID)
852 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
853 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
854 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
855 else if (y > 0 && Feld[x][y-1] == EL_ACID)
856 Feld[x][y] = EL_ACID_POOL_BOTTOM;
857 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
858 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
866 case EL_SPACESHIP_RIGHT:
867 case EL_SPACESHIP_UP:
868 case EL_SPACESHIP_LEFT:
869 case EL_SPACESHIP_DOWN:
871 case EL_BD_BUTTERFLY_RIGHT:
872 case EL_BD_BUTTERFLY_UP:
873 case EL_BD_BUTTERFLY_LEFT:
874 case EL_BD_BUTTERFLY_DOWN:
875 case EL_BD_BUTTERFLY:
876 case EL_BD_FIREFLY_RIGHT:
877 case EL_BD_FIREFLY_UP:
878 case EL_BD_FIREFLY_LEFT:
879 case EL_BD_FIREFLY_DOWN:
881 case EL_PACMAN_RIGHT:
905 if (y == lev_fieldy - 1)
907 Feld[x][y] = EL_AMOEBA_GROWING;
908 Store[x][y] = EL_AMOEBA_WET;
912 case EL_DYNAMITE_ACTIVE:
913 case EL_SP_DISK_RED_ACTIVE:
914 case EL_DYNABOMB_PLAYER_1_ACTIVE:
915 case EL_DYNABOMB_PLAYER_2_ACTIVE:
916 case EL_DYNABOMB_PLAYER_3_ACTIVE:
917 case EL_DYNABOMB_PLAYER_4_ACTIVE:
922 local_player->lights_still_needed++;
926 local_player->friends_still_needed++;
931 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
936 Feld[x][y] = EL_EMPTY;
941 case EL_EM_KEY_1_FILE:
942 Feld[x][y] = EL_EM_KEY_1;
944 case EL_EM_KEY_2_FILE:
945 Feld[x][y] = EL_EM_KEY_2;
947 case EL_EM_KEY_3_FILE:
948 Feld[x][y] = EL_EM_KEY_3;
950 case EL_EM_KEY_4_FILE:
951 Feld[x][y] = EL_EM_KEY_4;
955 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
956 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
957 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
958 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
959 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
960 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
961 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
962 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
963 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
964 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
965 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
966 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
969 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
970 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
971 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
973 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
975 game.belt_dir[belt_nr] = belt_dir;
976 game.belt_dir_nr[belt_nr] = belt_dir_nr;
978 else /* more than one switch -- set it like the first switch */
980 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
985 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
987 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
990 case EL_LIGHT_SWITCH_ACTIVE:
992 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
996 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
998 else if (IS_GROUP_ELEMENT(element))
1000 struct ElementGroupInfo *group = element_info[element].group;
1001 int last_anim_random_frame = gfx.anim_random_frame;
1004 if (group->choice_mode == ANIM_RANDOM)
1005 gfx.anim_random_frame = RND(group->num_elements_resolved);
1007 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1008 group->choice_mode, 0,
1011 if (group->choice_mode == ANIM_RANDOM)
1012 gfx.anim_random_frame = last_anim_random_frame;
1014 group->choice_pos++;
1016 Feld[x][y] = group->element_resolved[element_pos];
1018 InitField(x, y, init_game);
1024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1026 InitField(x, y, init_game);
1028 /* not needed to call InitMovDir() -- already done by InitField()! */
1029 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1030 CAN_MOVE(Feld[x][y]))
1034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1036 int old_element = Feld[x][y];
1038 InitField(x, y, init_game);
1040 /* not needed to call InitMovDir() -- already done by InitField()! */
1041 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1042 CAN_MOVE(old_element) &&
1043 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1046 /* this case is in fact a combination of not less than three bugs:
1047 first, it calls InitMovDir() for elements that can move, although this is
1048 already done by InitField(); then, it checks the element that was at this
1049 field _before_ the call to InitField() (which can change it); lastly, it
1050 was not called for "mole with direction" elements, which were treated as
1051 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1055 inline void DrawGameValue_Emeralds(int value)
1057 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1060 inline void DrawGameValue_Dynamite(int value)
1062 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1065 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1069 for (i = 0; i < MAX_KEYS; i++)
1071 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1072 el2edimg(EL_KEY_1 + i));
1075 inline void DrawGameValue_Score(int value)
1077 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1080 inline void DrawGameValue_Time(int value)
1083 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1085 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1088 inline void DrawGameValue_Level(int value)
1091 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1094 /* misuse area for displaying emeralds to draw bigger level number */
1095 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1096 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1098 /* now copy it to the area for displaying level number */
1099 BlitBitmap(drawto, drawto,
1100 DX_EMERALDS, DY_EMERALDS + 1,
1101 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1102 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1103 DX_LEVEL - 1, DY_LEVEL + 1);
1105 /* restore the area for displaying emeralds */
1106 DrawGameValue_Emeralds(local_player->gems_still_needed);
1108 /* yes, this is all really ugly :-) */
1112 void DrawGameDoorValues()
1116 DrawGameValue_Level(level_nr);
1118 for (i = 0; i < MAX_PLAYERS; i++)
1119 DrawGameValue_Keys(&stored_player[i]);
1121 DrawGameValue_Emeralds(local_player->gems_still_needed);
1122 DrawGameValue_Dynamite(local_player->inventory_size);
1123 DrawGameValue_Score(local_player->score);
1124 DrawGameValue_Time(TimeLeft);
1127 void DrawGameDoorValues_EM(int emeralds, int dynamite, int score, int time)
1129 DrawGameValue_Emeralds(emeralds);
1130 DrawGameValue_Dynamite(dynamite);
1131 DrawGameValue_Score(score);
1132 DrawGameValue_Time(time);
1135 static void resolve_group_element(int group_element, int recursion_depth)
1137 static int group_nr;
1138 static struct ElementGroupInfo *group;
1139 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1142 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1144 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1145 group_element - EL_GROUP_START + 1);
1147 /* replace element which caused too deep recursion by question mark */
1148 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1153 if (recursion_depth == 0) /* initialization */
1155 group = element_info[group_element].group;
1156 group_nr = group_element - EL_GROUP_START;
1158 group->num_elements_resolved = 0;
1159 group->choice_pos = 0;
1162 for (i = 0; i < actual_group->num_elements; i++)
1164 int element = actual_group->element[i];
1166 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1169 if (IS_GROUP_ELEMENT(element))
1170 resolve_group_element(element, recursion_depth + 1);
1173 group->element_resolved[group->num_elements_resolved++] = element;
1174 element_info[element].in_group[group_nr] = TRUE;
1179 if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1181 printf("::: group %d: %d resolved elements\n",
1182 group_element - EL_GROUP_START, group->num_elements_resolved);
1183 for (i = 0; i < group->num_elements_resolved; i++)
1184 printf("::: - %d ['%s']\n", group->element_resolved[i],
1185 element_info[group->element_resolved[i]].token_name);
1192 =============================================================================
1194 -----------------------------------------------------------------------------
1195 initialize game engine due to level / tape version number
1196 =============================================================================
1199 static void InitGameEngine()
1203 /* set game engine from tape file when re-playing, else from level file */
1204 game.engine_version = (tape.playing ? tape.engine_version :
1205 level.game_version);
1207 /* ---------------------------------------------------------------------- */
1208 /* set flags for bugs and changes according to active game engine version */
1209 /* ---------------------------------------------------------------------- */
1213 Before 3.1.0, custom elements that "change when pushing" changed directly
1214 after the player started pushing them (until then handled in "DigField()").
1215 Since 3.1.0, these custom elements are not changed until the "pushing"
1216 move of the element is finished (now handled in "ContinueMoving()").
1218 Affected levels/tapes:
1219 The first condition is generally needed for all levels/tapes before version
1220 3.1.0, which might use the old behaviour before it was changed; known tapes
1221 that are affected are some tapes from the level set "Walpurgis Gardens" by
1223 The second condition is an exception from the above case and is needed for
1224 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1225 above (including some development versions of 3.1.0), but before it was
1226 known that this change would break tapes like the above and was fixed in
1227 3.1.1, so that the changed behaviour was active although the engine version
1228 while recording maybe was before 3.1.0. There is at least one tape that is
1229 affected by this exception, which is the tape for the one-level set "Bug
1230 Machine" by Juergen Bonhagen.
1233 game.use_bug_change_when_pushing =
1234 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1236 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1237 tape.game_version < VERSION_IDENT(3,1,1,0)));
1239 /* ---------------------------------------------------------------------- */
1241 /* dynamically adjust element properties according to game engine version */
1242 InitElementPropertiesEngine(game.engine_version);
1245 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1246 printf(" tape version == %06d [%s] [file: %06d]\n",
1247 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1249 printf(" => game.engine_version == %06d\n", game.engine_version);
1252 /* ---------- recursively resolve group elements ------------------------- */
1254 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1255 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1256 element_info[i].in_group[j] = FALSE;
1258 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1259 resolve_group_element(EL_GROUP_START + i, 0);
1261 /* ---------- initialize player's initial move delay --------------------- */
1263 #if USE_NEW_MOVE_DELAY
1264 /* dynamically adjust player properties according to level information */
1265 game.initial_move_delay_value =
1266 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1268 /* dynamically adjust player properties according to game engine version */
1269 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1270 game.initial_move_delay_value : 0);
1272 /* dynamically adjust player properties according to game engine version */
1273 game.initial_move_delay =
1274 (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1275 INITIAL_MOVE_DELAY_OFF);
1277 /* dynamically adjust player properties according to level information */
1278 game.initial_move_delay_value =
1279 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1282 /* ---------- initialize player's initial push delay --------------------- */
1284 /* dynamically adjust player properties according to game engine version */
1285 game.initial_push_delay_value =
1286 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1288 /* ---------- initialize changing elements ------------------------------- */
1290 /* initialize changing elements information */
1291 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1293 struct ElementInfo *ei = &element_info[i];
1295 /* this pointer might have been changed in the level editor */
1296 ei->change = &ei->change_page[0];
1298 if (!IS_CUSTOM_ELEMENT(i))
1300 ei->change->target_element = EL_EMPTY_SPACE;
1301 ei->change->delay_fixed = 0;
1302 ei->change->delay_random = 0;
1303 ei->change->delay_frames = 1;
1306 ei->change_events = CE_BITMASK_DEFAULT;
1307 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1309 ei->event_page_nr[j] = 0;
1310 ei->event_page[j] = &ei->change_page[0];
1314 /* add changing elements from pre-defined list */
1315 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1317 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1318 struct ElementInfo *ei = &element_info[ch_delay->element];
1320 ei->change->target_element = ch_delay->target_element;
1321 ei->change->delay_fixed = ch_delay->change_delay;
1323 ei->change->pre_change_function = ch_delay->pre_change_function;
1324 ei->change->change_function = ch_delay->change_function;
1325 ei->change->post_change_function = ch_delay->post_change_function;
1327 ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1330 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1335 /* add change events from custom element configuration */
1336 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1338 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1340 for (j = 0; j < ei->num_change_pages; j++)
1342 if (!ei->change_page[j].can_change)
1345 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1347 /* only add event page for the first page found with this event */
1348 if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1349 !(ei->change_events & CH_EVENT_BIT(k)))
1351 ei->change_events |= CH_EVENT_BIT(k);
1352 ei->event_page_nr[k] = j;
1353 ei->event_page[k] = &ei->change_page[j];
1361 /* add change events from custom element configuration */
1362 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1364 int element = EL_CUSTOM_START + i;
1366 /* only add custom elements that change after fixed/random frame delay */
1367 if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1368 element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1372 /* ---------- initialize run-time trigger player and element ------------- */
1374 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1376 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1378 for (j = 0; j < ei->num_change_pages; j++)
1380 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1381 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1385 /* ---------- initialize trigger events ---------------------------------- */
1387 /* initialize trigger events information */
1388 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1389 trigger_events[i] = EP_BITMASK_DEFAULT;
1392 /* add trigger events from element change event properties */
1393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1395 struct ElementInfo *ei = &element_info[i];
1397 for (j = 0; j < ei->num_change_pages; j++)
1399 if (!ei->change_page[j].can_change)
1402 if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1404 int trigger_element = ei->change_page[j].trigger_element;
1406 if (IS_GROUP_ELEMENT(trigger_element))
1408 struct ElementGroupInfo *group = element_info[trigger_element].group;
1410 for (k = 0; k < group->num_elements_resolved; k++)
1411 trigger_events[group->element_resolved[k]]
1412 |= ei->change_page[j].events;
1415 trigger_events[trigger_element] |= ei->change_page[j].events;
1420 /* add trigger events from element change event properties */
1421 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1422 if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1423 trigger_events[element_info[i].change->trigger_element] |=
1424 element_info[i].change->events;
1427 /* ---------- initialize push delay -------------------------------------- */
1429 /* initialize push delay values to default */
1430 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1432 if (!IS_CUSTOM_ELEMENT(i))
1434 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1435 element_info[i].push_delay_random = game.default_push_delay_random;
1439 /* set push delay value for certain elements from pre-defined list */
1440 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1442 int e = push_delay_list[i].element;
1444 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1445 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1448 /* set push delay value for Supaplex elements for newer engine versions */
1449 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1453 if (IS_SP_ELEMENT(i))
1455 #if USE_NEW_MOVE_STYLE
1456 /* set SP push delay to just enough to push under a falling zonk */
1457 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1459 element_info[i].push_delay_fixed = delay;
1460 element_info[i].push_delay_random = 0;
1462 element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
1463 element_info[i].push_delay_random = 0; /* ... from falling zonk */
1469 /* ---------- initialize move stepsize ----------------------------------- */
1471 /* initialize move stepsize values to default */
1472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1473 if (!IS_CUSTOM_ELEMENT(i))
1474 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1476 /* set move stepsize value for certain elements from pre-defined list */
1477 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1479 int e = move_stepsize_list[i].element;
1481 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1485 /* ---------- initialize move dig/leave ---------------------------------- */
1487 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1489 element_info[i].can_leave_element = FALSE;
1490 element_info[i].can_leave_element_last = FALSE;
1494 /* ---------- initialize gem count --------------------------------------- */
1496 /* initialize gem count values for each element */
1497 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1498 if (!IS_CUSTOM_ELEMENT(i))
1499 element_info[i].collect_count = 0;
1501 /* add gem count values for all elements from pre-defined list */
1502 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1503 element_info[collect_count_list[i].element].collect_count =
1504 collect_count_list[i].count;
1506 /* ---------- initialize access direction -------------------------------- */
1508 /* initialize access direction values to default (access from every side) */
1509 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1510 if (!IS_CUSTOM_ELEMENT(i))
1511 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1513 /* set access direction value for certain elements from pre-defined list */
1514 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1515 element_info[access_direction_list[i].element].access_direction =
1516 access_direction_list[i].direction;
1521 =============================================================================
1523 -----------------------------------------------------------------------------
1524 initialize and start new game
1525 =============================================================================
1530 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1531 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1532 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1539 #if USE_NEW_AMOEBA_CODE
1540 printf("Using new amoeba code.\n");
1542 printf("Using old amoeba code.\n");
1547 /* don't play tapes over network */
1548 network_playing = (options.network && !tape.playing);
1550 for (i = 0; i < MAX_PLAYERS; i++)
1552 struct PlayerInfo *player = &stored_player[i];
1554 player->index_nr = i;
1555 player->index_bit = (1 << i);
1556 player->element_nr = EL_PLAYER_1 + i;
1558 player->present = FALSE;
1559 player->active = FALSE;
1562 player->effective_action = 0;
1563 player->programmed_action = 0;
1566 player->gems_still_needed = level.gems_needed;
1567 player->sokobanfields_still_needed = 0;
1568 player->lights_still_needed = 0;
1569 player->friends_still_needed = 0;
1571 for (j = 0; j < MAX_KEYS; j++)
1572 player->key[j] = FALSE;
1574 player->dynabomb_count = 0;
1575 player->dynabomb_size = 1;
1576 player->dynabombs_left = 0;
1577 player->dynabomb_xl = FALSE;
1579 player->MovDir = MV_NO_MOVING;
1582 player->GfxDir = MV_NO_MOVING;
1583 player->GfxAction = ACTION_DEFAULT;
1585 player->StepFrame = 0;
1587 player->use_murphy_graphic = FALSE;
1589 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1590 player->block_delay = -1; /* initialized in InitPlayerField() */
1592 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1594 player->actual_frame_counter = 0;
1596 player->step_counter = 0;
1598 player->last_move_dir = MV_NO_MOVING;
1600 player->is_waiting = FALSE;
1601 player->is_moving = FALSE;
1602 player->is_auto_moving = FALSE;
1603 player->is_digging = FALSE;
1604 player->is_snapping = FALSE;
1605 player->is_collecting = FALSE;
1606 player->is_pushing = FALSE;
1607 player->is_switching = FALSE;
1608 player->is_dropping = FALSE;
1610 player->is_bored = FALSE;
1611 player->is_sleeping = FALSE;
1613 player->frame_counter_bored = -1;
1614 player->frame_counter_sleeping = -1;
1616 player->anim_delay_counter = 0;
1617 player->post_delay_counter = 0;
1619 player->action_waiting = ACTION_DEFAULT;
1620 player->last_action_waiting = ACTION_DEFAULT;
1621 player->special_action_bored = ACTION_DEFAULT;
1622 player->special_action_sleeping = ACTION_DEFAULT;
1624 player->num_special_action_bored = 0;
1625 player->num_special_action_sleeping = 0;
1627 /* determine number of special actions for bored and sleeping animation */
1628 for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1630 boolean found = FALSE;
1632 for (k = 0; k < NUM_DIRECTIONS; k++)
1633 if (el_act_dir2img(player->element_nr, j, k) !=
1634 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1638 player->num_special_action_bored++;
1642 for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1644 boolean found = FALSE;
1646 for (k = 0; k < NUM_DIRECTIONS; k++)
1647 if (el_act_dir2img(player->element_nr, j, k) !=
1648 el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1652 player->num_special_action_sleeping++;
1657 player->switch_x = -1;
1658 player->switch_y = -1;
1660 player->show_envelope = 0;
1662 player->move_delay = game.initial_move_delay;
1663 player->move_delay_value = game.initial_move_delay_value;
1665 player->move_delay_reset_counter = 0;
1667 #if USE_NEW_PUSH_DELAY
1668 player->push_delay = -1; /* initialized when pushing starts */
1669 player->push_delay_value = game.initial_push_delay_value;
1671 player->push_delay = 0;
1672 player->push_delay_value = game.initial_push_delay_value;
1675 player->drop_delay = 0;
1677 player->last_jx = player->last_jy = 0;
1678 player->jx = player->jy = 0;
1680 player->shield_normal_time_left = 0;
1681 player->shield_deadly_time_left = 0;
1683 player->inventory_infinite_element = EL_UNDEFINED;
1684 player->inventory_size = 0;
1686 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1687 SnapField(player, 0, 0);
1689 player->LevelSolved = FALSE;
1690 player->GameOver = FALSE;
1693 network_player_action_received = FALSE;
1695 #if defined(NETWORK_AVALIABLE)
1696 /* initial null action */
1697 if (network_playing)
1698 SendToServer_MovePlayer(MV_NO_MOVING);
1706 TimeLeft = level.time;
1709 ScreenMovDir = MV_NO_MOVING;
1713 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1715 AllPlayersGone = FALSE;
1717 game.yamyam_content_nr = 0;
1718 game.magic_wall_active = FALSE;
1719 game.magic_wall_time_left = 0;
1720 game.light_time_left = 0;
1721 game.timegate_time_left = 0;
1722 game.switchgate_pos = 0;
1723 game.balloon_dir = MV_NO_MOVING;
1724 game.gravity = level.initial_gravity;
1725 game.explosions_delayed = TRUE;
1727 game.envelope_active = FALSE;
1729 for (i = 0; i < NUM_BELTS; i++)
1731 game.belt_dir[i] = MV_NO_MOVING;
1732 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1735 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1736 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1738 for (x = 0; x < lev_fieldx; x++)
1740 for (y = 0; y < lev_fieldy; y++)
1742 Feld[x][y] = level.field[x][y];
1743 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1744 ChangeDelay[x][y] = 0;
1745 ChangePage[x][y] = -1;
1746 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1748 WasJustMoving[x][y] = 0;
1749 WasJustFalling[x][y] = 0;
1750 CheckCollision[x][y] = 0;
1752 Pushed[x][y] = FALSE;
1754 Changed[x][y] = CE_BITMASK_DEFAULT;
1755 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1757 ExplodePhase[x][y] = 0;
1758 ExplodeDelay[x][y] = 0;
1759 ExplodeField[x][y] = EX_TYPE_NONE;
1761 RunnerVisit[x][y] = 0;
1762 PlayerVisit[x][y] = 0;
1765 GfxRandom[x][y] = INIT_GFX_RANDOM();
1766 GfxElement[x][y] = EL_UNDEFINED;
1767 GfxAction[x][y] = ACTION_DEFAULT;
1768 GfxDir[x][y] = MV_NO_MOVING;
1772 for (y = 0; y < lev_fieldy; y++)
1774 for (x = 0; x < lev_fieldx; x++)
1776 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1778 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1780 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1783 InitField(x, y, TRUE);
1789 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1790 emulate_sb ? EMU_SOKOBAN :
1791 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1793 /* initialize explosion and ignition delay */
1794 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1796 if (!IS_CUSTOM_ELEMENT(i))
1799 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1800 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1801 game.emulation == EMU_SUPAPLEX ? 3 : 2);
1802 int last_phase = (num_phase + 1) * delay;
1803 int half_phase = (num_phase / 2) * delay;
1805 element_info[i].explosion_delay = last_phase - 1;
1806 element_info[i].ignition_delay = half_phase;
1809 if (i == EL_BLACK_ORB)
1810 element_info[i].ignition_delay = 0;
1812 if (i == EL_BLACK_ORB)
1813 element_info[i].ignition_delay = 1;
1818 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
1819 element_info[i].explosion_delay = 1;
1821 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
1822 element_info[i].ignition_delay = 1;
1826 /* correct non-moving belts to start moving left */
1827 for (i = 0; i < NUM_BELTS; i++)
1828 if (game.belt_dir[i] == MV_NO_MOVING)
1829 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1831 /* check if any connected player was not found in playfield */
1832 for (i = 0; i < MAX_PLAYERS; i++)
1834 struct PlayerInfo *player = &stored_player[i];
1836 if (player->connected && !player->present)
1838 for (j = 0; j < MAX_PLAYERS; j++)
1840 struct PlayerInfo *some_player = &stored_player[j];
1841 int jx = some_player->jx, jy = some_player->jy;
1843 /* assign first free player found that is present in the playfield */
1844 if (some_player->present && !some_player->connected)
1846 player->present = TRUE;
1847 player->active = TRUE;
1849 some_player->present = FALSE;
1850 some_player->active = FALSE;
1853 player->element_nr = some_player->element_nr;
1856 #if USE_NEW_BLOCK_STYLE
1857 player->block_last_field = some_player->block_last_field;
1858 player->block_delay = some_player->block_delay;
1861 StorePlayer[jx][jy] = player->element_nr;
1862 player->jx = player->last_jx = jx;
1863 player->jy = player->last_jy = jy;
1873 /* when playing a tape, eliminate all players which do not participate */
1875 for (i = 0; i < MAX_PLAYERS; i++)
1877 if (stored_player[i].active && !tape.player_participates[i])
1879 struct PlayerInfo *player = &stored_player[i];
1880 int jx = player->jx, jy = player->jy;
1882 player->active = FALSE;
1883 StorePlayer[jx][jy] = 0;
1884 Feld[jx][jy] = EL_EMPTY;
1888 else if (!options.network && !setup.team_mode) /* && !tape.playing */
1890 /* when in single player mode, eliminate all but the first active player */
1892 for (i = 0; i < MAX_PLAYERS; i++)
1894 if (stored_player[i].active)
1896 for (j = i + 1; j < MAX_PLAYERS; j++)
1898 if (stored_player[j].active)
1900 struct PlayerInfo *player = &stored_player[j];
1901 int jx = player->jx, jy = player->jy;
1903 player->active = FALSE;
1904 player->present = FALSE;
1906 StorePlayer[jx][jy] = 0;
1907 Feld[jx][jy] = EL_EMPTY;
1914 /* when recording the game, store which players take part in the game */
1917 for (i = 0; i < MAX_PLAYERS; i++)
1918 if (stored_player[i].active)
1919 tape.player_participates[i] = TRUE;
1924 for (i = 0; i < MAX_PLAYERS; i++)
1926 struct PlayerInfo *player = &stored_player[i];
1928 printf("Player %d: present == %d, connected == %d, active == %d.\n",
1933 if (local_player == player)
1934 printf("Player %d is local player.\n", i+1);
1938 if (BorderElement == EL_EMPTY)
1941 SBX_Right = lev_fieldx - SCR_FIELDX;
1943 SBY_Lower = lev_fieldy - SCR_FIELDY;
1948 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1950 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1953 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1954 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1956 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1957 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1959 /* if local player not found, look for custom element that might create
1960 the player (make some assumptions about the right custom element) */
1961 if (!local_player->present)
1963 int start_x = 0, start_y = 0;
1964 int found_rating = 0;
1965 int found_element = EL_UNDEFINED;
1967 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1969 int element = Feld[x][y];
1974 if (!IS_CUSTOM_ELEMENT(element))
1977 if (CAN_CHANGE(element))
1979 for (i = 0; i < element_info[element].num_change_pages; i++)
1981 content = element_info[element].change_page[i].target_element;
1982 is_player = ELEM_IS_PLAYER(content);
1984 if (is_player && (found_rating < 3 || element < found_element))
1990 found_element = element;
1995 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1997 content = element_info[element].content[xx][yy];
1998 is_player = ELEM_IS_PLAYER(content);
2000 if (is_player && (found_rating < 2 || element < found_element))
2002 start_x = x + xx - 1;
2003 start_y = y + yy - 1;
2006 found_element = element;
2009 if (!CAN_CHANGE(element))
2012 for (i = 0; i < element_info[element].num_change_pages; i++)
2014 content= element_info[element].change_page[i].target_content[xx][yy];
2015 is_player = ELEM_IS_PLAYER(content);
2017 if (is_player && (found_rating < 1 || element < found_element))
2019 start_x = x + xx - 1;
2020 start_y = y + yy - 1;
2023 found_element = element;
2029 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2030 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2033 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2034 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2040 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2041 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2042 local_player->jx - MIDPOSX);
2044 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2045 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2046 local_player->jy - MIDPOSY);
2048 scroll_x = SBX_Left;
2049 scroll_y = SBY_Upper;
2050 if (local_player->jx >= SBX_Left + MIDPOSX)
2051 scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2052 local_player->jx - MIDPOSX :
2054 if (local_player->jy >= SBY_Upper + MIDPOSY)
2055 scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2056 local_player->jy - MIDPOSY :
2061 CloseDoor(DOOR_CLOSE_1);
2063 /* !!! FIX THIS (START) !!! */
2064 if (level.file_info.type == LEVEL_FILE_TYPE_EM)
2066 if (em_main_init_game(level_nr, level.file_info.filename) != 0)
2068 game_status = GAME_MODE_MAIN;
2079 /* after drawing the level, correct some elements */
2080 if (game.timegate_time_left == 0)
2081 CloseAllOpenTimegates();
2083 if (setup.soft_scrolling)
2084 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2086 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2089 /* !!! FIX THIS (END) !!! */
2091 /* copy default game door content to main double buffer */
2092 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2093 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2095 DrawGameDoorValues();
2099 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2100 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2101 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2105 /* copy actual game door content to door double buffer for OpenDoor() */
2106 BlitBitmap(drawto, bitmap_db_door,
2107 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2109 OpenDoor(DOOR_OPEN_ALL);
2111 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2113 if (setup.sound_music)
2116 KeyboardAutoRepeatOffUnlessAutoplay();
2120 for (i = 0; i < MAX_PLAYERS; i++)
2121 printf("Player %d %sactive.\n",
2122 i + 1, (stored_player[i].active ? "" : "not "));
2126 printf("::: starting game [%d]\n", FrameCounter);
2130 void InitMovDir(int x, int y)
2132 int i, element = Feld[x][y];
2133 static int xy[4][2] =
2140 static int direction[3][4] =
2142 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2143 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2144 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2153 Feld[x][y] = EL_BUG;
2154 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2157 case EL_SPACESHIP_RIGHT:
2158 case EL_SPACESHIP_UP:
2159 case EL_SPACESHIP_LEFT:
2160 case EL_SPACESHIP_DOWN:
2161 Feld[x][y] = EL_SPACESHIP;
2162 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2165 case EL_BD_BUTTERFLY_RIGHT:
2166 case EL_BD_BUTTERFLY_UP:
2167 case EL_BD_BUTTERFLY_LEFT:
2168 case EL_BD_BUTTERFLY_DOWN:
2169 Feld[x][y] = EL_BD_BUTTERFLY;
2170 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2173 case EL_BD_FIREFLY_RIGHT:
2174 case EL_BD_FIREFLY_UP:
2175 case EL_BD_FIREFLY_LEFT:
2176 case EL_BD_FIREFLY_DOWN:
2177 Feld[x][y] = EL_BD_FIREFLY;
2178 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2181 case EL_PACMAN_RIGHT:
2183 case EL_PACMAN_LEFT:
2184 case EL_PACMAN_DOWN:
2185 Feld[x][y] = EL_PACMAN;
2186 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2189 case EL_SP_SNIKSNAK:
2190 MovDir[x][y] = MV_UP;
2193 case EL_SP_ELECTRON:
2194 MovDir[x][y] = MV_LEFT;
2201 Feld[x][y] = EL_MOLE;
2202 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2206 if (IS_CUSTOM_ELEMENT(element))
2208 struct ElementInfo *ei = &element_info[element];
2209 int move_direction_initial = ei->move_direction_initial;
2210 int move_pattern = ei->move_pattern;
2212 if (move_direction_initial == MV_START_PREVIOUS)
2214 if (MovDir[x][y] != MV_NO_MOVING)
2217 move_direction_initial = MV_START_AUTOMATIC;
2220 if (move_direction_initial == MV_START_RANDOM)
2221 MovDir[x][y] = 1 << RND(4);
2222 else if (move_direction_initial & MV_ANY_DIRECTION)
2223 MovDir[x][y] = move_direction_initial;
2224 else if (move_pattern == MV_ALL_DIRECTIONS ||
2225 move_pattern == MV_TURNING_LEFT ||
2226 move_pattern == MV_TURNING_RIGHT ||
2227 move_pattern == MV_TURNING_LEFT_RIGHT ||
2228 move_pattern == MV_TURNING_RIGHT_LEFT ||
2229 move_pattern == MV_TURNING_RANDOM)
2230 MovDir[x][y] = 1 << RND(4);
2231 else if (move_pattern == MV_HORIZONTAL)
2232 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2233 else if (move_pattern == MV_VERTICAL)
2234 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2235 else if (move_pattern & MV_ANY_DIRECTION)
2236 MovDir[x][y] = element_info[element].move_pattern;
2237 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2238 move_pattern == MV_ALONG_RIGHT_SIDE)
2241 /* use random direction as default start direction */
2242 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2243 MovDir[x][y] = 1 << RND(4);
2246 for (i = 0; i < NUM_DIRECTIONS; i++)
2248 int x1 = x + xy[i][0];
2249 int y1 = y + xy[i][1];
2251 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2253 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2254 MovDir[x][y] = direction[0][i];
2256 MovDir[x][y] = direction[1][i];
2265 MovDir[x][y] = 1 << RND(4);
2267 if (element != EL_BUG &&
2268 element != EL_SPACESHIP &&
2269 element != EL_BD_BUTTERFLY &&
2270 element != EL_BD_FIREFLY)
2273 for (i = 0; i < NUM_DIRECTIONS; i++)
2275 int x1 = x + xy[i][0];
2276 int y1 = y + xy[i][1];
2278 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2280 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2282 MovDir[x][y] = direction[0][i];
2285 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2286 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2288 MovDir[x][y] = direction[1][i];
2297 GfxDir[x][y] = MovDir[x][y];
2300 void InitAmoebaNr(int x, int y)
2303 int group_nr = AmoebeNachbarNr(x, y);
2307 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2309 if (AmoebaCnt[i] == 0)
2317 AmoebaNr[x][y] = group_nr;
2318 AmoebaCnt[group_nr]++;
2319 AmoebaCnt2[group_nr]++;
2325 boolean raise_level = FALSE;
2327 if (local_player->MovPos)
2331 if (tape.auto_play) /* tape might already be stopped here */
2332 tape.auto_play_level_solved = TRUE;
2334 if (tape.playing && tape.auto_play)
2335 tape.auto_play_level_solved = TRUE;
2338 local_player->LevelSolved = FALSE;
2340 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2344 if (!tape.playing && setup.sound_loops)
2345 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2346 SND_CTRL_PLAY_LOOP);
2348 while (TimeLeft > 0)
2350 if (!tape.playing && !setup.sound_loops)
2351 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2352 if (TimeLeft > 0 && !(TimeLeft % 10))
2353 RaiseScore(level.score[SC_TIME_BONUS]);
2354 if (TimeLeft > 100 && !(TimeLeft % 10))
2359 DrawGameValue_Time(TimeLeft);
2367 if (!tape.playing && setup.sound_loops)
2368 StopSound(SND_GAME_LEVELTIME_BONUS);
2370 else if (level.time == 0) /* level without time limit */
2372 if (!tape.playing && setup.sound_loops)
2373 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2374 SND_CTRL_PLAY_LOOP);
2376 while (TimePlayed < 999)
2378 if (!tape.playing && !setup.sound_loops)
2379 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2380 if (TimePlayed < 999 && !(TimePlayed % 10))
2381 RaiseScore(level.score[SC_TIME_BONUS]);
2382 if (TimePlayed < 900 && !(TimePlayed % 10))
2387 DrawGameValue_Time(TimePlayed);
2395 if (!tape.playing && setup.sound_loops)
2396 StopSound(SND_GAME_LEVELTIME_BONUS);
2399 /* close exit door after last player */
2400 if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2401 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2403 int element = Feld[ExitX][ExitY];
2405 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2406 EL_SP_EXIT_CLOSING);
2408 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2411 /* Hero disappears */
2412 DrawLevelField(ExitX, ExitY);
2418 CloseDoor(DOOR_CLOSE_1);
2423 SaveTape(tape.level_nr); /* Ask to save tape */
2426 if (level_nr == leveldir_current->handicap_level)
2428 leveldir_current->handicap_level++;
2429 SaveLevelSetup_SeriesInfo();
2432 if (level_editor_test_game)
2433 local_player->score = -1; /* no highscore when playing from editor */
2434 else if (level_nr < leveldir_current->last_level)
2435 raise_level = TRUE; /* advance to next level */
2437 if ((hi_pos = NewHiScore()) >= 0)
2439 game_status = GAME_MODE_SCORES;
2440 DrawHallOfFame(hi_pos);
2449 game_status = GAME_MODE_MAIN;
2466 LoadScore(level_nr);
2468 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2469 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2472 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2474 if (local_player->score > highscore[k].Score)
2476 /* player has made it to the hall of fame */
2478 if (k < MAX_SCORE_ENTRIES - 1)
2480 int m = MAX_SCORE_ENTRIES - 1;
2483 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2484 if (!strcmp(setup.player_name, highscore[l].Name))
2486 if (m == k) /* player's new highscore overwrites his old one */
2490 for (l = m; l > k; l--)
2492 strcpy(highscore[l].Name, highscore[l - 1].Name);
2493 highscore[l].Score = highscore[l - 1].Score;
2500 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2501 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2502 highscore[k].Score = local_player->score;
2508 else if (!strncmp(setup.player_name, highscore[k].Name,
2509 MAX_PLAYER_NAME_LEN))
2510 break; /* player already there with a higher score */
2516 SaveScore(level_nr);
2521 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2523 if (player->GfxAction != action || player->GfxDir != dir)
2526 printf("Player frame reset! (%d => %d, %d => %d)\n",
2527 player->GfxAction, action, player->GfxDir, dir);
2530 player->GfxAction = action;
2531 player->GfxDir = dir;
2533 player->StepFrame = 0;
2537 static void ResetRandomAnimationValue(int x, int y)
2539 GfxRandom[x][y] = INIT_GFX_RANDOM();
2542 static void ResetGfxAnimation(int x, int y)
2545 GfxAction[x][y] = ACTION_DEFAULT;
2546 GfxDir[x][y] = MovDir[x][y];
2549 void InitMovingField(int x, int y, int direction)
2551 int element = Feld[x][y];
2552 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2553 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2557 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2558 ResetGfxAnimation(x, y);
2560 MovDir[newx][newy] = MovDir[x][y] = direction;
2561 GfxDir[x][y] = direction;
2563 if (Feld[newx][newy] == EL_EMPTY)
2564 Feld[newx][newy] = EL_BLOCKED;
2566 if (direction == MV_DOWN && CAN_FALL(element))
2567 GfxAction[x][y] = ACTION_FALLING;
2569 GfxAction[x][y] = ACTION_MOVING;
2571 GfxFrame[newx][newy] = GfxFrame[x][y];
2572 GfxRandom[newx][newy] = GfxRandom[x][y];
2573 GfxAction[newx][newy] = GfxAction[x][y];
2574 GfxDir[newx][newy] = GfxDir[x][y];
2577 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2579 int direction = MovDir[x][y];
2580 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2581 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2587 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2589 int oldx = x, oldy = y;
2590 int direction = MovDir[x][y];
2592 if (direction == MV_LEFT)
2594 else if (direction == MV_RIGHT)
2596 else if (direction == MV_UP)
2598 else if (direction == MV_DOWN)
2601 *comes_from_x = oldx;
2602 *comes_from_y = oldy;
2605 int MovingOrBlocked2Element(int x, int y)
2607 int element = Feld[x][y];
2609 if (element == EL_BLOCKED)
2613 Blocked2Moving(x, y, &oldx, &oldy);
2614 return Feld[oldx][oldy];
2620 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2622 /* like MovingOrBlocked2Element(), but if element is moving
2623 and (x,y) is the field the moving element is just leaving,
2624 return EL_BLOCKED instead of the element value */
2625 int element = Feld[x][y];
2627 if (IS_MOVING(x, y))
2629 if (element == EL_BLOCKED)
2633 Blocked2Moving(x, y, &oldx, &oldy);
2634 return Feld[oldx][oldy];
2643 static void RemoveField(int x, int y)
2645 Feld[x][y] = EL_EMPTY;
2652 ChangeDelay[x][y] = 0;
2653 ChangePage[x][y] = -1;
2654 Pushed[x][y] = FALSE;
2657 ExplodeField[x][y] = EX_TYPE_NONE;
2660 GfxElement[x][y] = EL_UNDEFINED;
2661 GfxAction[x][y] = ACTION_DEFAULT;
2662 GfxDir[x][y] = MV_NO_MOVING;
2665 void RemoveMovingField(int x, int y)
2667 int oldx = x, oldy = y, newx = x, newy = y;
2668 int element = Feld[x][y];
2669 int next_element = EL_UNDEFINED;
2671 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2674 if (IS_MOVING(x, y))
2676 Moving2Blocked(x, y, &newx, &newy);
2678 if (Feld[newx][newy] != EL_BLOCKED)
2681 if (Feld[newx][newy] != EL_BLOCKED)
2683 /* element is moving, but target field is not free (blocked), but
2684 already occupied by something different (example: acid pool);
2685 in this case, only remove the moving field, but not the target */
2687 RemoveField(oldx, oldy);
2689 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2691 DrawLevelField(oldx, oldy);
2697 else if (element == EL_BLOCKED)
2699 Blocked2Moving(x, y, &oldx, &oldy);
2700 if (!IS_MOVING(oldx, oldy))
2704 if (element == EL_BLOCKED &&
2705 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2706 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2707 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2708 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2709 next_element = get_next_element(Feld[oldx][oldy]);
2711 RemoveField(oldx, oldy);
2712 RemoveField(newx, newy);
2714 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2716 if (next_element != EL_UNDEFINED)
2717 Feld[oldx][oldy] = next_element;
2719 DrawLevelField(oldx, oldy);
2720 DrawLevelField(newx, newy);
2723 void DrawDynamite(int x, int y)
2725 int sx = SCREENX(x), sy = SCREENY(y);
2726 int graphic = el2img(Feld[x][y]);
2729 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2732 if (IS_WALKABLE_INSIDE(Back[x][y]))
2736 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2737 else if (Store[x][y])
2738 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2740 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2743 if (Back[x][y] || Store[x][y])
2744 DrawGraphicThruMask(sx, sy, graphic, frame);
2746 DrawGraphic(sx, sy, graphic, frame);
2748 if (game.emulation == EMU_SUPAPLEX)
2749 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2750 else if (Store[x][y])
2751 DrawGraphicThruMask(sx, sy, graphic, frame);
2753 DrawGraphic(sx, sy, graphic, frame);
2757 void CheckDynamite(int x, int y)
2759 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
2763 if (MovDelay[x][y] != 0)
2766 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2773 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2775 if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2776 Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2777 StopSound(SND_DYNAMITE_ACTIVE);
2779 StopSound(SND_DYNABOMB_ACTIVE);
2785 void DrawRelocatePlayer(struct PlayerInfo *player)
2787 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2788 boolean no_delay = (tape.warp_forward);
2789 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2790 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2791 int jx = player->jx;
2792 int jy = player->jy;
2794 if (level.instant_relocation)
2797 int offset = (setup.scroll_delay ? 3 : 0);
2799 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
2801 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2802 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2803 local_player->jx - MIDPOSX);
2805 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2806 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2807 local_player->jy - MIDPOSY);
2811 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
2812 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
2813 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
2815 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
2816 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
2817 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
2819 /* don't scroll over playfield boundaries */
2820 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2821 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2823 /* don't scroll over playfield boundaries */
2824 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2825 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2828 scroll_x += (local_player->jx - old_jx);
2829 scroll_y += (local_player->jy - old_jy);
2831 /* don't scroll over playfield boundaries */
2832 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2833 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2835 /* don't scroll over playfield boundaries */
2836 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2837 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2840 RedrawPlayfield(TRUE, 0,0,0,0);
2846 int offset = (setup.scroll_delay ? 3 : 0);
2848 int scroll_xx = -999, scroll_yy = -999;
2850 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2852 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2855 int fx = FX, fy = FY;
2857 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2858 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2859 local_player->jx - MIDPOSX);
2861 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2862 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2863 local_player->jy - MIDPOSY);
2865 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2866 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2869 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2872 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2879 fx += dx * TILEX / 2;
2880 fy += dy * TILEY / 2;
2882 ScrollLevel(dx, dy);
2885 /* scroll in two steps of half tile size to make things smoother */
2886 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2888 Delay(wait_delay_value);
2890 /* scroll second step to align at full tile size */
2892 Delay(wait_delay_value);
2895 int scroll_xx = -999, scroll_yy = -999;
2897 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
2899 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2902 int fx = FX, fy = FY;
2904 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2905 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2906 local_player->jx - MIDPOSX);
2908 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2909 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2910 local_player->jy - MIDPOSY);
2912 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2913 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2916 if (dx == 0 && dy == 0) /* no scrolling needed at all */
2919 if (scroll_xx == scroll_x && scroll_yy == scroll_y)
2926 fx += dx * TILEX / 2;
2927 fy += dy * TILEY / 2;
2929 ScrollLevel(dx, dy);
2932 /* scroll in two steps of half tile size to make things smoother */
2933 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2935 Delay(wait_delay_value);
2937 /* scroll second step to align at full tile size */
2939 Delay(wait_delay_value);
2945 Delay(wait_delay_value);
2949 void RelocatePlayer(int jx, int jy, int el_player_raw)
2952 int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
2954 int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
2956 struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
2957 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2958 boolean no_delay = (tape.warp_forward);
2959 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2960 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2961 int old_jx = player->jx;
2962 int old_jy = player->jy;
2963 int old_element = Feld[old_jx][old_jy];
2964 int element = Feld[jx][jy];
2965 boolean player_relocated = (old_jx != jx || old_jy != jy);
2967 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
2968 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
2970 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
2971 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
2972 int leave_side_horiz = move_dir_horiz;
2973 int leave_side_vert = move_dir_vert;
2975 static int trigger_sides[4][2] =
2977 /* enter side leave side */
2978 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
2979 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
2980 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
2981 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
2983 int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
2984 int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
2985 int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
2986 int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
2988 int enter_side = enter_side_horiz | enter_side_vert;
2989 int leave_side = leave_side_horiz | leave_side_vert;
2991 if (player->GameOver) /* do not reanimate dead player */
2994 if (!player_relocated) /* no need to relocate the player */
2997 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
2999 RemoveField(jx, jy); /* temporarily remove newly placed player */
3000 DrawLevelField(jx, jy);
3003 if (player->present)
3005 while (player->MovPos)
3007 ScrollPlayer(player, SCROLL_GO_ON);
3008 ScrollScreen(NULL, SCROLL_GO_ON);
3010 #if USE_NEW_MOVE_DELAY
3011 AdvanceFrameAndPlayerCounters(player->index_nr);
3019 Delay(wait_delay_value);
3022 DrawPlayer(player); /* needed here only to cleanup last field */
3023 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3025 player->is_moving = FALSE;
3029 if (IS_CUSTOM_ELEMENT(old_element))
3030 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3032 player->index_bit, leave_side);
3034 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3036 player->index_bit, leave_side);
3039 Feld[jx][jy] = el_player;
3040 InitPlayerField(jx, jy, el_player, TRUE);
3042 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3044 Feld[jx][jy] = element;
3045 InitField(jx, jy, FALSE);
3049 if (player == local_player) /* only visually relocate local player */
3050 DrawRelocatePlayer(player);
3054 TestIfHeroTouchesBadThing(jx, jy);
3055 TestIfPlayerTouchesCustomElement(jx, jy);
3059 printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3064 /* needed to allow change of walkable custom element by entering player */
3065 if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3066 Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
3068 /* needed to allow change of walkable custom element by entering player */
3069 Changed[jx][jy] = 0; /* allow another change */
3074 printf("::: player entering %d, %d from %s ...\n", jx, jy,
3075 enter_side == MV_LEFT ? "left" :
3076 enter_side == MV_RIGHT ? "right" :
3077 enter_side == MV_UP ? "top" :
3078 enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
3082 if (IS_CUSTOM_ELEMENT(element))
3083 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3084 player->index_bit, enter_side);
3086 CheckTriggeredElementChangeByPlayer(jx, jy, element,
3087 CE_OTHER_GETS_ENTERED,
3088 player->index_bit, enter_side);
3092 void Explode(int ex, int ey, int phase, int mode)
3099 /* !!! eliminate this variable !!! */
3100 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3105 int last_phase = num_phase * delay;
3106 int half_phase = (num_phase / 2) * delay;
3107 int first_phase_after_start = EX_PHASE_START + 1;
3111 if (game.explosions_delayed)
3113 ExplodeField[ex][ey] = mode;
3117 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3119 int center_element = Feld[ex][ey];
3122 printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3126 /* --- This is only really needed (and now handled) in "Impact()". --- */
3127 /* do not explode moving elements that left the explode field in time */
3128 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3129 center_element == EL_EMPTY &&
3130 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3134 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3135 PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3137 /* remove things displayed in background while burning dynamite */
3138 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3141 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3143 /* put moving element to center field (and let it explode there) */
3144 center_element = MovingOrBlocked2Element(ex, ey);
3145 RemoveMovingField(ex, ey);
3146 Feld[ex][ey] = center_element;
3152 last_phase = element_info[center_element].explosion_delay + 1;
3154 last_phase = element_info[center_element].explosion_delay;
3158 printf("::: %d -> %d\n", center_element, last_phase);
3162 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3164 int xx = x - ex + 1;
3165 int yy = y - ey + 1;
3170 if (!IN_LEV_FIELD(x, y) ||
3171 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3172 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3175 if (!IN_LEV_FIELD(x, y) ||
3176 (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3180 if (!IN_LEV_FIELD(x, y) ||
3181 ((mode != EX_TYPE_NORMAL ||
3182 center_element == EL_AMOEBA_TO_DIAMOND) &&
3183 (x != ex || y != ey)))
3187 element = Feld[x][y];
3189 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3191 element = MovingOrBlocked2Element(x, y);
3193 if (!IS_EXPLOSION_PROOF(element))
3194 RemoveMovingField(x, y);
3200 if (IS_EXPLOSION_PROOF(element))
3203 /* indestructible elements can only explode in center (but not flames) */
3205 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3206 mode == EX_TYPE_BORDER)) ||
3207 element == EL_FLAMES)
3210 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3211 element == EL_FLAMES)
3217 if ((IS_INDESTRUCTIBLE(element) &&
3218 (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3219 (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3220 element == EL_FLAMES)
3225 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3226 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3227 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3229 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3232 if (IS_ACTIVE_BOMB(element))
3234 /* re-activate things under the bomb like gate or penguin */
3236 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3239 Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3244 printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3245 element_info[Feld[x][y]].token_name,
3246 Store[x][y], Store2[x][y]);
3253 /* save walkable background elements while explosion on same tile */
3255 if (IS_INDESTRUCTIBLE(element))
3256 Back[x][y] = element;
3260 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3261 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3262 Back[x][y] = element;
3264 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3265 (x != ex || y != ey))
3266 Back[x][y] = element;
3269 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3270 Back[x][y] = element;
3274 /* ignite explodable elements reached by other explosion */
3275 if (element == EL_EXPLOSION)
3276 element = Store2[x][y];
3279 if (AmoebaNr[x][y] &&
3280 (element == EL_AMOEBA_FULL ||
3281 element == EL_BD_AMOEBA ||
3282 element == EL_AMOEBA_GROWING))
3284 AmoebaCnt[AmoebaNr[x][y]]--;
3285 AmoebaCnt2[AmoebaNr[x][y]]--;
3291 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3293 switch(StorePlayer[ex][ey])
3296 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3299 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3302 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3306 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3311 if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3312 Store[x][y] = EL_EMPTY;
3314 if (game.emulation == EMU_SUPAPLEX)
3315 Store[x][y] = EL_EMPTY;
3318 else if (center_element == EL_MOLE)
3319 Store[x][y] = EL_EMERALD_RED;
3320 else if (center_element == EL_PENGUIN)
3321 Store[x][y] = EL_EMERALD_PURPLE;
3322 else if (center_element == EL_BUG)
3323 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3324 else if (center_element == EL_BD_BUTTERFLY)
3325 Store[x][y] = EL_BD_DIAMOND;
3326 else if (center_element == EL_SP_ELECTRON)
3327 Store[x][y] = EL_SP_INFOTRON;
3328 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3329 Store[x][y] = level.amoeba_content;
3330 else if (center_element == EL_YAMYAM)
3331 Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3332 else if (IS_CUSTOM_ELEMENT(center_element) &&
3333 element_info[center_element].content[xx][yy] != EL_EMPTY)
3334 Store[x][y] = element_info[center_element].content[xx][yy];
3335 else if (element == EL_WALL_EMERALD)
3336 Store[x][y] = EL_EMERALD;
3337 else if (element == EL_WALL_DIAMOND)
3338 Store[x][y] = EL_DIAMOND;
3339 else if (element == EL_WALL_BD_DIAMOND)
3340 Store[x][y] = EL_BD_DIAMOND;
3341 else if (element == EL_WALL_EMERALD_YELLOW)
3342 Store[x][y] = EL_EMERALD_YELLOW;
3343 else if (element == EL_WALL_EMERALD_RED)
3344 Store[x][y] = EL_EMERALD_RED;
3345 else if (element == EL_WALL_EMERALD_PURPLE)
3346 Store[x][y] = EL_EMERALD_PURPLE;
3347 else if (element == EL_WALL_PEARL)
3348 Store[x][y] = EL_PEARL;
3349 else if (element == EL_WALL_CRYSTAL)
3350 Store[x][y] = EL_CRYSTAL;
3351 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3352 Store[x][y] = element_info[element].content[1][1];
3354 Store[x][y] = EL_EMPTY;
3356 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3357 center_element == EL_AMOEBA_TO_DIAMOND)
3358 Store2[x][y] = element;
3361 printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3362 element_info[Store2[x][y]].token_name);
3366 if (AmoebaNr[x][y] &&
3367 (element == EL_AMOEBA_FULL ||
3368 element == EL_BD_AMOEBA ||
3369 element == EL_AMOEBA_GROWING))
3371 AmoebaCnt[AmoebaNr[x][y]]--;
3372 AmoebaCnt2[AmoebaNr[x][y]]--;
3378 MovDir[x][y] = MovPos[x][y] = 0;
3379 GfxDir[x][y] = MovDir[x][y];
3384 Feld[x][y] = EL_EXPLOSION;
3386 GfxElement[x][y] = center_element;
3388 GfxElement[x][y] = EL_UNDEFINED;
3391 ExplodePhase[x][y] = 1;
3393 ExplodeDelay[x][y] = last_phase;
3398 GfxFrame[x][y] = 0; /* animation does not start until next frame */
3400 GfxFrame[x][y] = -1; /* animation does not start until next frame */
3407 if (center_element == EL_YAMYAM)
3408 game.yamyam_content_nr =
3409 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3412 printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3413 element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3427 GfxFrame[x][y] = 0; /* restart explosion animation */
3431 printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3435 last_phase = ExplodeDelay[x][y];
3438 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3442 /* activate this even in non-DEBUG version until cause for crash in
3443 getGraphicAnimationFrame() (see below) is found and eliminated */
3447 if (GfxElement[x][y] == EL_UNDEFINED)
3450 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3451 printf("Explode(): This should never happen!\n");
3454 GfxElement[x][y] = EL_EMPTY;
3460 border_element = Store2[x][y];
3462 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3463 border_element = StorePlayer[x][y];
3465 if (IS_PLAYER(x, y))
3466 border_element = StorePlayer[x][y];
3470 printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3471 element_info[border_element].token_name, Store2[x][y]);
3475 printf("::: phase == %d\n", phase);
3478 if (phase == element_info[border_element].ignition_delay ||
3479 phase == last_phase)
3481 boolean border_explosion = FALSE;
3485 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3486 !PLAYER_EXPLOSION_PROTECTED(x, y))
3488 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3491 if (IS_PLAYER(x, y))
3494 KillHeroUnlessExplosionProtected(x, y);
3495 border_explosion = TRUE;
3498 if (phase == last_phase)
3499 printf("::: IS_PLAYER\n");
3502 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3505 printf("::: %d,%d: %d %s\n", x, y, border_element,
3506 element_info[border_element].token_name);
3509 Feld[x][y] = Store2[x][y];
3512 border_explosion = TRUE;
3515 if (phase == last_phase)
3516 printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3519 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3521 AmoebeUmwandeln(x, y);
3523 border_explosion = TRUE;
3526 if (phase == last_phase)
3527 printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3528 element_info[border_element].explosion_delay,
3529 element_info[border_element].ignition_delay,
3535 /* if an element just explodes due to another explosion (chain-reaction),
3536 do not immediately end the new explosion when it was the last frame of
3537 the explosion (as it would be done in the following "if"-statement!) */
3538 if (border_explosion && phase == last_phase)
3545 if (phase == first_phase_after_start)
3547 int element = Store2[x][y];
3549 if (element == EL_BLACK_ORB)
3551 Feld[x][y] = Store2[x][y];
3556 else if (phase == half_phase)
3558 int element = Store2[x][y];
3560 if (IS_PLAYER(x, y))
3561 KillHeroUnlessExplosionProtected(x, y);
3562 else if (CAN_EXPLODE_BY_EXPLOSION(element))
3564 Feld[x][y] = Store2[x][y];
3568 else if (element == EL_AMOEBA_TO_DIAMOND)
3569 AmoebeUmwandeln(x, y);
3573 if (phase == last_phase)
3578 printf("::: done: phase == %d\n", phase);
3582 printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3585 element = Feld[x][y] = Store[x][y];
3586 Store[x][y] = Store2[x][y] = 0;
3587 GfxElement[x][y] = EL_UNDEFINED;
3589 /* player can escape from explosions and might therefore be still alive */
3590 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3591 element <= EL_PLAYER_IS_EXPLODING_4)
3592 Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3594 element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3595 element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3596 element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3599 /* restore probably existing indestructible background element */
3600 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3601 element = Feld[x][y] = Back[x][y];
3604 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3605 GfxDir[x][y] = MV_NO_MOVING;
3606 ChangeDelay[x][y] = 0;
3607 ChangePage[x][y] = -1;
3610 InitField_WithBug2(x, y, FALSE);
3612 InitField(x, y, FALSE);
3614 /* !!! not needed !!! */
3616 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3617 CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3620 if (CAN_MOVE(element))
3625 DrawLevelField(x, y);
3627 TestIfElementTouchesCustomElement(x, y);
3629 if (GFX_CRUMBLED(element))
3630 DrawLevelFieldCrumbledSandNeighbours(x, y);
3632 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3633 StorePlayer[x][y] = 0;
3635 if (ELEM_IS_PLAYER(element))
3636 RelocatePlayer(x, y, element);
3639 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3641 else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3645 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3647 int stored = Store[x][y];
3648 int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3649 stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3653 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3655 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3659 printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3663 printf("::: %d / %d [%d - %d]\n",
3664 GfxFrame[x][y], phase - delay, phase, delay);
3668 printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3669 element_info[GfxElement[x][y]].token_name,
3674 DrawLevelFieldCrumbledSand(x, y);
3676 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3678 DrawLevelElement(x, y, Back[x][y]);
3679 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3681 else if (IS_WALKABLE_UNDER(Back[x][y]))
3683 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3684 DrawLevelElementThruMask(x, y, Back[x][y]);
3686 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3687 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3691 void DynaExplode(int ex, int ey)
3694 int dynabomb_element = Feld[ex][ey];
3695 int dynabomb_size = 1;
3696 boolean dynabomb_xl = FALSE;
3697 struct PlayerInfo *player;
3698 static int xy[4][2] =
3706 if (IS_ACTIVE_BOMB(dynabomb_element))
3708 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3709 dynabomb_size = player->dynabomb_size;
3710 dynabomb_xl = player->dynabomb_xl;
3711 player->dynabombs_left++;
3714 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3716 for (i = 0; i < NUM_DIRECTIONS; i++)
3718 for (j = 1; j <= dynabomb_size; j++)
3720 int x = ex + j * xy[i][0];
3721 int y = ey + j * xy[i][1];
3724 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3727 element = Feld[x][y];
3729 /* do not restart explosions of fields with active bombs */
3730 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3733 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3737 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3738 !IS_DIGGABLE(element) && !dynabomb_xl)
3741 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3742 !CAN_GROW_INTO(element) && !dynabomb_xl)
3746 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3747 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3748 element != EL_SAND && !dynabomb_xl)
3755 void Bang(int x, int y)
3758 int element = MovingOrBlocked2Element(x, y);
3760 int element = Feld[x][y];
3764 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3766 if (IS_PLAYER(x, y))
3769 struct PlayerInfo *player = PLAYERINFO(x, y);
3771 element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3772 player->element_nr);
3777 PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3779 if (game.emulation == EMU_SUPAPLEX)
3780 PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3782 PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3787 if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
3795 case EL_BD_BUTTERFLY:
3798 case EL_DARK_YAMYAM:
3802 RaiseScoreElement(element);
3803 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3805 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3806 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3807 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3808 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3809 case EL_DYNABOMB_INCREASE_NUMBER:
3810 case EL_DYNABOMB_INCREASE_SIZE:
3811 case EL_DYNABOMB_INCREASE_POWER:
3816 case EL_LAMP_ACTIVE:
3818 case EL_AMOEBA_TO_DIAMOND:
3820 if (IS_PLAYER(x, y))
3821 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3823 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3827 if (element_info[element].explosion_type == EXPLODES_CROSS)
3829 if (CAN_EXPLODE_CROSS(element))
3832 Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3837 else if (element_info[element].explosion_type == EXPLODES_1X1)
3839 else if (CAN_EXPLODE_1X1(element))
3841 Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3843 Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3847 CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3850 void SplashAcid(int x, int y)
3853 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3854 (!IN_LEV_FIELD(x - 1, y - 2) ||
3855 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3856 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3858 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3859 (!IN_LEV_FIELD(x + 1, y - 2) ||
3860 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3861 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3863 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3865 /* input: position of element entering acid (obsolete) */
3867 int element = Feld[x][y];
3869 if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3872 if (element != EL_ACID_SPLASH_LEFT &&
3873 element != EL_ACID_SPLASH_RIGHT)
3875 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3877 if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3878 (!IN_LEV_FIELD(x - 1, y - 1) ||
3879 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3880 Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3882 if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3883 (!IN_LEV_FIELD(x + 1, y - 1) ||
3884 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3885 Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3890 static void InitBeltMovement()
3892 static int belt_base_element[4] =
3894 EL_CONVEYOR_BELT_1_LEFT,
3895 EL_CONVEYOR_BELT_2_LEFT,
3896 EL_CONVEYOR_BELT_3_LEFT,
3897 EL_CONVEYOR_BELT_4_LEFT
3899 static int belt_base_active_element[4] =
3901 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3902 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3903 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3904 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3909 /* set frame order for belt animation graphic according to belt direction */
3910 for (i = 0; i < NUM_BELTS; i++)
3914 for (j = 0; j < NUM_BELT_PARTS; j++)
3916 int element = belt_base_active_element[belt_nr] + j;
3917 int graphic = el2img(element);
3919 if (game.belt_dir[i] == MV_LEFT)
3920 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3922 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3926 for (y = 0; y < lev_fieldy; y++)
3928 for (x = 0; x < lev_fieldx; x++)
3930 int element = Feld[x][y];
3932 for (i = 0; i < NUM_BELTS; i++)
3934 if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3936 int e_belt_nr = getBeltNrFromBeltElement(element);
3939 if (e_belt_nr == belt_nr)
3941 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3943 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3951 static void ToggleBeltSwitch(int x, int y)
3953 static int belt_base_element[4] =
3955 EL_CONVEYOR_BELT_1_LEFT,
3956 EL_CONVEYOR_BELT_2_LEFT,
3957 EL_CONVEYOR_BELT_3_LEFT,
3958 EL_CONVEYOR_BELT_4_LEFT
3960 static int belt_base_active_element[4] =
3962 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3963 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3964 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3965 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3967 static int belt_base_switch_element[4] =
3969 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3970 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3971 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3972 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3974 static int belt_move_dir[4] =
3982 int element = Feld[x][y];
3983 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3984 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3985 int belt_dir = belt_move_dir[belt_dir_nr];
3988 if (!IS_BELT_SWITCH(element))
3991 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3992 game.belt_dir[belt_nr] = belt_dir;
3994 if (belt_dir_nr == 3)
3997 /* set frame order for belt animation graphic according to belt direction */
3998 for (i = 0; i < NUM_BELT_PARTS; i++)
4000 int element = belt_base_active_element[belt_nr] + i;
4001 int graphic = el2img(element);
4003 if (belt_dir == MV_LEFT)
4004 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4006 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4009 for (yy = 0; yy < lev_fieldy; yy++)
4011 for (xx = 0; xx < lev_fieldx; xx++)
4013 int element = Feld[xx][yy];
4015 if (IS_BELT_SWITCH(element))
4017 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4019 if (e_belt_nr == belt_nr)
4021 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4022 DrawLevelField(xx, yy);
4025 else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4027 int e_belt_nr = getBeltNrFromBeltElement(element);
4029 if (e_belt_nr == belt_nr)
4031 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4033 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4034 DrawLevelField(xx, yy);
4037 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4039 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4041 if (e_belt_nr == belt_nr)
4043 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4045 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4046 DrawLevelField(xx, yy);
4053 static void ToggleSwitchgateSwitch(int x, int y)
4057 game.switchgate_pos = !game.switchgate_pos;
4059 for (yy = 0; yy < lev_fieldy; yy++)
4061 for (xx = 0; xx < lev_fieldx; xx++)
4063 int element = Feld[xx][yy];
4065 if (element == EL_SWITCHGATE_SWITCH_UP ||
4066 element == EL_SWITCHGATE_SWITCH_DOWN)
4068 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4069 DrawLevelField(xx, yy);
4071 else if (element == EL_SWITCHGATE_OPEN ||
4072 element == EL_SWITCHGATE_OPENING)
4074 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4076 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4078 PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4081 else if (element == EL_SWITCHGATE_CLOSED ||
4082 element == EL_SWITCHGATE_CLOSING)
4084 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4086 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4088 PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4095 static int getInvisibleActiveFromInvisibleElement(int element)
4097 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4098 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4099 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4103 static int getInvisibleFromInvisibleActiveElement(int element)
4105 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4106 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4107 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4111 static void RedrawAllLightSwitchesAndInvisibleElements()
4115 for (y = 0; y < lev_fieldy; y++)
4117 for (x = 0; x < lev_fieldx; x++)
4119 int element = Feld[x][y];
4121 if (element == EL_LIGHT_SWITCH &&
4122 game.light_time_left > 0)
4124 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4125 DrawLevelField(x, y);
4127 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4128 game.light_time_left == 0)
4130 Feld[x][y] = EL_LIGHT_SWITCH;
4131 DrawLevelField(x, y);
4133 else if (element == EL_INVISIBLE_STEELWALL ||
4134 element == EL_INVISIBLE_WALL ||
4135 element == EL_INVISIBLE_SAND)
4137 if (game.light_time_left > 0)
4138 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4140 DrawLevelField(x, y);
4142 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4143 element == EL_INVISIBLE_WALL_ACTIVE ||
4144 element == EL_INVISIBLE_SAND_ACTIVE)
4146 if (game.light_time_left == 0)
4147 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4149 DrawLevelField(x, y);
4155 static void ToggleLightSwitch(int x, int y)
4157 int element = Feld[x][y];
4159 game.light_time_left =
4160 (element == EL_LIGHT_SWITCH ?
4161 level.time_light * FRAMES_PER_SECOND : 0);
4163 RedrawAllLightSwitchesAndInvisibleElements();
4166 static void ActivateTimegateSwitch(int x, int y)
4170 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4172 for (yy = 0; yy < lev_fieldy; yy++)
4174 for (xx = 0; xx < lev_fieldx; xx++)
4176 int element = Feld[xx][yy];
4178 if (element == EL_TIMEGATE_CLOSED ||
4179 element == EL_TIMEGATE_CLOSING)
4181 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4182 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4186 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4188 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4189 DrawLevelField(xx, yy);
4196 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4199 inline static int getElementMoveStepsize(int x, int y)
4201 int element = Feld[x][y];
4202 int direction = MovDir[x][y];
4203 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4204 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4205 int horiz_move = (dx != 0);
4206 int sign = (horiz_move ? dx : dy);
4207 int step = sign * element_info[element].move_stepsize;
4209 /* special values for move stepsize for spring and things on conveyor belt */
4213 if (element == EL_SPRING)
4214 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4215 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4216 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4217 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4219 if (CAN_FALL(element) &&
4220 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4221 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4222 else if (element == EL_SPRING)
4223 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4230 void Impact(int x, int y)
4232 boolean lastline = (y == lev_fieldy-1);
4233 boolean object_hit = FALSE;
4234 boolean impact = (lastline || object_hit);
4235 int element = Feld[x][y];
4236 int smashed = EL_STEELWALL;
4238 if (!lastline) /* check if element below was hit */
4240 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4243 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4244 MovDir[x][y + 1] != MV_DOWN ||
4245 MovPos[x][y + 1] <= TILEY / 2));
4248 object_hit = !IS_FREE(x, y + 1);
4251 /* do not smash moving elements that left the smashed field in time */
4252 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4253 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4257 smashed = MovingOrBlocked2Element(x, y + 1);
4259 impact = (lastline || object_hit);
4262 if (!lastline && smashed == EL_ACID) /* element falls into acid */
4264 SplashAcid(x, y + 1);
4268 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4269 /* only reset graphic animation if graphic really changes after impact */
4271 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4273 ResetGfxAnimation(x, y);
4274 DrawLevelField(x, y);
4277 if (impact && CAN_EXPLODE_IMPACT(element))
4282 else if (impact && element == EL_PEARL)
4284 ResetGfxAnimation(x, y);
4286 Feld[x][y] = EL_PEARL_BREAKING;
4287 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4290 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4292 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4297 if (impact && element == EL_AMOEBA_DROP)
4299 if (object_hit && IS_PLAYER(x, y + 1))
4300 KillHeroUnlessEnemyProtected(x, y + 1);
4301 else if (object_hit && smashed == EL_PENGUIN)
4305 Feld[x][y] = EL_AMOEBA_GROWING;
4306 Store[x][y] = EL_AMOEBA_WET;
4308 ResetRandomAnimationValue(x, y);
4313 if (object_hit) /* check which object was hit */
4315 if (CAN_PASS_MAGIC_WALL(element) &&
4316 (smashed == EL_MAGIC_WALL ||
4317 smashed == EL_BD_MAGIC_WALL))
4320 int activated_magic_wall =
4321 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4322 EL_BD_MAGIC_WALL_ACTIVE);
4324 /* activate magic wall / mill */
4325 for (yy = 0; yy < lev_fieldy; yy++)
4326 for (xx = 0; xx < lev_fieldx; xx++)
4327 if (Feld[xx][yy] == smashed)
4328 Feld[xx][yy] = activated_magic_wall;
4330 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4331 game.magic_wall_active = TRUE;
4333 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4334 SND_MAGIC_WALL_ACTIVATING :
4335 SND_BD_MAGIC_WALL_ACTIVATING));
4338 if (IS_PLAYER(x, y + 1))
4340 if (CAN_SMASH_PLAYER(element))
4342 KillHeroUnlessEnemyProtected(x, y + 1);
4346 else if (smashed == EL_PENGUIN)
4348 if (CAN_SMASH_PLAYER(element))
4354 else if (element == EL_BD_DIAMOND)
4356 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4362 else if (((element == EL_SP_INFOTRON ||
4363 element == EL_SP_ZONK) &&
4364 (smashed == EL_SP_SNIKSNAK ||
4365 smashed == EL_SP_ELECTRON ||
4366 smashed == EL_SP_DISK_ORANGE)) ||
4367 (element == EL_SP_INFOTRON &&
4368 smashed == EL_SP_DISK_YELLOW))
4374 else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4380 else if (CAN_SMASH_EVERYTHING(element))
4382 if (IS_CLASSIC_ENEMY(smashed) ||
4383 CAN_EXPLODE_SMASHED(smashed))
4388 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4390 if (smashed == EL_LAMP ||
4391 smashed == EL_LAMP_ACTIVE)
4396 else if (smashed == EL_NUT)
4398 Feld[x][y + 1] = EL_NUT_BREAKING;
4399 PlayLevelSound(x, y, SND_NUT_BREAKING);
4400 RaiseScoreElement(EL_NUT);
4403 else if (smashed == EL_PEARL)
4405 ResetGfxAnimation(x, y);
4407 Feld[x][y + 1] = EL_PEARL_BREAKING;
4408 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4411 else if (smashed == EL_DIAMOND)
4413 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4414 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4417 else if (IS_BELT_SWITCH(smashed))
4419 ToggleBeltSwitch(x, y + 1);
4421 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4422 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4424 ToggleSwitchgateSwitch(x, y + 1);
4426 else if (smashed == EL_LIGHT_SWITCH ||
4427 smashed == EL_LIGHT_SWITCH_ACTIVE)
4429 ToggleLightSwitch(x, y + 1);
4434 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4437 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4440 /* !!! TEST ONLY !!! */
4441 CheckElementChangeBySide(x, y + 1, smashed, element,
4442 CE_SWITCHED, CH_SIDE_TOP);
4443 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4444 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4446 CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4447 CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4448 CheckElementChangeBySide(x, y + 1, smashed, element,
4449 CE_SWITCHED, CH_SIDE_TOP);
4455 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4460 /* play sound of magic wall / mill */
4462 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4463 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4465 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4466 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4467 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4468 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4473 /* play sound of object that hits the ground */
4474 if (lastline || object_hit)
4475 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4478 inline static void TurnRoundExt(int x, int y)
4490 { 0, 0 }, { 0, 0 }, { 0, 0 },
4495 int left, right, back;
4499 { MV_DOWN, MV_UP, MV_RIGHT },
4500 { MV_UP, MV_DOWN, MV_LEFT },
4502 { MV_LEFT, MV_RIGHT, MV_DOWN },
4506 { MV_RIGHT, MV_LEFT, MV_UP }
4509 int element = Feld[x][y];
4510 int move_pattern = element_info[element].move_pattern;
4512 int old_move_dir = MovDir[x][y];
4513 int left_dir = turn[old_move_dir].left;
4514 int right_dir = turn[old_move_dir].right;
4515 int back_dir = turn[old_move_dir].back;
4517 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
4518 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
4519 int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
4520 int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
4522 int left_x = x + left_dx, left_y = y + left_dy;
4523 int right_x = x + right_dx, right_y = y + right_dy;
4524 int move_x = x + move_dx, move_y = y + move_dy;
4528 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4530 TestIfBadThingTouchesOtherBadThing(x, y);
4532 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4533 MovDir[x][y] = right_dir;
4534 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4535 MovDir[x][y] = left_dir;
4537 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4539 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4543 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4544 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4546 TestIfBadThingTouchesOtherBadThing(x, y);
4548 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4549 MovDir[x][y] = left_dir;
4550 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4551 MovDir[x][y] = right_dir;
4553 if ((element == EL_SPACESHIP ||
4554 element == EL_SP_SNIKSNAK ||
4555 element == EL_SP_ELECTRON)
4556 && MovDir[x][y] != old_move_dir)
4558 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4562 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4564 TestIfBadThingTouchesOtherBadThing(x, y);
4566 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4567 MovDir[x][y] = left_dir;
4568 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4569 MovDir[x][y] = right_dir;
4571 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4573 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4576 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4578 TestIfBadThingTouchesOtherBadThing(x, y);
4580 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4581 MovDir[x][y] = left_dir;
4582 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4583 MovDir[x][y] = right_dir;
4585 if (MovDir[x][y] != old_move_dir)
4589 else if (element == EL_YAMYAM)
4591 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4592 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4594 if (can_turn_left && can_turn_right)
4595 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4596 else if (can_turn_left)
4597 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4598 else if (can_turn_right)
4599 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4601 MovDir[x][y] = back_dir;
4603 MovDelay[x][y] = 16 + 16 * RND(3);
4605 else if (element == EL_DARK_YAMYAM)
4607 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4609 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4612 if (can_turn_left && can_turn_right)
4613 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4614 else if (can_turn_left)
4615 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4616 else if (can_turn_right)
4617 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4619 MovDir[x][y] = back_dir;
4621 MovDelay[x][y] = 16 + 16 * RND(3);
4623 else if (element == EL_PACMAN)
4625 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4626 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4628 if (can_turn_left && can_turn_right)
4629 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4630 else if (can_turn_left)
4631 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4632 else if (can_turn_right)
4633 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4635 MovDir[x][y] = back_dir;
4637 MovDelay[x][y] = 6 + RND(40);
4639 else if (element == EL_PIG)
4641 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4642 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4643 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4644 boolean should_turn_left, should_turn_right, should_move_on;
4646 int rnd = RND(rnd_value);
4648 should_turn_left = (can_turn_left &&
4650 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4651 y + back_dy + left_dy)));
4652 should_turn_right = (can_turn_right &&
4654 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4655 y + back_dy + right_dy)));
4656 should_move_on = (can_move_on &&
4659 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4660 y + move_dy + left_dy) ||
4661 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4662 y + move_dy + right_dy)));
4664 if (should_turn_left || should_turn_right || should_move_on)
4666 if (should_turn_left && should_turn_right && should_move_on)
4667 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4668 rnd < 2 * rnd_value / 3 ? right_dir :
4670 else if (should_turn_left && should_turn_right)
4671 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4672 else if (should_turn_left && should_move_on)
4673 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4674 else if (should_turn_right && should_move_on)
4675 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4676 else if (should_turn_left)
4677 MovDir[x][y] = left_dir;
4678 else if (should_turn_right)
4679 MovDir[x][y] = right_dir;
4680 else if (should_move_on)
4681 MovDir[x][y] = old_move_dir;
4683 else if (can_move_on && rnd > rnd_value / 8)
4684 MovDir[x][y] = old_move_dir;
4685 else if (can_turn_left && can_turn_right)
4686 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4687 else if (can_turn_left && rnd > rnd_value / 8)
4688 MovDir[x][y] = left_dir;
4689 else if (can_turn_right && rnd > rnd_value/8)
4690 MovDir[x][y] = right_dir;
4692 MovDir[x][y] = back_dir;
4694 xx = x + move_xy[MovDir[x][y]].x;
4695 yy = y + move_xy[MovDir[x][y]].y;
4698 /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4699 if (!IN_LEV_FIELD(xx, yy) ||
4700 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4701 MovDir[x][y] = old_move_dir;
4703 if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4704 MovDir[x][y] = old_move_dir;
4709 else if (element == EL_DRAGON)
4711 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4712 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4713 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4715 int rnd = RND(rnd_value);
4718 if (FrameCounter < 1 && x == 0 && y == 29)
4719 printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4722 if (can_move_on && rnd > rnd_value / 8)
4723 MovDir[x][y] = old_move_dir;
4724 else if (can_turn_left && can_turn_right)
4725 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4726 else if (can_turn_left && rnd > rnd_value / 8)
4727 MovDir[x][y] = left_dir;
4728 else if (can_turn_right && rnd > rnd_value / 8)
4729 MovDir[x][y] = right_dir;
4731 MovDir[x][y] = back_dir;
4733 xx = x + move_xy[MovDir[x][y]].x;
4734 yy = y + move_xy[MovDir[x][y]].y;
4737 if (FrameCounter < 1 && x == 0 && y == 29)
4738 printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4739 xx, yy, Feld[xx][yy],
4744 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4745 MovDir[x][y] = old_move_dir;
4747 if (!IS_FREE(xx, yy))
4748 MovDir[x][y] = old_move_dir;
4752 if (FrameCounter < 1 && x == 0 && y == 29)
4753 printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4758 else if (element == EL_MOLE)
4760 boolean can_move_on =
4761 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4762 IS_AMOEBOID(Feld[move_x][move_y]) ||
4763 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4766 boolean can_turn_left =
4767 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4768 IS_AMOEBOID(Feld[left_x][left_y])));
4770 boolean can_turn_right =
4771 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4772 IS_AMOEBOID(Feld[right_x][right_y])));
4774 if (can_turn_left && can_turn_right)
4775 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4776 else if (can_turn_left)
4777 MovDir[x][y] = left_dir;
4779 MovDir[x][y] = right_dir;
4782 if (MovDir[x][y] != old_move_dir)
4785 else if (element == EL_BALLOON)
4787 MovDir[x][y] = game.balloon_dir;
4790 else if (element == EL_SPRING)
4793 if (MovDir[x][y] & MV_HORIZONTAL &&
4794 !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4795 MovDir[x][y] = MV_NO_MOVING;
4797 if (MovDir[x][y] & MV_HORIZONTAL &&
4798 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4799 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4800 MovDir[x][y] = MV_NO_MOVING;
4805 else if (element == EL_ROBOT ||
4806 element == EL_SATELLITE ||
4807 element == EL_PENGUIN)
4809 int attr_x = -1, attr_y = -1;
4820 for (i = 0; i < MAX_PLAYERS; i++)
4822 struct PlayerInfo *player = &stored_player[i];
4823 int jx = player->jx, jy = player->jy;
4825 if (!player->active)
4829 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4838 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4839 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4840 game.engine_version < VERSION_IDENT(3,1,0,0)))
4842 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4849 if (element == EL_PENGUIN)
4852 static int xy[4][2] =
4860 for (i = 0; i < NUM_DIRECTIONS; i++)
4862 int ex = x + xy[i][0];
4863 int ey = y + xy[i][1];
4865 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4874 MovDir[x][y] = MV_NO_MOVING;
4876 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4877 else if (attr_x > x)
4878 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4880 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4881 else if (attr_y > y)
4882 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4884 if (element == EL_ROBOT)
4888 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4889 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4890 Moving2Blocked(x, y, &newx, &newy);
4892 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4893 MovDelay[x][y] = 8 + 8 * !RND(3);
4895 MovDelay[x][y] = 16;
4897 else if (element == EL_PENGUIN)
4903 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4905 boolean first_horiz = RND(2);
4906 int new_move_dir = MovDir[x][y];
4909 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4910 Moving2Blocked(x, y, &newx, &newy);
4912 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4916 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4917 Moving2Blocked(x, y, &newx, &newy);
4919 if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4922 MovDir[x][y] = old_move_dir;
4926 else /* (element == EL_SATELLITE) */
4932 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4934 boolean first_horiz = RND(2);
4935 int new_move_dir = MovDir[x][y];
4938 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4939 Moving2Blocked(x, y, &newx, &newy);
4941 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4945 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4946 Moving2Blocked(x, y, &newx, &newy);
4948 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4951 MovDir[x][y] = old_move_dir;
4956 else if (move_pattern == MV_TURNING_LEFT ||
4957 move_pattern == MV_TURNING_RIGHT ||
4958 move_pattern == MV_TURNING_LEFT_RIGHT ||
4959 move_pattern == MV_TURNING_RIGHT_LEFT ||
4960 move_pattern == MV_TURNING_RANDOM ||
4961 move_pattern == MV_ALL_DIRECTIONS)
4963 boolean can_turn_left =
4964 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4965 boolean can_turn_right =
4966 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4968 if (move_pattern == MV_TURNING_LEFT)
4969 MovDir[x][y] = left_dir;
4970 else if (move_pattern == MV_TURNING_RIGHT)
4971 MovDir[x][y] = right_dir;
4972 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4973 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4974 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4975 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4976 else if (move_pattern == MV_TURNING_RANDOM)
4977 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4978 can_turn_right && !can_turn_left ? right_dir :
4979 RND(2) ? left_dir : right_dir);
4980 else if (can_turn_left && can_turn_right)
4981 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4982 else if (can_turn_left)
4983 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4984 else if (can_turn_right)
4985 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4987 MovDir[x][y] = back_dir;
4989 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4991 else if (move_pattern == MV_HORIZONTAL ||
4992 move_pattern == MV_VERTICAL)
4994 if (move_pattern & old_move_dir)
4995 MovDir[x][y] = back_dir;
4996 else if (move_pattern == MV_HORIZONTAL)
4997 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4998 else if (move_pattern == MV_VERTICAL)
4999 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5001 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5003 else if (move_pattern & MV_ANY_DIRECTION)
5005 MovDir[x][y] = move_pattern;
5006 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5008 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5010 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5011 MovDir[x][y] = left_dir;
5012 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5013 MovDir[x][y] = right_dir;
5015 if (MovDir[x][y] != old_move_dir)
5016 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5018 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5020 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5021 MovDir[x][y] = right_dir;
5022 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5023 MovDir[x][y] = left_dir;
5025 if (MovDir[x][y] != old_move_dir)
5026 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5028 else if (move_pattern == MV_TOWARDS_PLAYER ||
5029 move_pattern == MV_AWAY_FROM_PLAYER)
5031 int attr_x = -1, attr_y = -1;
5033 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5044 for (i = 0; i < MAX_PLAYERS; i++)
5046 struct PlayerInfo *player = &stored_player[i];
5047 int jx = player->jx, jy = player->jy;
5049 if (!player->active)
5053 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5061 MovDir[x][y] = MV_NO_MOVING;
5063 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5064 else if (attr_x > x)
5065 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5067 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5068 else if (attr_y > y)
5069 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5071 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5073 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5075 boolean first_horiz = RND(2);
5076 int new_move_dir = MovDir[x][y];
5079 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5080 Moving2Blocked(x, y, &newx, &newy);
5082 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5086 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5087 Moving2Blocked(x, y, &newx, &newy);
5089 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5092 MovDir[x][y] = old_move_dir;
5095 else if (move_pattern == MV_WHEN_PUSHED ||
5096 move_pattern == MV_WHEN_DROPPED)
5098 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5099 MovDir[x][y] = MV_NO_MOVING;
5103 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5105 static int test_xy[7][2] =
5115 static int test_dir[7] =
5125 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5126 int move_preference = -1000000; /* start with very low preference */
5127 int new_move_dir = MV_NO_MOVING;
5128 int start_test = RND(4);
5131 for (i = 0; i < NUM_DIRECTIONS; i++)
5133 int move_dir = test_dir[start_test + i];
5134 int move_dir_preference;
5136 xx = x + test_xy[start_test + i][0];
5137 yy = y + test_xy[start_test + i][1];
5139 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5140 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5142 new_move_dir = move_dir;
5147 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5150 move_dir_preference = -1 * RunnerVisit[xx][yy];
5151 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5152 move_dir_preference = PlayerVisit[xx][yy];
5154 if (move_dir_preference > move_preference)
5156 /* prefer field that has not been visited for the longest time */
5157 move_preference = move_dir_preference;
5158 new_move_dir = move_dir;
5160 else if (move_dir_preference == move_preference &&
5161 move_dir == old_move_dir)
5163 /* prefer last direction when all directions are preferred equally */
5164 move_preference = move_dir_preference;
5165 new_move_dir = move_dir;
5169 MovDir[x][y] = new_move_dir;
5170 if (old_move_dir != new_move_dir)
5173 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5181 static void TurnRound(int x, int y)
5183 int direction = MovDir[x][y];
5186 GfxDir[x][y] = MovDir[x][y];
5192 GfxDir[x][y] = MovDir[x][y];
5195 if (direction != MovDir[x][y])
5200 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5203 GfxAction[x][y] = ACTION_WAITING;
5207 static boolean JustBeingPushed(int x, int y)
5211 for (i = 0; i < MAX_PLAYERS; i++)
5213 struct PlayerInfo *player = &stored_player[i];
5215 if (player->active && player->is_pushing && player->MovPos)
5217 int next_jx = player->jx + (player->jx - player->last_jx);
5218 int next_jy = player->jy + (player->jy - player->last_jy);
5220 if (x == next_jx && y == next_jy)
5228 void StartMoving(int x, int y)
5231 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5233 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5234 int element = Feld[x][y];
5240 if (MovDelay[x][y] == 0)
5241 GfxAction[x][y] = ACTION_DEFAULT;
5243 /* !!! this should be handled more generic (not only for mole) !!! */
5244 if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5245 GfxAction[x][y] = ACTION_DEFAULT;
5248 if (CAN_FALL(element) && y < lev_fieldy - 1)
5250 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5251 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5252 if (JustBeingPushed(x, y))
5255 if (element == EL_QUICKSAND_FULL)
5257 if (IS_FREE(x, y + 1))
5259 InitMovingField(x, y, MV_DOWN);
5260 started_moving = TRUE;
5262 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5263 Store[x][y] = EL_ROCK;
5265 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5267 PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5270 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5272 if (!MovDelay[x][y])
5273 MovDelay[x][y] = TILEY + 1;
5282 Feld[x][y] = EL_QUICKSAND_EMPTY;
5283 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5284 Store[x][y + 1] = Store[x][y];
5287 PlayLevelSoundAction(x, y, ACTION_FILLING);
5289 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5293 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5294 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5296 InitMovingField(x, y, MV_DOWN);
5297 started_moving = TRUE;
5299 Feld[x][y] = EL_QUICKSAND_FILLING;
5300 Store[x][y] = element;
5302 PlayLevelSoundAction(x, y, ACTION_FILLING);
5304 PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5307 else if (element == EL_MAGIC_WALL_FULL)
5309 if (IS_FREE(x, y + 1))
5311 InitMovingField(x, y, MV_DOWN);
5312 started_moving = TRUE;
5314 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5315 Store[x][y] = EL_CHANGED(Store[x][y]);
5317 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5319 if (!MovDelay[x][y])
5320 MovDelay[x][y] = TILEY/4 + 1;
5329 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5330 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5331 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5335 else if (element == EL_BD_MAGIC_WALL_FULL)
5337 if (IS_FREE(x, y + 1))
5339 InitMovingField(x, y, MV_DOWN);
5340 started_moving = TRUE;
5342 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5343 Store[x][y] = EL_CHANGED2(Store[x][y]);
5345 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5347 if (!MovDelay[x][y])
5348 MovDelay[x][y] = TILEY/4 + 1;
5357 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5358 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5359 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5363 else if (CAN_PASS_MAGIC_WALL(element) &&
5364 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5365 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5367 InitMovingField(x, y, MV_DOWN);
5368 started_moving = TRUE;
5371 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5372 EL_BD_MAGIC_WALL_FILLING);
5373 Store[x][y] = element;
5376 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5378 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5381 SplashAcid(x, y + 1);
5383 InitMovingField(x, y, MV_DOWN);
5384 started_moving = TRUE;
5386 Store[x][y] = EL_ACID;
5388 /* !!! TEST !!! better use "_FALLING" etc. !!! */
5389 GfxAction[x][y + 1] = ACTION_ACTIVE;
5393 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5394 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5396 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5397 CAN_SMASH(element) && WasJustFalling[x][y] &&
5398 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5400 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5401 CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5402 (Feld[x][y + 1] == EL_BLOCKED)))
5406 else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5407 CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5408 WasJustMoving[x][y] && !Pushed[x][y + 1])
5410 else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5411 WasJustMoving[x][y])
5416 /* this is needed for a special case not covered by calling "Impact()"
5417 from "ContinueMoving()": if an element moves to a tile directly below
5418 another element which was just falling on that tile (which was empty
5419 in the previous frame), the falling element above would just stop
5420 instead of smashing the element below (in previous version, the above
5421 element was just checked for "moving" instead of "falling", resulting
5422 in incorrect smashes caused by horizontal movement of the above
5423 element; also, the case of the player being the element to smash was
5424 simply not covered here... :-/ ) */
5427 WasJustMoving[x][y] = 0;
5428 WasJustFalling[x][y] = 0;
5431 CheckCollision[x][y] = 0;
5434 if (IS_PLAYER(x, y + 1))
5435 printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5440 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5442 if (MovDir[x][y] == MV_NO_MOVING)
5444 InitMovingField(x, y, MV_DOWN);
5445 started_moving = TRUE;
5448 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5450 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5451 MovDir[x][y] = MV_DOWN;
5453 InitMovingField(x, y, MV_DOWN);
5454 started_moving = TRUE;
5456 else if (element == EL_AMOEBA_DROP)
5458 Feld[x][y] = EL_AMOEBA_GROWING;
5459 Store[x][y] = EL_AMOEBA_WET;
5461 /* Store[x][y + 1] must be zero, because:
5462 (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5465 #if OLD_GAME_BEHAVIOUR
5466 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5468 else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5469 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5470 element != EL_DX_SUPABOMB)
5473 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5474 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5475 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5476 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5479 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5480 (IS_FREE(x - 1, y + 1) ||
5481 Feld[x - 1][y + 1] == EL_ACID));
5482 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5483 (IS_FREE(x + 1, y + 1) ||
5484 Feld[x + 1][y + 1] == EL_ACID));
5485 boolean can_fall_any = (can_fall_left || can_fall_right);
5486 boolean can_fall_both = (can_fall_left && can_fall_right);
5488 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5490 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5492 if (slippery_type == SLIPPERY_ONLY_LEFT)
5493 can_fall_right = FALSE;
5494 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5495 can_fall_left = FALSE;
5496 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5497 can_fall_right = FALSE;
5498 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5499 can_fall_left = FALSE;
5501 can_fall_any = (can_fall_left || can_fall_right);
5502 can_fall_both = (can_fall_left && can_fall_right);
5505 #if USE_NEW_SP_SLIPPERY
5506 /* !!! better use the same properties as for custom elements here !!! */
5507 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5508 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5510 can_fall_right = FALSE; /* slip down on left side */
5511 can_fall_both = FALSE;
5518 if (game.emulation == EMU_BOULDERDASH ||
5519 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5520 can_fall_right = FALSE; /* slip down on left side */
5522 can_fall_left = !(can_fall_right = RND(2));
5524 can_fall_both = FALSE;
5531 if (can_fall_both &&
5532 (game.emulation != EMU_BOULDERDASH &&
5533 element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5534 can_fall_left = !(can_fall_right = RND(2));
5537 /* if not determined otherwise, prefer left side for slipping down */
5538 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5539 started_moving = TRUE;
5543 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5545 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5548 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5549 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5550 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5551 int belt_dir = game.belt_dir[belt_nr];
5553 if ((belt_dir == MV_LEFT && left_is_free) ||
5554 (belt_dir == MV_RIGHT && right_is_free))
5557 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5560 InitMovingField(x, y, belt_dir);
5561 started_moving = TRUE;
5564 Pushed[x][y] = TRUE;
5565 Pushed[nextx][y] = TRUE;
5568 GfxAction[x][y] = ACTION_DEFAULT;
5572 MovDir[x][y] = 0; /* if element was moving, stop it */
5577 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5579 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5581 if (CAN_MOVE(element) && !started_moving)
5584 int move_pattern = element_info[element].move_pattern;
5589 if (MovDir[x][y] == MV_NO_MOVING)
5591 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5592 x, y, element, element_info[element].token_name);
5593 printf("StartMoving(): This should never happen!\n");
5598 Moving2Blocked(x, y, &newx, &newy);
5601 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5604 if ((element == EL_SATELLITE ||
5605 element == EL_BALLOON ||
5606 element == EL_SPRING)
5607 && JustBeingPushed(x, y))
5614 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5615 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5617 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5618 WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5619 (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5623 printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5624 element, element_info[element].token_name,
5625 WasJustMoving[x][y],
5626 HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5627 HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5628 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5629 HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5633 WasJustMoving[x][y] = 0;
5636 CheckCollision[x][y] = 0;
5638 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5641 if (Feld[x][y] != element) /* element has changed */
5643 element = Feld[x][y];
5644 move_pattern = element_info[element].move_pattern;
5646 if (!CAN_MOVE(element))
5650 if (Feld[x][y] != element) /* element has changed */
5658 if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5659 Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
5661 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5663 Moving2Blocked(x, y, &newx, &newy);
5664 if (Feld[newx][newy] == EL_BLOCKED)
5665 Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
5671 if (FrameCounter < 1 && x == 0 && y == 29)
5672 printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5675 if (!MovDelay[x][y]) /* start new movement phase */
5677 /* all objects that can change their move direction after each step
5678 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5680 if (element != EL_YAMYAM &&
5681 element != EL_DARK_YAMYAM &&
5682 element != EL_PACMAN &&
5683 !(move_pattern & MV_ANY_DIRECTION) &&
5684 move_pattern != MV_TURNING_LEFT &&
5685 move_pattern != MV_TURNING_RIGHT &&
5686 move_pattern != MV_TURNING_LEFT_RIGHT &&
5687 move_pattern != MV_TURNING_RIGHT_LEFT &&
5688 move_pattern != MV_TURNING_RANDOM)
5693 if (FrameCounter < 1 && x == 0 && y == 29)
5694 printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5697 if (MovDelay[x][y] && (element == EL_BUG ||
5698 element == EL_SPACESHIP ||
5699 element == EL_SP_SNIKSNAK ||
5700 element == EL_SP_ELECTRON ||
5701 element == EL_MOLE))
5702 DrawLevelField(x, y);
5706 if (MovDelay[x][y]) /* wait some time before next movement */
5711 if (element == EL_YAMYAM)
5714 el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5715 DrawLevelElementAnimation(x, y, element);
5719 if (MovDelay[x][y]) /* element still has to wait some time */
5722 /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5723 ResetGfxAnimation(x, y);
5727 if (GfxAction[x][y] != ACTION_WAITING)
5728 printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5730 GfxAction[x][y] = ACTION_WAITING;
5734 if (element == EL_ROBOT ||
5736 element == EL_PACMAN ||
5738 element == EL_YAMYAM ||
5739 element == EL_DARK_YAMYAM)
5742 DrawLevelElementAnimation(x, y, element);
5744 DrawLevelElementAnimationIfNeeded(x, y, element);
5746 PlayLevelSoundAction(x, y, ACTION_WAITING);
5748 else if (element == EL_SP_ELECTRON)
5749 DrawLevelElementAnimationIfNeeded(x, y, element);
5750 else if (element == EL_DRAGON)
5753 int dir = MovDir[x][y];
5754 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5755 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5756 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5757 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5758 dir == MV_UP ? IMG_FLAMES_1_UP :
5759 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5760 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5763 printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5766 GfxAction[x][y] = ACTION_ATTACKING;
5768 if (IS_PLAYER(x, y))
5769 DrawPlayerField(x, y);
5771 DrawLevelField(x, y);
5773 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5775 for (i = 1; i <= 3; i++)
5777 int xx = x + i * dx;
5778 int yy = y + i * dy;
5779 int sx = SCREENX(xx);
5780 int sy = SCREENY(yy);
5781 int flame_graphic = graphic + (i - 1);
5783 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5788 int flamed = MovingOrBlocked2Element(xx, yy);
5792 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5794 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5795 RemoveMovingField(xx, yy);
5797 RemoveField(xx, yy);
5799 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5802 RemoveMovingField(xx, yy);
5806 if (ChangeDelay[xx][yy])
5807 printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5808 Feld[xx][yy] == EL_BLOCKED));
5812 ChangeDelay[xx][yy] = 0;
5814 Feld[xx][yy] = EL_FLAMES;
5815 if (IN_SCR_FIELD(sx, sy))
5817 DrawLevelFieldCrumbledSand(xx, yy);
5818 DrawGraphic(sx, sy, flame_graphic, frame);
5823 if (Feld[xx][yy] == EL_FLAMES)
5824 Feld[xx][yy] = EL_EMPTY;
5825 DrawLevelField(xx, yy);
5830 if (MovDelay[x][y]) /* element still has to wait some time */
5832 PlayLevelSoundAction(x, y, ACTION_WAITING);
5838 /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5839 for all other elements GfxAction will be set by InitMovingField() */
5840 if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5841 GfxAction[x][y] = ACTION_MOVING;
5845 /* now make next step */
5847 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5849 if (DONT_COLLIDE_WITH(element) &&
5850 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5851 !PLAYER_ENEMY_PROTECTED(newx, newy))
5854 TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5858 /* player killed by element which is deadly when colliding with */
5860 KillHero(PLAYERINFO(newx, newy));
5867 else if (CAN_MOVE_INTO_ACID(element) &&
5868 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5869 (MovDir[x][y] == MV_DOWN ||
5870 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5872 else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5873 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5877 else if ((element == EL_PENGUIN ||
5878 element == EL_ROBOT ||
5879 element == EL_SATELLITE ||
5880 element == EL_BALLOON ||
5881 IS_CUSTOM_ELEMENT(element)) &&
5882 IN_LEV_FIELD(newx, newy) &&
5883 MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5886 SplashAcid(newx, newy);
5887 Store[x][y] = EL_ACID;
5889 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5891 if (Feld[newx][newy] == EL_EXIT_OPEN)
5895 DrawLevelField(x, y);
5897 Feld[x][y] = EL_EMPTY;
5898 DrawLevelField(x, y);
5901 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5902 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5903 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5905 local_player->friends_still_needed--;
5906 if (!local_player->friends_still_needed &&
5907 !local_player->GameOver && AllPlayersGone)
5908 local_player->LevelSolved = local_player->GameOver = TRUE;
5912 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5914 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5915 DrawLevelField(newx, newy);
5917 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5919 else if (!IS_FREE(newx, newy))
5921 GfxAction[x][y] = ACTION_WAITING;
5923 if (IS_PLAYER(x, y))
5924 DrawPlayerField(x, y);
5926 DrawLevelField(x, y);
5931 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5933 if (IS_FOOD_PIG(Feld[newx][newy]))
5935 if (IS_MOVING(newx, newy))
5936 RemoveMovingField(newx, newy);
5939 Feld[newx][newy] = EL_EMPTY;
5940 DrawLevelField(newx, newy);
5943 PlayLevelSound(x, y, SND_PIG_DIGGING);
5945 else if (!IS_FREE(newx, newy))
5947 if (IS_PLAYER(x, y))
5948 DrawPlayerField(x, y);
5950 DrawLevelField(x, y);
5959 else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5962 else if (IS_CUSTOM_ELEMENT(element) &&
5963 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5967 !IS_FREE(newx, newy)
5972 int new_element = Feld[newx][newy];
5975 printf("::: '%s' digs '%s' [%d]\n",
5976 element_info[element].token_name,
5977 element_info[Feld[newx][newy]].token_name,
5978 StorePlayer[newx][newy]);
5981 if (!IS_FREE(newx, newy))
5983 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5984 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5987 /* no element can dig solid indestructible elements */
5988 if (IS_INDESTRUCTIBLE(new_element) &&
5989 !IS_DIGGABLE(new_element) &&
5990 !IS_COLLECTIBLE(new_element))
5993 if (AmoebaNr[newx][newy] &&
5994 (new_element == EL_AMOEBA_FULL ||
5995 new_element == EL_BD_AMOEBA ||
5996 new_element == EL_AMOEBA_GROWING))
5998 AmoebaCnt[AmoebaNr[newx][newy]]--;
5999 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6002 if (IS_MOVING(newx, newy))
6003 RemoveMovingField(newx, newy);
6006 RemoveField(newx, newy);
6007 DrawLevelField(newx, newy);
6010 /* if digged element was about to explode, prevent the explosion */
6011 ExplodeField[newx][newy] = EX_TYPE_NONE;
6013 PlayLevelSoundAction(x, y, action);
6018 Store[newx][newy] = EL_EMPTY;
6019 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6020 Store[newx][newy] = element_info[element].move_leave_element;
6022 Store[newx][newy] = EL_EMPTY;
6023 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6024 element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6025 Store[newx][newy] = element_info[element].move_leave_element;
6028 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6029 element_info[element].can_leave_element = TRUE;
6032 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6034 RunnerVisit[x][y] = FrameCounter;
6035 PlayerVisit[x][y] /= 8; /* expire player visit path */
6041 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6043 if (!IS_FREE(newx, newy))
6045 if (IS_PLAYER(x, y))
6046 DrawPlayerField(x, y);
6048 DrawLevelField(x, y);
6054 boolean wanna_flame = !RND(10);
6055 int dx = newx - x, dy = newy - y;
6056 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6057 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6058 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6059 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6060 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6061 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6064 IS_CLASSIC_ENEMY(element1) ||
6065 IS_CLASSIC_ENEMY(element2)) &&
6066 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6067 element1 != EL_FLAMES && element2 != EL_FLAMES)
6070 ResetGfxAnimation(x, y);
6071 GfxAction[x][y] = ACTION_ATTACKING;
6074 if (IS_PLAYER(x, y))
6075 DrawPlayerField(x, y);
6077 DrawLevelField(x, y);
6079 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6081 MovDelay[x][y] = 50;
6085 RemoveField(newx, newy);
6087 Feld[newx][newy] = EL_FLAMES;
6088 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6091 RemoveField(newx1, newy1);
6093 Feld[newx1][newy1] = EL_FLAMES;
6095 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6098 RemoveField(newx2, newy2);
6100 Feld[newx2][newy2] = EL_FLAMES;
6107 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6108 Feld[newx][newy] == EL_DIAMOND)
6110 if (IS_MOVING(newx, newy))
6111 RemoveMovingField(newx, newy);
6114 Feld[newx][newy] = EL_EMPTY;
6115 DrawLevelField(newx, newy);
6118 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6120 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6121 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6123 if (AmoebaNr[newx][newy])
6125 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6126 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6127 Feld[newx][newy] == EL_BD_AMOEBA)
6128 AmoebaCnt[AmoebaNr[newx][newy]]--;
6133 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6135 if (IS_MOVING(newx, newy))
6138 RemoveMovingField(newx, newy);
6142 Feld[newx][newy] = EL_EMPTY;
6143 DrawLevelField(newx, newy);
6146 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6148 else if ((element == EL_PACMAN || element == EL_MOLE)
6149 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6151 if (AmoebaNr[newx][newy])
6153 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6154 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6155 Feld[newx][newy] == EL_BD_AMOEBA)
6156 AmoebaCnt[AmoebaNr[newx][newy]]--;
6159 if (element == EL_MOLE)
6161 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6162 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6164 ResetGfxAnimation(x, y);
6165 GfxAction[x][y] = ACTION_DIGGING;
6166 DrawLevelField(x, y);
6168 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6170 return; /* wait for shrinking amoeba */
6172 else /* element == EL_PACMAN */
6174 Feld[newx][newy] = EL_EMPTY;
6175 DrawLevelField(newx, newy);
6176 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6179 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6180 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6181 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6183 /* wait for shrinking amoeba to completely disappear */
6186 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6188 /* object was running against a wall */
6193 if (move_pattern & MV_ANY_DIRECTION &&
6194 move_pattern == MovDir[x][y])
6196 int blocking_element =
6197 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6200 printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6201 element_info[element].token_name,
6202 element_info[blocking_element].token_name,
6206 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6209 element = Feld[x][y]; /* element might have changed */
6214 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6215 DrawLevelElementAnimation(x, y, element);
6217 if (element == EL_BUG ||
6218 element == EL_SPACESHIP ||
6219 element == EL_SP_SNIKSNAK)
6220 DrawLevelField(x, y);
6221 else if (element == EL_MOLE)
6222 DrawLevelField(x, y);
6223 else if (element == EL_BD_BUTTERFLY ||
6224 element == EL_BD_FIREFLY)
6225 DrawLevelElementAnimationIfNeeded(x, y, element);
6226 else if (element == EL_SATELLITE)
6227 DrawLevelElementAnimationIfNeeded(x, y, element);
6228 else if (element == EL_SP_ELECTRON)
6229 DrawLevelElementAnimationIfNeeded(x, y, element);
6232 if (DONT_TOUCH(element))
6233 TestIfBadThingTouchesHero(x, y);
6236 PlayLevelSoundAction(x, y, ACTION_WAITING);
6242 InitMovingField(x, y, MovDir[x][y]);
6244 PlayLevelSoundAction(x, y, ACTION_MOVING);
6248 ContinueMoving(x, y);
6251 void ContinueMoving(int x, int y)
6253 int element = Feld[x][y];
6254 int stored = Store[x][y];
6255 struct ElementInfo *ei = &element_info[element];
6256 int direction = MovDir[x][y];
6257 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6258 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6259 int newx = x + dx, newy = y + dy;
6261 int nextx = newx + dx, nexty = newy + dy;
6264 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6265 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6267 boolean pushed_by_player = Pushed[x][y];
6270 MovPos[x][y] += getElementMoveStepsize(x, y);
6273 if (pushed_by_player && IS_PLAYER(x, y))
6275 /* special case: moving object pushed by player */
6276 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6279 if (pushed_by_player) /* special case: moving object pushed by player */
6280 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6283 if (ABS(MovPos[x][y]) < TILEX)
6285 DrawLevelField(x, y);
6287 return; /* element is still moving */
6290 /* element reached destination field */
6292 Feld[x][y] = EL_EMPTY;
6293 Feld[newx][newy] = element;
6294 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6297 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6299 element = Feld[newx][newy] = EL_ACID;
6302 else if (element == EL_MOLE)
6304 Feld[x][y] = EL_SAND;
6306 DrawLevelFieldCrumbledSandNeighbours(x, y);
6308 else if (element == EL_QUICKSAND_FILLING)
6310 element = Feld[newx][newy] = get_next_element(element);
6311 Store[newx][newy] = Store[x][y];
6313 else if (element == EL_QUICKSAND_EMPTYING)
6315 Feld[x][y] = get_next_element(element);
6316 element = Feld[newx][newy] = Store[x][y];
6318 else if (element == EL_MAGIC_WALL_FILLING)
6320 element = Feld[newx][newy] = get_next_element(element);
6321 if (!game.magic_wall_active)
6322 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6323 Store[newx][newy] = Store[x][y];
6325 else if (element == EL_MAGIC_WALL_EMPTYING)
6327 Feld[x][y] = get_next_element(element);
6328 if (!game.magic_wall_active)
6329 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6330 element = Feld[newx][newy] = Store[x][y];
6332 else if (element == EL_BD_MAGIC_WALL_FILLING)
6334 element = Feld[newx][newy] = get_next_element(element);
6335 if (!game.magic_wall_active)
6336 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6337 Store[newx][newy] = Store[x][y];
6339 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6341 Feld[x][y] = get_next_element(element);
6342 if (!game.magic_wall_active)
6343 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6344 element = Feld[newx][newy] = Store[x][y];
6346 else if (element == EL_AMOEBA_DROPPING)
6348 Feld[x][y] = get_next_element(element);
6349 element = Feld[newx][newy] = Store[x][y];
6351 else if (element == EL_SOKOBAN_OBJECT)
6354 Feld[x][y] = Back[x][y];
6356 if (Back[newx][newy])
6357 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6359 Back[x][y] = Back[newx][newy] = 0;
6362 else if (Store[x][y] == EL_ACID)
6364 element = Feld[newx][newy] = EL_ACID;
6368 else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6369 ei->move_leave_element != EL_EMPTY &&
6370 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6371 Store[x][y] != EL_EMPTY))
6373 /* some elements can leave other elements behind after moving */
6375 Feld[x][y] = ei->move_leave_element;
6376 InitField(x, y, FALSE);
6378 if (GFX_CRUMBLED(Feld[x][y]))
6379 DrawLevelFieldCrumbledSandNeighbours(x, y);
6383 Store[x][y] = EL_EMPTY;
6384 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6385 MovDelay[newx][newy] = 0;
6387 if (CAN_CHANGE(element))
6389 /* copy element change control values to new field */
6390 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6391 ChangePage[newx][newy] = ChangePage[x][y];
6392 Changed[newx][newy] = Changed[x][y];
6393 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6396 ChangeDelay[x][y] = 0;
6397 ChangePage[x][y] = -1;
6398 Changed[x][y] = CE_BITMASK_DEFAULT;
6399 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6401 /* copy animation control values to new field */
6402 GfxFrame[newx][newy] = GfxFrame[x][y];
6403 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6404 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6405 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6407 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6409 ResetGfxAnimation(x, y); /* reset animation values for old field */
6412 /* some elements can leave other elements behind after moving */
6414 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6415 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6416 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6418 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6419 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6423 int move_leave_element = ei->move_leave_element;
6425 Feld[x][y] = move_leave_element;
6426 InitField(x, y, FALSE);
6428 if (GFX_CRUMBLED(Feld[x][y]))
6429 DrawLevelFieldCrumbledSandNeighbours(x, y);
6431 if (ELEM_IS_PLAYER(move_leave_element))
6432 RelocatePlayer(x, y, move_leave_element);
6437 /* some elements can leave other elements behind after moving */
6438 if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6439 ei->move_leave_element != EL_EMPTY &&
6440 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6441 ei->can_leave_element_last))
6443 Feld[x][y] = ei->move_leave_element;
6444 InitField(x, y, FALSE);
6446 if (GFX_CRUMBLED(Feld[x][y]))
6447 DrawLevelFieldCrumbledSandNeighbours(x, y);
6450 ei->can_leave_element_last = ei->can_leave_element;
6451 ei->can_leave_element = FALSE;
6455 /* 2.1.1 (does not work correctly for spring) */
6456 if (!CAN_MOVE(element))
6457 MovDir[newx][newy] = 0;
6461 /* (does not work for falling objects that slide horizontally) */
6462 if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6463 MovDir[newx][newy] = 0;
6466 if (!CAN_MOVE(element) ||
6467 (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6468 MovDir[newx][newy] = 0;
6472 if (!CAN_MOVE(element) ||
6473 (CAN_FALL(element) && direction == MV_DOWN))
6474 GfxDir[x][y] = MovDir[newx][newy] = 0;
6476 if (!CAN_MOVE(element) ||
6477 (CAN_FALL(element) && direction == MV_DOWN &&
6478 (element == EL_SPRING ||
6479 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6480 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6481 GfxDir[x][y] = MovDir[newx][newy] = 0;
6487 DrawLevelField(x, y);
6488 DrawLevelField(newx, newy);
6490 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6492 /* prevent pushed element from moving on in pushed direction */
6493 if (pushed_by_player && CAN_MOVE(element) &&
6494 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6495 !(element_info[element].move_pattern & direction))
6496 TurnRound(newx, newy);
6499 /* prevent elements on conveyor belt from moving on in last direction */
6500 if (pushed_by_conveyor && CAN_FALL(element) &&
6501 direction & MV_HORIZONTAL)
6504 if (CAN_MOVE(element))
6505 InitMovDir(newx, newy);
6507 MovDir[newx][newy] = 0;
6509 MovDir[newx][newy] = 0;
6514 if (!pushed_by_player)
6516 int nextx = newx + dx, nexty = newy + dy;
6517 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6519 WasJustMoving[newx][newy] = 3;
6521 if (CAN_FALL(element) && direction == MV_DOWN)
6522 WasJustFalling[newx][newy] = 3;
6524 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6525 CheckCollision[newx][newy] = 2;
6528 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6530 TestIfBadThingTouchesHero(newx, newy);
6531 TestIfBadThingTouchesFriend(newx, newy);
6533 if (!IS_CUSTOM_ELEMENT(element))
6534 TestIfBadThingTouchesOtherBadThing(newx, newy);
6536 else if (element == EL_PENGUIN)
6537 TestIfFriendTouchesBadThing(newx, newy);
6539 #if USE_NEW_MOVE_STYLE
6541 if (CAN_FALL(element) && direction == MV_DOWN &&
6542 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6543 IS_PLAYER(x, newy + 1))
6544 printf("::: we would now kill the player [%d]\n", FrameCounter);
6547 /* give the player one last chance (one more frame) to move away */
6548 if (CAN_FALL(element) && direction == MV_DOWN &&
6549 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6550 (!IS_PLAYER(x, newy + 1) ||
6551 game.engine_version < VERSION_IDENT(3,1,1,0)))
6554 if (CAN_FALL(element) && direction == MV_DOWN &&
6555 (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6563 if (pushed_by_player && !game.use_bug_change_when_pushing)
6565 if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6568 if (pushed_by_player)
6573 int dig_side = MV_DIR_OPPOSITE(direction);
6575 static int trigger_sides[4] =
6577 CH_SIDE_RIGHT, /* moving left */
6578 CH_SIDE_LEFT, /* moving right */
6579 CH_SIDE_BOTTOM, /* moving up */
6580 CH_SIDE_TOP, /* moving down */
6582 int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6584 struct PlayerInfo *player = PLAYERINFO(x, y);
6586 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6587 player->index_bit, dig_side);
6588 CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6589 player->index_bit, dig_side);
6594 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6598 if (ChangePage[newx][newy] != -1) /* delayed change */
6599 ChangeElement(newx, newy, ChangePage[newx][newy]);
6604 TestIfElementHitsCustomElement(newx, newy, direction);
6608 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6610 int hitting_element = Feld[newx][newy];
6612 /* !!! fix side (direction) orientation here and elsewhere !!! */
6613 CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6617 if (IN_LEV_FIELD(nextx, nexty))
6619 int opposite_direction = MV_DIR_OPPOSITE(direction);
6620 int hitting_side = direction;
6621 int touched_side = opposite_direction;
6622 int touched_element = MovingOrBlocked2Element(nextx, nexty);
6623 boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6624 MovDir[nextx][nexty] != direction ||
6625 ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6631 CheckElementChangeBySide(nextx, nexty, touched_element,
6632 CE_HIT_BY_SOMETHING, opposite_direction);
6634 if (IS_CUSTOM_ELEMENT(hitting_element) &&
6635 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6637 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6639 struct ElementChangeInfo *change =
6640 &element_info[hitting_element].change_page[i];
6642 if (change->can_change &&
6643 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6644 change->trigger_side & touched_side &&
6645 change->trigger_element == touched_element)
6647 CheckElementChangeByPage(newx, newy, hitting_element,
6648 touched_element, CE_OTHER_IS_HITTING,i);
6654 if (IS_CUSTOM_ELEMENT(touched_element) &&
6655 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6657 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6659 struct ElementChangeInfo *change =
6660 &element_info[touched_element].change_page[i];
6662 if (change->can_change &&
6663 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6664 change->trigger_side & hitting_side &&
6665 change->trigger_element == hitting_element)
6667 CheckElementChangeByPage(nextx, nexty, touched_element,
6668 hitting_element, CE_OTHER_GETS_HIT, i);
6679 TestIfPlayerTouchesCustomElement(newx, newy);
6680 TestIfElementTouchesCustomElement(newx, newy);
6683 int AmoebeNachbarNr(int ax, int ay)
6686 int element = Feld[ax][ay];
6688 static int xy[4][2] =
6696 for (i = 0; i < NUM_DIRECTIONS; i++)
6698 int x = ax + xy[i][0];
6699 int y = ay + xy[i][1];
6701 if (!IN_LEV_FIELD(x, y))
6704 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6705 group_nr = AmoebaNr[x][y];
6711 void AmoebenVereinigen(int ax, int ay)
6713 int i, x, y, xx, yy;
6714 int new_group_nr = AmoebaNr[ax][ay];
6715 static int xy[4][2] =
6723 if (new_group_nr == 0)
6726 for (i = 0; i < NUM_DIRECTIONS; i++)
6731 if (!IN_LEV_FIELD(x, y))
6734 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6735 Feld[x][y] == EL_BD_AMOEBA ||
6736 Feld[x][y] == EL_AMOEBA_DEAD) &&
6737 AmoebaNr[x][y] != new_group_nr)
6739 int old_group_nr = AmoebaNr[x][y];
6741 if (old_group_nr == 0)
6744 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6745 AmoebaCnt[old_group_nr] = 0;
6746 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6747 AmoebaCnt2[old_group_nr] = 0;
6749 for (yy = 0; yy < lev_fieldy; yy++)
6751 for (xx = 0; xx < lev_fieldx; xx++)
6753 if (AmoebaNr[xx][yy] == old_group_nr)
6754 AmoebaNr[xx][yy] = new_group_nr;
6761 void AmoebeUmwandeln(int ax, int ay)
6765 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6767 int group_nr = AmoebaNr[ax][ay];
6772 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6773 printf("AmoebeUmwandeln(): This should never happen!\n");
6778 for (y = 0; y < lev_fieldy; y++)
6780 for (x = 0; x < lev_fieldx; x++)
6782 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6785 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6789 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6790 SND_AMOEBA_TURNING_TO_GEM :
6791 SND_AMOEBA_TURNING_TO_ROCK));
6796 static int xy[4][2] =
6804 for (i = 0; i < NUM_DIRECTIONS; i++)
6809 if (!IN_LEV_FIELD(x, y))
6812 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6814 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6815 SND_AMOEBA_TURNING_TO_GEM :
6816 SND_AMOEBA_TURNING_TO_ROCK));
6823 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6826 int group_nr = AmoebaNr[ax][ay];
6827 boolean done = FALSE;
6832 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6833 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6838 for (y = 0; y < lev_fieldy; y++)
6840 for (x = 0; x < lev_fieldx; x++)
6842 if (AmoebaNr[x][y] == group_nr &&
6843 (Feld[x][y] == EL_AMOEBA_DEAD ||
6844 Feld[x][y] == EL_BD_AMOEBA ||
6845 Feld[x][y] == EL_AMOEBA_GROWING))
6848 Feld[x][y] = new_element;
6849 InitField(x, y, FALSE);
6850 DrawLevelField(x, y);
6857 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6858 SND_BD_AMOEBA_TURNING_TO_ROCK :
6859 SND_BD_AMOEBA_TURNING_TO_GEM));
6862 void AmoebeWaechst(int x, int y)
6864 static unsigned long sound_delay = 0;
6865 static unsigned long sound_delay_value = 0;
6867 if (!MovDelay[x][y]) /* start new growing cycle */
6871 if (DelayReached(&sound_delay, sound_delay_value))
6874 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6876 if (Store[x][y] == EL_BD_AMOEBA)
6877 PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6879 PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6881 sound_delay_value = 30;
6885 if (MovDelay[x][y]) /* wait some time before growing bigger */
6888 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6890 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6891 6 - MovDelay[x][y]);
6893 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6896 if (!MovDelay[x][y])
6898 Feld[x][y] = Store[x][y];
6900 DrawLevelField(x, y);
6905 void AmoebaDisappearing(int x, int y)
6907 static unsigned long sound_delay = 0;
6908 static unsigned long sound_delay_value = 0;
6910 if (!MovDelay[x][y]) /* start new shrinking cycle */
6914 if (DelayReached(&sound_delay, sound_delay_value))
6915 sound_delay_value = 30;
6918 if (MovDelay[x][y]) /* wait some time before shrinking */
6921 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6923 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6924 6 - MovDelay[x][y]);
6926 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6929 if (!MovDelay[x][y])
6931 Feld[x][y] = EL_EMPTY;
6932 DrawLevelField(x, y);
6934 /* don't let mole enter this field in this cycle;
6935 (give priority to objects falling to this field from above) */
6941 void AmoebeAbleger(int ax, int ay)
6944 int element = Feld[ax][ay];
6945 int graphic = el2img(element);
6946 int newax = ax, neway = ay;
6947 static int xy[4][2] =
6955 if (!level.amoeba_speed)
6957 Feld[ax][ay] = EL_AMOEBA_DEAD;
6958 DrawLevelField(ax, ay);
6962 if (IS_ANIMATED(graphic))
6963 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6965 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6966 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6968 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6971 if (MovDelay[ax][ay])
6975 if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6978 int x = ax + xy[start][0];
6979 int y = ay + xy[start][1];
6981 if (!IN_LEV_FIELD(x, y))
6985 if (IS_FREE(x, y) ||
6986 CAN_GROW_INTO(Feld[x][y]) ||
6987 Feld[x][y] == EL_QUICKSAND_EMPTY)
6993 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6994 if (IS_FREE(x, y) ||
6995 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7002 if (newax == ax && neway == ay)
7005 else /* normal or "filled" (BD style) amoeba */
7008 boolean waiting_for_player = FALSE;
7010 for (i = 0; i < NUM_DIRECTIONS; i++)
7012 int j = (start + i) % 4;
7013 int x = ax + xy[j][0];
7014 int y = ay + xy[j][1];
7016 if (!IN_LEV_FIELD(x, y))
7020 if (IS_FREE(x, y) ||
7021 CAN_GROW_INTO(Feld[x][y]) ||
7022 Feld[x][y] == EL_QUICKSAND_EMPTY)
7029 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7030 if (IS_FREE(x, y) ||
7031 Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7038 else if (IS_PLAYER(x, y))
7039 waiting_for_player = TRUE;
7042 if (newax == ax && neway == ay) /* amoeba cannot grow */
7045 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7047 if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7050 Feld[ax][ay] = EL_AMOEBA_DEAD;
7051 DrawLevelField(ax, ay);
7052 AmoebaCnt[AmoebaNr[ax][ay]]--;
7054 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7056 if (element == EL_AMOEBA_FULL)
7057 AmoebeUmwandeln(ax, ay);
7058 else if (element == EL_BD_AMOEBA)
7059 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7064 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7066 /* amoeba gets larger by growing in some direction */
7068 int new_group_nr = AmoebaNr[ax][ay];
7071 if (new_group_nr == 0)
7073 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7074 printf("AmoebeAbleger(): This should never happen!\n");
7079 AmoebaNr[newax][neway] = new_group_nr;
7080 AmoebaCnt[new_group_nr]++;
7081 AmoebaCnt2[new_group_nr]++;
7083 /* if amoeba touches other amoeba(s) after growing, unify them */
7084 AmoebenVereinigen(newax, neway);
7086 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7088 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7094 if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7095 (neway == lev_fieldy - 1 && newax != ax))
7097 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7098 Store[newax][neway] = element;
7100 else if (neway == ay)
7102 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7104 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7106 PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7111 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7112 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7113 Store[ax][ay] = EL_AMOEBA_DROP;
7114 ContinueMoving(ax, ay);
7118 DrawLevelField(newax, neway);
7121 void Life(int ax, int ay)
7124 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7126 int element = Feld[ax][ay];
7127 int graphic = el2img(element);
7128 boolean changed = FALSE;
7130 if (IS_ANIMATED(graphic))
7131 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7136 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7137 MovDelay[ax][ay] = life_time;
7139 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7142 if (MovDelay[ax][ay])
7146 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7148 int xx = ax+x1, yy = ay+y1;
7151 if (!IN_LEV_FIELD(xx, yy))
7154 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7156 int x = xx+x2, y = yy+y2;
7158 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7161 if (((Feld[x][y] == element ||
7162 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7164 (IS_FREE(x, y) && Stop[x][y]))
7168 if (xx == ax && yy == ay) /* field in the middle */
7170 if (nachbarn < life[0] || nachbarn > life[1])
7172 Feld[xx][yy] = EL_EMPTY;
7174 DrawLevelField(xx, yy);
7175 Stop[xx][yy] = TRUE;
7180 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7181 { /* free border field */
7182 if (nachbarn >= life[2] && nachbarn <= life[3])
7184 Feld[xx][yy] = element;
7185 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7187 DrawLevelField(xx, yy);
7188 Stop[xx][yy] = TRUE;
7193 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7194 else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7195 { /* free border field */
7196 if (nachbarn >= life[2] && nachbarn <= life[3])
7198 Feld[xx][yy] = element;
7199 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7201 DrawLevelField(xx, yy);
7202 Stop[xx][yy] = TRUE;
7210 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7211 SND_GAME_OF_LIFE_GROWING);
7214 static void InitRobotWheel(int x, int y)
7216 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7219 static void RunRobotWheel(int x, int y)
7221 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7224 static void StopRobotWheel(int x, int y)
7226 if (ZX == x && ZY == y)
7230 static void InitTimegateWheel(int x, int y)
7233 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7235 /* another brainless, "type style" bug ... :-( */
7236 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7240 static void RunTimegateWheel(int x, int y)
7242 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7245 void CheckExit(int x, int y)
7247 if (local_player->gems_still_needed > 0 ||
7248 local_player->sokobanfields_still_needed > 0 ||
7249 local_player->lights_still_needed > 0)
7251 int element = Feld[x][y];
7252 int graphic = el2img(element);
7254 if (IS_ANIMATED(graphic))
7255 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7260 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7263 Feld[x][y] = EL_EXIT_OPENING;
7265 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7268 void CheckExitSP(int x, int y)
7270 if (local_player->gems_still_needed > 0)
7272 int element = Feld[x][y];
7273 int graphic = el2img(element);
7275 if (IS_ANIMATED(graphic))
7276 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7281 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7284 Feld[x][y] = EL_SP_EXIT_OPENING;
7286 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7289 static void CloseAllOpenTimegates()
7293 for (y = 0; y < lev_fieldy; y++)
7295 for (x = 0; x < lev_fieldx; x++)
7297 int element = Feld[x][y];
7299 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7301 Feld[x][y] = EL_TIMEGATE_CLOSING;
7303 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7305 PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7312 void EdelsteinFunkeln(int x, int y)
7314 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7317 if (Feld[x][y] == EL_BD_DIAMOND)
7320 if (MovDelay[x][y] == 0) /* next animation frame */
7321 MovDelay[x][y] = 11 * !SimpleRND(500);
7323 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7327 if (setup.direct_draw && MovDelay[x][y])
7328 SetDrawtoField(DRAW_BUFFERED);
7330 DrawLevelElementAnimation(x, y, Feld[x][y]);
7332 if (MovDelay[x][y] != 0)
7334 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7335 10 - MovDelay[x][y]);
7337 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7339 if (setup.direct_draw)
7343 dest_x = FX + SCREENX(x) * TILEX;
7344 dest_y = FY + SCREENY(y) * TILEY;
7346 BlitBitmap(drawto_field, window,
7347 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7348 SetDrawtoField(DRAW_DIRECT);
7354 void MauerWaechst(int x, int y)
7358 if (!MovDelay[x][y]) /* next animation frame */
7359 MovDelay[x][y] = 3 * delay;
7361 if (MovDelay[x][y]) /* wait some time before next frame */
7365 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7367 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7368 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7370 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7373 if (!MovDelay[x][y])
7375 if (MovDir[x][y] == MV_LEFT)
7377 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7378 DrawLevelField(x - 1, y);
7380 else if (MovDir[x][y] == MV_RIGHT)
7382 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7383 DrawLevelField(x + 1, y);
7385 else if (MovDir[x][y] == MV_UP)
7387 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7388 DrawLevelField(x, y - 1);
7392 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7393 DrawLevelField(x, y + 1);
7396 Feld[x][y] = Store[x][y];
7398 GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7399 DrawLevelField(x, y);
7404 void MauerAbleger(int ax, int ay)
7406 int element = Feld[ax][ay];
7407 int graphic = el2img(element);
7408 boolean oben_frei = FALSE, unten_frei = FALSE;
7409 boolean links_frei = FALSE, rechts_frei = FALSE;
7410 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7411 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7412 boolean new_wall = FALSE;
7414 if (IS_ANIMATED(graphic))
7415 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7417 if (!MovDelay[ax][ay]) /* start building new wall */
7418 MovDelay[ax][ay] = 6;
7420 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7423 if (MovDelay[ax][ay])
7427 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7429 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7431 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7433 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7436 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7437 element == EL_EXPANDABLE_WALL_ANY)
7441 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7442 Store[ax][ay-1] = element;
7443 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7444 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7445 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7446 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7451 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7452 Store[ax][ay+1] = element;
7453 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7454 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7455 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7456 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7461 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7462 element == EL_EXPANDABLE_WALL_ANY ||
7463 element == EL_EXPANDABLE_WALL)
7467 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7468 Store[ax-1][ay] = element;
7469 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7470 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7471 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7472 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7478 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7479 Store[ax+1][ay] = element;
7480 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7481 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7482 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7483 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7488 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7489 DrawLevelField(ax, ay);
7491 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7493 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7494 unten_massiv = TRUE;
7495 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7496 links_massiv = TRUE;
7497 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7498 rechts_massiv = TRUE;
7500 if (((oben_massiv && unten_massiv) ||
7501 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7502 element == EL_EXPANDABLE_WALL) &&
7503 ((links_massiv && rechts_massiv) ||
7504 element == EL_EXPANDABLE_WALL_VERTICAL))
7505 Feld[ax][ay] = EL_WALL;
7509 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7511 PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7515 void CheckForDragon(int x, int y)
7518 boolean dragon_found = FALSE;
7519 static int xy[4][2] =
7527 for (i = 0; i < NUM_DIRECTIONS; i++)
7529 for (j = 0; j < 4; j++)
7531 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7533 if (IN_LEV_FIELD(xx, yy) &&
7534 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7536 if (Feld[xx][yy] == EL_DRAGON)
7537 dragon_found = TRUE;
7546 for (i = 0; i < NUM_DIRECTIONS; i++)
7548 for (j = 0; j < 3; j++)
7550 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7552 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7554 Feld[xx][yy] = EL_EMPTY;
7555 DrawLevelField(xx, yy);
7564 static void InitBuggyBase(int x, int y)
7566 int element = Feld[x][y];
7567 int activating_delay = FRAMES_PER_SECOND / 4;
7570 (element == EL_SP_BUGGY_BASE ?
7571 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7572 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7574 element == EL_SP_BUGGY_BASE_ACTIVE ?
7575 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7578 static void WarnBuggyBase(int x, int y)
7581 static int xy[4][2] =
7589 for (i = 0; i < NUM_DIRECTIONS; i++)
7591 int xx = x + xy[i][0], yy = y + xy[i][1];
7593 if (IS_PLAYER(xx, yy))
7595 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7602 static void InitTrap(int x, int y)
7604 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7607 static void ActivateTrap(int x, int y)
7609 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7612 static void ChangeActiveTrap(int x, int y)
7614 int graphic = IMG_TRAP_ACTIVE;
7616 /* if new animation frame was drawn, correct crumbled sand border */
7617 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7618 DrawLevelFieldCrumbledSand(x, y);
7621 static void ChangeElementNowExt(int x, int y, int target_element)
7623 int previous_move_direction = MovDir[x][y];
7625 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7626 IS_WALKABLE(Feld[x][y]));
7628 boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7629 IS_WALKABLE(Feld[x][y]) &&
7633 /* check if element under player changes from accessible to unaccessible
7634 (needed for special case of dropping element which then changes) */
7635 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7636 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7639 printf("::: BOOOM! [%d, '%s']\n", target_element,
7640 element_info[target_element].token_name);
7652 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7653 RemoveMovingField(x, y);
7657 Feld[x][y] = target_element;
7660 Feld[x][y] = target_element;
7663 ResetGfxAnimation(x, y);
7664 ResetRandomAnimationValue(x, y);
7666 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7667 MovDir[x][y] = previous_move_direction;
7670 InitField_WithBug1(x, y, FALSE);
7672 InitField(x, y, FALSE);
7673 if (CAN_MOVE(Feld[x][y]))
7677 DrawLevelField(x, y);
7679 if (GFX_CRUMBLED(Feld[x][y]))
7680 DrawLevelFieldCrumbledSandNeighbours(x, y);
7684 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7688 TestIfBadThingTouchesHero(x, y);
7689 TestIfPlayerTouchesCustomElement(x, y);
7690 TestIfElementTouchesCustomElement(x, y);
7693 /* "Changed[][]" not set yet to allow "entered by player" change one time */
7694 if (ELEM_IS_PLAYER(target_element))
7695 RelocatePlayer(x, y, target_element);
7698 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7702 TestIfBadThingTouchesHero(x, y);
7703 TestIfPlayerTouchesCustomElement(x, y);
7704 TestIfElementTouchesCustomElement(x, y);
7708 static boolean ChangeElementNow(int x, int y, int element, int page)
7710 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7712 int old_element = Feld[x][y];
7714 /* always use default change event to prevent running into a loop */
7715 if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7716 ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7718 if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7720 /* reset actual trigger element and player */
7721 change->actual_trigger_element = EL_EMPTY;
7722 change->actual_trigger_player = EL_PLAYER_1;
7725 /* do not change already changed elements with same change event */
7727 if (Changed[x][y] & ChangeEvent[x][y])
7734 Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
7737 /* !!! indirect change before direct change !!! */
7738 CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7741 if (change->explode)
7748 if (change->use_target_content)
7750 boolean complete_replace = TRUE;
7751 boolean can_replace[3][3];
7754 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7757 boolean is_walkable;
7758 boolean is_diggable;
7759 boolean is_collectible;
7760 boolean is_removable;
7761 boolean is_destructible;
7762 int ex = x + xx - 1;
7763 int ey = y + yy - 1;
7764 int content_element = change->target_content[xx][yy];
7767 can_replace[xx][yy] = TRUE;
7769 if (ex == x && ey == y) /* do not check changing element itself */
7772 if (content_element == EL_EMPTY_SPACE)
7774 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7779 if (!IN_LEV_FIELD(ex, ey))
7781 can_replace[xx][yy] = FALSE;
7782 complete_replace = FALSE;
7788 if (Changed[ex][ey]) /* do not change already changed elements */
7790 can_replace[xx][yy] = FALSE;
7791 complete_replace = FALSE;
7799 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7800 e = MovingOrBlocked2Element(ex, ey);
7805 is_empty = (IS_FREE(ex, ey) ||
7806 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7807 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7808 !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7812 is_empty = (IS_FREE(ex, ey) ||
7813 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7815 is_empty = (IS_FREE(ex, ey) ||
7816 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7821 is_walkable = (is_empty || IS_WALKABLE(e));
7822 is_diggable = (is_empty || IS_DIGGABLE(e));
7823 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7824 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7825 is_removable = (is_diggable || is_collectible);
7827 can_replace[xx][yy] =
7828 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7829 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7830 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7831 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7832 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7833 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7834 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7836 if (!can_replace[xx][yy])
7837 complete_replace = FALSE;
7839 empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7840 IS_WALKABLE(content_element)));
7842 half_destructible = (empty_for_element || IS_DIGGABLE(e));
7844 half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7847 if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
7848 (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7849 (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7851 can_replace[xx][yy] = FALSE;
7852 complete_replace = FALSE;
7857 if (!change->only_if_complete || complete_replace)
7859 boolean something_has_changed = FALSE;
7861 if (change->only_if_complete && change->use_random_replace &&
7862 RND(100) < change->random_percentage)
7865 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7867 int ex = x + xx - 1;
7868 int ey = y + yy - 1;
7869 int content_element;
7871 if (can_replace[xx][yy] && (!change->use_random_replace ||
7872 RND(100) < change->random_percentage))
7874 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7875 RemoveMovingField(ex, ey);
7877 ChangeEvent[ex][ey] = ChangeEvent[x][y];
7879 content_element = change->target_content[xx][yy];
7880 target_element = GET_TARGET_ELEMENT(content_element, change);
7882 ChangeElementNowExt(ex, ey, target_element);
7884 something_has_changed = TRUE;
7886 /* for symmetry reasons, freeze newly created border elements */
7887 if (ex != x || ey != y)
7888 Stop[ex][ey] = TRUE; /* no more moving in this frame */
7892 if (something_has_changed)
7893 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7898 target_element = GET_TARGET_ELEMENT(change->target_element, change);
7900 ChangeElementNowExt(x, y, target_element);
7902 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7906 /* this uses direct change before indirect change */
7907 CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7913 static void ChangeElement(int x, int y, int page)
7915 int element = MovingOrBlocked2Element(x, y);
7916 struct ElementInfo *ei = &element_info[element];
7917 struct ElementChangeInfo *change = &ei->change_page[page];
7920 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7923 printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7924 x, y, element, element_info[element].token_name);
7925 printf("ChangeElement(): This should never happen!\n");
7930 /* this can happen with classic bombs on walkable, changing elements */
7931 if (!CAN_CHANGE(element))
7934 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
7935 ChangeDelay[x][y] = 0;
7941 if (ChangeDelay[x][y] == 0) /* initialize element change */
7943 ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
7944 RND(change->delay_random * change->delay_frames)) + 1;
7946 ResetGfxAnimation(x, y);
7947 ResetRandomAnimationValue(x, y);
7949 if (change->pre_change_function)
7950 change->pre_change_function(x, y);
7953 ChangeDelay[x][y]--;
7955 if (ChangeDelay[x][y] != 0) /* continue element change */
7957 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7959 if (IS_ANIMATED(graphic))
7960 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7962 if (change->change_function)
7963 change->change_function(x, y);
7965 else /* finish element change */
7967 if (ChangePage[x][y] != -1) /* remember page from delayed change */
7969 page = ChangePage[x][y];
7970 ChangePage[x][y] = -1;
7972 change = &ei->change_page[page];
7976 if (IS_MOVING(x, y) && !change->explode)
7978 if (IS_MOVING(x, y)) /* never change a running system ;-) */
7981 ChangeDelay[x][y] = 1; /* try change after next move step */
7982 ChangePage[x][y] = page; /* remember page to use for change */
7987 if (ChangeElementNow(x, y, element, page))
7989 if (change->post_change_function)
7990 change->post_change_function(x, y);
7995 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7996 int trigger_element,
8003 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8005 if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
8008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8010 int element = EL_CUSTOM_START + i;
8012 boolean change_element = FALSE;
8015 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8018 for (j = 0; j < element_info[element].num_change_pages; j++)
8020 struct ElementChangeInfo *change = &element_info[element].change_page[j];
8022 if (change->can_change &&
8023 change->events & CH_EVENT_BIT(trigger_event) &&
8024 change->trigger_side & trigger_side &&
8025 change->trigger_player & trigger_player &&
8026 change->trigger_page & trigger_page_bits &&
8027 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8030 if (!(change->events & CH_EVENT_BIT(trigger_event)))
8031 printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8032 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8035 change_element = TRUE;
8038 change->actual_trigger_element = trigger_element;
8039 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8045 if (!change_element)
8048 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8051 if (x == lx && y == ly) /* do not change trigger element itself */
8055 if (Feld[x][y] == element)
8057 ChangeDelay[x][y] = 1;
8058 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8059 ChangeElement(x, y, page);
8067 static boolean CheckElementChangeExt(int x, int y,
8069 int trigger_element,
8075 if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8078 if (Feld[x][y] == EL_BLOCKED)
8080 Blocked2Moving(x, y, &x, &y);
8081 element = Feld[x][y];
8085 if (Feld[x][y] != element) /* check if element has already changed */
8088 printf("::: %d ('%s') != %d ('%s') [%d]\n",
8089 Feld[x][y], element_info[Feld[x][y]].token_name,
8090 element, element_info[element].token_name,
8099 if (trigger_page < 0)
8101 boolean change_element = FALSE;
8104 for (i = 0; i < element_info[element].num_change_pages; i++)
8106 struct ElementChangeInfo *change = &element_info[element].change_page[i];
8108 if (change->can_change &&
8109 change->events & CH_EVENT_BIT(trigger_event) &&
8110 change->trigger_side & trigger_side &&
8111 change->trigger_player & trigger_player)
8113 change_element = TRUE;
8116 change->actual_trigger_element = trigger_element;
8117 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8123 if (!change_element)
8128 struct ElementInfo *ei = &element_info[element];
8129 struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8131 change->actual_trigger_element = trigger_element;
8132 change->actual_trigger_player = EL_PLAYER_1; /* unused */
8137 /* !!! this check misses pages with same event, but different side !!! */
8139 if (trigger_page < 0)
8140 trigger_page = element_info[element].event_page_nr[trigger_event];
8142 if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8146 ChangeDelay[x][y] = 1;
8147 ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8148 ChangeElement(x, y, trigger_page);
8153 static void PlayPlayerSound(struct PlayerInfo *player)
8155 int jx = player->jx, jy = player->jy;
8156 int element = player->element_nr;
8157 int last_action = player->last_action_waiting;
8158 int action = player->action_waiting;
8160 if (player->is_waiting)
8162 if (action != last_action)
8163 PlayLevelSoundElementAction(jx, jy, element, action);
8165 PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8169 if (action != last_action)
8170 StopSound(element_info[element].sound[last_action]);
8172 if (last_action == ACTION_SLEEPING)
8173 PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8177 static void PlayAllPlayersSound()
8181 for (i = 0; i < MAX_PLAYERS; i++)
8182 if (stored_player[i].active)
8183 PlayPlayerSound(&stored_player[i]);
8186 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8188 boolean last_waiting = player->is_waiting;
8189 int move_dir = player->MovDir;
8191 player->last_action_waiting = player->action_waiting;
8195 if (!last_waiting) /* not waiting -> waiting */
8197 player->is_waiting = TRUE;
8199 player->frame_counter_bored =
8201 game.player_boring_delay_fixed +
8202 SimpleRND(game.player_boring_delay_random);
8203 player->frame_counter_sleeping =
8205 game.player_sleeping_delay_fixed +
8206 SimpleRND(game.player_sleeping_delay_random);
8208 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8211 if (game.player_sleeping_delay_fixed +
8212 game.player_sleeping_delay_random > 0 &&
8213 player->anim_delay_counter == 0 &&
8214 player->post_delay_counter == 0 &&
8215 FrameCounter >= player->frame_counter_sleeping)
8216 player->is_sleeping = TRUE;
8217 else if (game.player_boring_delay_fixed +
8218 game.player_boring_delay_random > 0 &&
8219 FrameCounter >= player->frame_counter_bored)
8220 player->is_bored = TRUE;
8222 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8223 player->is_bored ? ACTION_BORING :
8226 if (player->is_sleeping)
8228 if (player->num_special_action_sleeping > 0)
8230 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8232 int last_special_action = player->special_action_sleeping;
8233 int num_special_action = player->num_special_action_sleeping;
8234 int special_action =
8235 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8236 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8237 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8238 last_special_action + 1 : ACTION_SLEEPING);
8239 int special_graphic =
8240 el_act_dir2img(player->element_nr, special_action, move_dir);
8242 player->anim_delay_counter =
8243 graphic_info[special_graphic].anim_delay_fixed +
8244 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8245 player->post_delay_counter =
8246 graphic_info[special_graphic].post_delay_fixed +
8247 SimpleRND(graphic_info[special_graphic].post_delay_random);
8249 player->special_action_sleeping = special_action;
8252 if (player->anim_delay_counter > 0)
8254 player->action_waiting = player->special_action_sleeping;
8255 player->anim_delay_counter--;
8257 else if (player->post_delay_counter > 0)
8259 player->post_delay_counter--;
8263 else if (player->is_bored)
8265 if (player->num_special_action_bored > 0)
8267 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8269 int special_action =
8270 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8271 int special_graphic =
8272 el_act_dir2img(player->element_nr, special_action, move_dir);
8274 player->anim_delay_counter =
8275 graphic_info[special_graphic].anim_delay_fixed +
8276 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8277 player->post_delay_counter =
8278 graphic_info[special_graphic].post_delay_fixed +
8279 SimpleRND(graphic_info[special_graphic].post_delay_random);
8281 player->special_action_bored = special_action;
8284 if (player->anim_delay_counter > 0)
8286 player->action_waiting = player->special_action_bored;
8287 player->anim_delay_counter--;
8289 else if (player->post_delay_counter > 0)
8291 player->post_delay_counter--;
8296 else if (last_waiting) /* waiting -> not waiting */
8298 player->is_waiting = FALSE;
8299 player->is_bored = FALSE;
8300 player->is_sleeping = FALSE;
8302 player->frame_counter_bored = -1;
8303 player->frame_counter_sleeping = -1;
8305 player->anim_delay_counter = 0;
8306 player->post_delay_counter = 0;
8308 player->action_waiting = ACTION_DEFAULT;
8310 player->special_action_bored = ACTION_DEFAULT;
8311 player->special_action_sleeping = ACTION_DEFAULT;
8316 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8319 static byte stored_player_action[MAX_PLAYERS];
8320 static int num_stored_actions = 0;
8322 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8323 int left = player_action & JOY_LEFT;
8324 int right = player_action & JOY_RIGHT;
8325 int up = player_action & JOY_UP;
8326 int down = player_action & JOY_DOWN;
8327 int button1 = player_action & JOY_BUTTON_1;
8328 int button2 = player_action & JOY_BUTTON_2;
8329 int dx = (left ? -1 : right ? 1 : 0);
8330 int dy = (up ? -1 : down ? 1 : 0);
8333 stored_player_action[player->index_nr] = 0;
8334 num_stored_actions++;
8338 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8341 if (!player->active || tape.pausing)
8345 printf("::: [%d %d %d %d] [%d %d]\n",
8346 left, right, up, down, button1, button2);
8352 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8357 if (player->MovPos == 0)
8358 CheckGravityMovement(player);
8361 snapped = SnapField(player, dx, dy);
8365 dropped = DropElement(player);
8367 moved = MovePlayer(player, dx, dy);
8370 if (tape.single_step && tape.recording && !tape.pausing)
8372 if (button1 || (dropped && !moved))
8374 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8375 SnapField(player, 0, 0); /* stop snapping */
8379 SetPlayerWaiting(player, FALSE);
8382 return player_action;
8384 stored_player_action[player->index_nr] = player_action;
8390 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8393 /* no actions for this player (no input at player's configured device) */
8395 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8396 SnapField(player, 0, 0);
8397 CheckGravityMovementWhenNotMoving(player);
8399 if (player->MovPos == 0)
8400 SetPlayerWaiting(player, TRUE);
8402 if (player->MovPos == 0) /* needed for tape.playing */
8403 player->is_moving = FALSE;
8405 player->is_dropping = FALSE;
8411 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8413 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8415 TapeRecordAction(stored_player_action);
8416 num_stored_actions = 0;
8423 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8425 static byte stored_player_action[MAX_PLAYERS];
8426 static int num_stored_actions = 0;
8427 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8428 int left = player_action & JOY_LEFT;
8429 int right = player_action & JOY_RIGHT;
8430 int up = player_action & JOY_UP;
8431 int down = player_action & JOY_DOWN;
8432 int button1 = player_action & JOY_BUTTON_1;
8433 int button2 = player_action & JOY_BUTTON_2;
8434 int dx = (left ? -1 : right ? 1 : 0);
8435 int dy = (up ? -1 : down ? 1 : 0);
8437 stored_player_action[player->index_nr] = 0;
8438 num_stored_actions++;
8440 printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8442 if (!player->active || tape.pausing)
8447 printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8450 snapped = SnapField(player, dx, dy);
8454 dropped = DropElement(player);
8456 moved = MovePlayer(player, dx, dy);
8459 if (tape.single_step && tape.recording && !tape.pausing)
8461 if (button1 || (dropped && !moved))
8463 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8464 SnapField(player, 0, 0); /* stop snapping */
8468 stored_player_action[player->index_nr] = player_action;
8472 printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8474 /* no actions for this player (no input at player's configured device) */
8476 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8477 SnapField(player, 0, 0);
8478 CheckGravityMovementWhenNotMoving(player);
8480 if (player->MovPos == 0)
8481 InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8483 if (player->MovPos == 0) /* needed for tape.playing */
8484 player->is_moving = FALSE;
8487 if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8489 printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8491 TapeRecordAction(stored_player_action);
8492 num_stored_actions = 0;
8497 void AdvanceFrameAndPlayerCounters(int player_nr)
8501 /* advance frame counters (global frame counter and time frame counter) */
8505 /* advance player counters (counters for move delay, move animation etc.) */
8506 for (i = 0; i < MAX_PLAYERS; i++)
8508 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8510 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
8512 if (!advance_player_counters) /* not all players may be affected */
8515 stored_player[i].Frame += move_frames;
8517 if (stored_player[i].MovPos != 0)
8518 stored_player[i].StepFrame += move_frames;
8520 #if USE_NEW_MOVE_DELAY
8521 if (stored_player[i].move_delay > 0)
8522 stored_player[i].move_delay--;
8525 #if USE_NEW_PUSH_DELAY
8526 /* due to bugs in previous versions, counter must count up, not down */
8527 if (stored_player[i].push_delay != -1)
8528 stored_player[i].push_delay++;
8531 if (stored_player[i].drop_delay > 0)
8532 stored_player[i].drop_delay--;
8538 static unsigned long action_delay = 0;
8539 unsigned long action_delay_value;
8540 int magic_wall_x = 0, magic_wall_y = 0;
8541 int i, x, y, element, graphic;
8542 byte *recorded_player_action;
8543 byte summarized_player_action = 0;
8545 byte tape_action[MAX_PLAYERS];
8548 if (game_status != GAME_MODE_PLAYING)
8551 action_delay_value =
8552 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8554 if (tape.playing && tape.warp_forward && !tape.pausing)
8555 action_delay_value = 0;
8557 /* ---------- main game synchronization point ---------- */
8559 WaitUntilDelayReached(&action_delay, action_delay_value);
8561 if (network_playing && !network_player_action_received)
8565 printf("DEBUG: try to get network player actions in time\n");
8569 #if defined(NETWORK_AVALIABLE)
8570 /* last chance to get network player actions without main loop delay */
8574 if (game_status != GAME_MODE_PLAYING)
8577 if (!network_player_action_received)
8581 printf("DEBUG: failed to get network player actions in time\n");
8592 printf("::: getting new tape action [%d]\n", FrameCounter);
8595 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8598 if (recorded_player_action == NULL && tape.pausing)
8603 printf("::: %d\n", stored_player[0].action);
8607 if (recorded_player_action != NULL)
8608 for (i = 0; i < MAX_PLAYERS; i++)
8609 stored_player[i].action = recorded_player_action[i];
8612 for (i = 0; i < MAX_PLAYERS; i++)
8614 summarized_player_action |= stored_player[i].action;
8616 if (!network_playing)
8617 stored_player[i].effective_action = stored_player[i].action;
8620 #if defined(NETWORK_AVALIABLE)
8621 if (network_playing)
8622 SendToServer_MovePlayer(summarized_player_action);
8625 if (!options.network && !setup.team_mode)
8626 local_player->effective_action = summarized_player_action;
8629 if (recorded_player_action != NULL)
8630 for (i = 0; i < MAX_PLAYERS; i++)
8631 stored_player[i].effective_action = recorded_player_action[i];
8635 for (i = 0; i < MAX_PLAYERS; i++)
8637 tape_action[i] = stored_player[i].effective_action;
8639 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8640 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8643 /* only save actions from input devices, but not programmed actions */
8645 TapeRecordAction(tape_action);
8648 for (i = 0; i < MAX_PLAYERS; i++)
8650 int actual_player_action = stored_player[i].effective_action;
8653 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8654 - rnd_equinox_tetrachloride 048
8655 - rnd_equinox_tetrachloride_ii 096
8656 - rnd_emanuel_schmieg 002
8657 - doctor_sloan_ww 001, 020
8659 if (stored_player[i].MovPos == 0)
8660 CheckGravityMovement(&stored_player[i]);
8664 /* overwrite programmed action with tape action */
8665 if (stored_player[i].programmed_action)
8666 actual_player_action = stored_player[i].programmed_action;
8670 if (stored_player[i].programmed_action)
8671 printf("::: %d\n", stored_player[i].programmed_action);
8674 if (recorded_player_action)
8677 if (stored_player[i].programmed_action &&
8678 stored_player[i].programmed_action != recorded_player_action[i])
8679 printf("::: %d: %d <-> %d\n", i,
8680 stored_player[i].programmed_action, recorded_player_action[i]);
8684 actual_player_action = recorded_player_action[i];
8689 /* overwrite tape action with programmed action */
8690 if (stored_player[i].programmed_action)
8691 actual_player_action = stored_player[i].programmed_action;
8696 printf("::: action: %d: %x [%d]\n",
8697 stored_player[i].MovPos, actual_player_action, FrameCounter);
8701 PlayerActions(&stored_player[i], actual_player_action);
8703 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8705 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8706 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8709 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8714 TapeRecordAction(tape_action);
8717 network_player_action_received = FALSE;
8719 ScrollScreen(NULL, SCROLL_GO_ON);
8725 for (i = 0; i < MAX_PLAYERS; i++)
8726 stored_player[i].Frame++;
8730 /* for backwards compatibility, the following code emulates a fixed bug that
8731 occured when pushing elements (causing elements that just made their last
8732 pushing step to already (if possible) make their first falling step in the
8733 same game frame, which is bad); this code is also needed to use the famous
8734 "spring push bug" which is used in older levels and might be wanted to be
8735 used also in newer levels, but in this case the buggy pushing code is only
8736 affecting the "spring" element and no other elements */
8739 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8741 if (game.engine_version < VERSION_IDENT(2,2,0,7))
8744 for (i = 0; i < MAX_PLAYERS; i++)
8746 struct PlayerInfo *player = &stored_player[i];
8751 if (player->active && player->is_pushing && player->is_moving &&
8753 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8754 Feld[x][y] == EL_SPRING))
8756 if (player->active && player->is_pushing && player->is_moving &&
8760 ContinueMoving(x, y);
8762 /* continue moving after pushing (this is actually a bug) */
8763 if (!IS_MOVING(x, y))
8772 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8774 Changed[x][y] = CE_BITMASK_DEFAULT;
8775 ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8777 #if USE_NEW_BLOCK_STYLE
8778 /* this must be handled before main playfield loop */
8779 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8782 if (MovDelay[x][y] <= 0)
8788 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8790 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8791 printf("GameActions(): This should never happen!\n");
8793 ChangePage[x][y] = -1;
8798 if (WasJustMoving[x][y] > 0)
8799 WasJustMoving[x][y]--;
8800 if (WasJustFalling[x][y] > 0)
8801 WasJustFalling[x][y]--;
8802 if (CheckCollision[x][y] > 0)
8803 CheckCollision[x][y]--;
8808 /* reset finished pushing action (not done in ContinueMoving() to allow
8809 continous pushing animation for elements with zero push delay) */
8810 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8812 ResetGfxAnimation(x, y);
8813 DrawLevelField(x, y);
8818 if (IS_BLOCKED(x, y))
8822 Blocked2Moving(x, y, &oldx, &oldy);
8823 if (!IS_MOVING(oldx, oldy))
8825 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8826 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8827 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8828 printf("GameActions(): This should never happen!\n");
8834 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8836 element = Feld[x][y];
8838 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8840 graphic = el2img(element);
8846 printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8848 element = graphic = 0;
8852 if (graphic_info[graphic].anim_global_sync)
8853 GfxFrame[x][y] = FrameCounter;
8855 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8856 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8857 ResetRandomAnimationValue(x, y);
8859 SetRandomAnimationValue(x, y);
8862 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8865 if (IS_INACTIVE(element))
8867 if (IS_ANIMATED(graphic))
8868 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8874 /* this may take place after moving, so 'element' may have changed */
8876 if (IS_CHANGING(x, y))
8878 if (IS_CHANGING(x, y) &&
8879 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8883 ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8884 element_info[element].event_page_nr[CE_DELAY]);
8886 ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8889 element = Feld[x][y];
8890 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8894 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8899 element = Feld[x][y];
8900 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8902 if (element == EL_MOLE)
8903 printf("::: %d, %d, %d [%d]\n",
8904 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8908 if (element == EL_YAMYAM)
8909 printf("::: %d, %d, %d\n",
8910 IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8914 if (IS_ANIMATED(graphic) &&
8918 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8921 if (element == EL_BUG)
8922 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8926 if (element == EL_MOLE)
8927 printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8931 if (IS_GEM(element) || element == EL_SP_INFOTRON)
8932 EdelsteinFunkeln(x, y);
8934 else if ((element == EL_ACID ||
8935 element == EL_EXIT_OPEN ||
8936 element == EL_SP_EXIT_OPEN ||
8937 element == EL_SP_TERMINAL ||
8938 element == EL_SP_TERMINAL_ACTIVE ||
8939 element == EL_EXTRA_TIME ||
8940 element == EL_SHIELD_NORMAL ||
8941 element == EL_SHIELD_DEADLY) &&
8942 IS_ANIMATED(graphic))
8943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8944 else if (IS_MOVING(x, y))
8945 ContinueMoving(x, y);
8946 else if (IS_ACTIVE_BOMB(element))
8947 CheckDynamite(x, y);
8949 else if (element == EL_EXPLOSION && !game.explosions_delayed)
8950 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8952 else if (element == EL_AMOEBA_GROWING)
8953 AmoebeWaechst(x, y);
8954 else if (element == EL_AMOEBA_SHRINKING)
8955 AmoebaDisappearing(x, y);
8957 #if !USE_NEW_AMOEBA_CODE
8958 else if (IS_AMOEBALIVE(element))
8959 AmoebeAbleger(x, y);
8962 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8964 else if (element == EL_EXIT_CLOSED)
8966 else if (element == EL_SP_EXIT_CLOSED)
8968 else if (element == EL_EXPANDABLE_WALL_GROWING)
8970 else if (element == EL_EXPANDABLE_WALL ||
8971 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8972 element == EL_EXPANDABLE_WALL_VERTICAL ||
8973 element == EL_EXPANDABLE_WALL_ANY)
8975 else if (element == EL_FLAMES)
8976 CheckForDragon(x, y);
8978 else if (IS_AUTO_CHANGING(element))
8979 ChangeElement(x, y);
8981 else if (element == EL_EXPLOSION)
8982 ; /* drawing of correct explosion animation is handled separately */
8983 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8984 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8987 /* this may take place after moving, so 'element' may have changed */
8988 if (IS_AUTO_CHANGING(Feld[x][y]))
8989 ChangeElement(x, y);
8992 if (IS_BELT_ACTIVE(element))
8993 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8995 if (game.magic_wall_active)
8997 int jx = local_player->jx, jy = local_player->jy;
8999 /* play the element sound at the position nearest to the player */
9000 if ((element == EL_MAGIC_WALL_FULL ||
9001 element == EL_MAGIC_WALL_ACTIVE ||
9002 element == EL_MAGIC_WALL_EMPTYING ||
9003 element == EL_BD_MAGIC_WALL_FULL ||
9004 element == EL_BD_MAGIC_WALL_ACTIVE ||
9005 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9006 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9014 #if USE_NEW_AMOEBA_CODE
9015 /* new experimental amoeba growth stuff */
9017 if (!(FrameCounter % 8))
9020 static unsigned long random = 1684108901;
9022 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9025 x = (random >> 10) % lev_fieldx;
9026 y = (random >> 20) % lev_fieldy;
9028 x = RND(lev_fieldx);
9029 y = RND(lev_fieldy);
9031 element = Feld[x][y];
9034 if (!IS_PLAYER(x,y) &&
9035 (element == EL_EMPTY ||
9036 CAN_GROW_INTO(element) ||
9037 element == EL_QUICKSAND_EMPTY ||
9038 element == EL_ACID_SPLASH_LEFT ||
9039 element == EL_ACID_SPLASH_RIGHT))
9041 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9042 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9043 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9044 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9045 Feld[x][y] = EL_AMOEBA_DROP;
9048 /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9049 if (!IS_PLAYER(x,y) &&
9050 (element == EL_EMPTY ||
9051 element == EL_SAND ||
9052 element == EL_QUICKSAND_EMPTY ||
9053 element == EL_ACID_SPLASH_LEFT ||
9054 element == EL_ACID_SPLASH_RIGHT))
9056 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9057 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9058 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9059 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9060 Feld[x][y] = EL_AMOEBA_DROP;
9064 random = random * 129 + 1;
9070 if (game.explosions_delayed)
9073 game.explosions_delayed = FALSE;
9075 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9077 element = Feld[x][y];
9079 if (ExplodeField[x][y])
9080 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9081 else if (element == EL_EXPLOSION)
9082 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9084 ExplodeField[x][y] = EX_TYPE_NONE;
9087 game.explosions_delayed = TRUE;
9090 if (game.magic_wall_active)
9092 if (!(game.magic_wall_time_left % 4))
9094 int element = Feld[magic_wall_x][magic_wall_y];
9096 if (element == EL_BD_MAGIC_WALL_FULL ||
9097 element == EL_BD_MAGIC_WALL_ACTIVE ||
9098 element == EL_BD_MAGIC_WALL_EMPTYING)
9099 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9101 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9104 if (game.magic_wall_time_left > 0)
9106 game.magic_wall_time_left--;
9107 if (!game.magic_wall_time_left)
9109 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9111 element = Feld[x][y];
9113 if (element == EL_MAGIC_WALL_ACTIVE ||
9114 element == EL_MAGIC_WALL_FULL)
9116 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9117 DrawLevelField(x, y);
9119 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9120 element == EL_BD_MAGIC_WALL_FULL)
9122 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9123 DrawLevelField(x, y);
9127 game.magic_wall_active = FALSE;
9132 if (game.light_time_left > 0)
9134 game.light_time_left--;
9136 if (game.light_time_left == 0)
9137 RedrawAllLightSwitchesAndInvisibleElements();
9140 if (game.timegate_time_left > 0)
9142 game.timegate_time_left--;
9144 if (game.timegate_time_left == 0)
9145 CloseAllOpenTimegates();
9148 for (i = 0; i < MAX_PLAYERS; i++)
9150 struct PlayerInfo *player = &stored_player[i];
9152 if (SHIELD_ON(player))
9154 if (player->shield_deadly_time_left)
9155 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9156 else if (player->shield_normal_time_left)
9157 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9161 if (TimeFrames >= FRAMES_PER_SECOND)
9166 for (i = 0; i < MAX_PLAYERS; i++)
9168 struct PlayerInfo *player = &stored_player[i];
9170 if (SHIELD_ON(player))
9172 player->shield_normal_time_left--;
9174 if (player->shield_deadly_time_left > 0)
9175 player->shield_deadly_time_left--;
9179 if (!level.use_step_counter)
9187 if (TimeLeft <= 10 && setup.time_limit)
9188 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9190 DrawGameValue_Time(TimeLeft);
9192 if (!TimeLeft && setup.time_limit)
9193 for (i = 0; i < MAX_PLAYERS; i++)
9194 KillHero(&stored_player[i]);
9196 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9197 DrawGameValue_Time(TimePlayed);
9200 if (tape.recording || tape.playing)
9201 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9205 PlayAllPlayersSound();
9207 if (options.debug) /* calculate frames per second */
9209 static unsigned long fps_counter = 0;
9210 static int fps_frames = 0;
9211 unsigned long fps_delay_ms = Counter() - fps_counter;
9215 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9217 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9220 fps_counter = Counter();
9223 redraw_mask |= REDRAW_FPS;
9227 if (stored_player[0].jx != stored_player[0].last_jx ||
9228 stored_player[0].jy != stored_player[0].last_jy)
9229 printf("::: %d, %d, %d, %d, %d\n",
9230 stored_player[0].MovDir,
9231 stored_player[0].MovPos,
9232 stored_player[0].GfxPos,
9233 stored_player[0].Frame,
9234 stored_player[0].StepFrame);
9237 #if USE_NEW_MOVE_DELAY
9238 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9243 for (i = 0; i < MAX_PLAYERS; i++)
9246 MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
9248 stored_player[i].Frame += move_frames;
9250 if (stored_player[i].MovPos != 0)
9251 stored_player[i].StepFrame += move_frames;
9253 #if USE_NEW_MOVE_DELAY
9254 if (stored_player[i].move_delay > 0)
9255 stored_player[i].move_delay--;
9258 if (stored_player[i].drop_delay > 0)
9259 stored_player[i].drop_delay--;
9264 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9266 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9268 local_player->show_envelope = 0;
9272 #if USE_NEW_RANDOMIZE
9273 /* use random number generator in every frame to make it less predictable */
9274 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9279 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9281 int min_x = x, min_y = y, max_x = x, max_y = y;
9284 for (i = 0; i < MAX_PLAYERS; i++)
9286 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9288 if (!stored_player[i].active || &stored_player[i] == player)
9291 min_x = MIN(min_x, jx);
9292 min_y = MIN(min_y, jy);
9293 max_x = MAX(max_x, jx);
9294 max_y = MAX(max_y, jy);
9297 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9300 static boolean AllPlayersInVisibleScreen()
9304 for (i = 0; i < MAX_PLAYERS; i++)
9306 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9308 if (!stored_player[i].active)
9311 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9318 void ScrollLevel(int dx, int dy)
9320 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9323 BlitBitmap(drawto_field, drawto_field,
9324 FX + TILEX * (dx == -1) - softscroll_offset,
9325 FY + TILEY * (dy == -1) - softscroll_offset,
9326 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9327 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9328 FX + TILEX * (dx == 1) - softscroll_offset,
9329 FY + TILEY * (dy == 1) - softscroll_offset);
9333 x = (dx == 1 ? BX1 : BX2);
9334 for (y = BY1; y <= BY2; y++)
9335 DrawScreenField(x, y);
9340 y = (dy == 1 ? BY1 : BY2);
9341 for (x = BX1; x <= BX2; x++)
9342 DrawScreenField(x, y);
9345 redraw_mask |= REDRAW_FIELD;
9349 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9351 int nextx = x + dx, nexty = y + dy;
9352 int element = Feld[x][y];
9355 element != EL_SP_PORT_LEFT &&
9356 element != EL_SP_GRAVITY_PORT_LEFT &&
9357 element != EL_SP_PORT_HORIZONTAL &&
9358 element != EL_SP_PORT_ANY) ||
9360 element != EL_SP_PORT_RIGHT &&
9361 element != EL_SP_GRAVITY_PORT_RIGHT &&
9362 element != EL_SP_PORT_HORIZONTAL &&
9363 element != EL_SP_PORT_ANY) ||
9365 element != EL_SP_PORT_UP &&
9366 element != EL_SP_GRAVITY_PORT_UP &&
9367 element != EL_SP_PORT_VERTICAL &&
9368 element != EL_SP_PORT_ANY) ||
9370 element != EL_SP_PORT_DOWN &&
9371 element != EL_SP_GRAVITY_PORT_DOWN &&
9372 element != EL_SP_PORT_VERTICAL &&
9373 element != EL_SP_PORT_ANY) ||
9374 !IN_LEV_FIELD(nextx, nexty) ||
9375 !IS_FREE(nextx, nexty))
9382 static boolean canFallDown(struct PlayerInfo *player)
9384 int jx = player->jx, jy = player->jy;
9386 return (IN_LEV_FIELD(jx, jy + 1) &&
9387 (IS_FREE(jx, jy + 1) ||
9388 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9389 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9390 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9393 static boolean canPassField(int x, int y, int move_dir)
9395 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9396 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9397 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9400 int element = Feld[x][y];
9402 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9403 !CAN_MOVE(element) &&
9404 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9405 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9406 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9409 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9411 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9412 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9413 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9417 int nextx = newx + dx;
9418 int nexty = newy + dy;
9422 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9423 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9425 (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9427 (IS_DIGGABLE(Feld[newx][newy]) ||
9428 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9429 canPassField(newx, newy, move_dir)));
9432 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9433 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9434 (IS_DIGGABLE(Feld[newx][newy]) ||
9435 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9436 canPassField(newx, newy, move_dir)));
9439 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9440 (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9441 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9442 canPassField(newx, newy, move_dir)));
9444 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9445 (IS_DIGGABLE(Feld[newx][newy]) ||
9446 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9447 (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9448 !CAN_MOVE(Feld[newx][newy]) &&
9449 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9450 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9451 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9457 static void CheckGravityMovement(struct PlayerInfo *player)
9459 if (game.gravity && !player->programmed_action)
9462 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9463 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9465 int move_dir_horizontal = player->action & MV_HORIZONTAL;
9466 int move_dir_vertical = player->action & MV_VERTICAL;
9470 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9472 boolean player_is_snapping = player->action & JOY_BUTTON_1;
9475 int jx = player->jx, jy = player->jy;
9477 boolean player_is_moving_to_valid_field =
9478 (!player_is_snapping &&
9479 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9480 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9484 (player->last_move_dir & MV_HORIZONTAL ?
9485 (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9486 (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9490 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9491 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9492 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9493 int new_jx = jx + dx, new_jy = jy + dy;
9494 int nextx = new_jx + dx, nexty = new_jy + dy;
9500 boolean player_can_fall_down = canFallDown(player);
9502 boolean player_can_fall_down =
9503 (IN_LEV_FIELD(jx, jy + 1) &&
9504 (IS_FREE(jx, jy + 1) ||
9505 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9509 boolean player_can_fall_down =
9510 (IN_LEV_FIELD(jx, jy + 1) &&
9511 (IS_FREE(jx, jy + 1)));
9515 boolean player_is_moving_to_valid_field =
9518 !player_is_snapping &&
9522 IN_LEV_FIELD(new_jx, new_jy) &&
9523 (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9524 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9525 element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9526 IN_LEV_FIELD(nextx, nexty) &&
9527 element_info[Feld[nextx][nexty]].access_direction & move_dir))
9529 IN_LEV_FIELD(new_jx, new_jy) &&
9530 (Feld[new_jx][new_jy] == EL_SP_BASE ||
9531 Feld[new_jx][new_jy] == EL_SAND ||
9532 (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9533 canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9534 /* !!! extend EL_SAND to anything diggable !!! */
9540 boolean player_is_standing_on_valid_field =
9541 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9542 (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9546 printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9547 player_can_fall_down,
9548 player_is_standing_on_valid_field,
9549 player_is_moving_to_valid_field,
9550 (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9551 player->effective_action,
9552 player->can_fall_into_acid);
9555 if (player_can_fall_down &&
9557 !player_is_standing_on_valid_field &&
9559 !player_is_moving_to_valid_field)
9562 printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9563 jx, jy, FrameCounter);
9566 player->programmed_action = MV_DOWN;
9571 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9574 return CheckGravityMovement(player);
9577 if (game.gravity && !player->programmed_action)
9579 int jx = player->jx, jy = player->jy;
9580 boolean field_under_player_is_free =
9581 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9582 boolean player_is_standing_on_valid_field =
9583 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9584 (IS_WALKABLE(Feld[jx][jy]) &&
9585 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9587 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9588 player->programmed_action = MV_DOWN;
9594 -----------------------------------------------------------------------------
9595 dx, dy: direction (non-diagonal) to try to move the player to
9596 real_dx, real_dy: direction as read from input device (can be diagonal)
9599 boolean MovePlayerOneStep(struct PlayerInfo *player,
9600 int dx, int dy, int real_dx, int real_dy)
9603 static int trigger_sides[4][2] =
9605 /* enter side leave side */
9606 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9607 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9608 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9609 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9611 int move_direction = (dx == -1 ? MV_LEFT :
9612 dx == +1 ? MV_RIGHT :
9614 dy == +1 ? MV_DOWN : MV_NO_MOVING);
9615 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9616 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9618 int jx = player->jx, jy = player->jy;
9619 int new_jx = jx + dx, new_jy = jy + dy;
9623 if (!player->active || (!dx && !dy))
9624 return MF_NO_ACTION;
9626 player->MovDir = (dx < 0 ? MV_LEFT :
9629 dy > 0 ? MV_DOWN : MV_NO_MOVING);
9631 if (!IN_LEV_FIELD(new_jx, new_jy))
9632 return MF_NO_ACTION;
9634 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9635 return MF_NO_ACTION;
9638 element = MovingOrBlocked2Element(new_jx, new_jy);
9640 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9643 if (DONT_RUN_INTO(element))
9645 if (element == EL_ACID && dx == 0 && dy == 1)
9647 SplashAcid(new_jx, new_jy);
9648 Feld[jx][jy] = EL_PLAYER_1;
9649 InitMovingField(jx, jy, MV_DOWN);
9650 Store[jx][jy] = EL_ACID;
9651 ContinueMoving(jx, jy);
9655 TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9660 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9661 if (can_move != MF_MOVING)
9664 /* check if DigField() has caused relocation of the player */
9665 if (player->jx != jx || player->jy != jy)
9666 return MF_NO_ACTION;
9668 StorePlayer[jx][jy] = 0;
9669 player->last_jx = jx;
9670 player->last_jy = jy;
9671 player->jx = new_jx;
9672 player->jy = new_jy;
9673 StorePlayer[new_jx][new_jy] = player->element_nr;
9676 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9678 player->step_counter++;
9681 player->drop_delay = 0;
9684 PlayerVisit[jx][jy] = FrameCounter;
9686 ScrollPlayer(player, SCROLL_INIT);
9689 if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9691 CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9693 CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9696 if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9698 CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9699 CE_OTHER_GETS_ENTERED, enter_side);
9700 CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9701 CE_ENTERED_BY_PLAYER, enter_side);
9708 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9710 int jx = player->jx, jy = player->jy;
9711 int old_jx = jx, old_jy = jy;
9712 int moved = MF_NO_ACTION;
9715 if (!player->active)
9720 if (player->MovPos == 0)
9722 player->is_moving = FALSE;
9723 player->is_digging = FALSE;
9724 player->is_collecting = FALSE;
9725 player->is_snapping = FALSE;
9726 player->is_pushing = FALSE;
9732 if (!player->active || (!dx && !dy))
9737 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9745 printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9746 player->move_delay + player->move_delay_value);
9749 #if USE_NEW_MOVE_DELAY
9750 if (player->move_delay > 0)
9752 if (!FrameReached(&player->move_delay, player->move_delay_value))
9756 printf("::: can NOT move\n");
9762 if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9763 !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9770 printf("::: COULD move now\n");
9773 #if USE_NEW_MOVE_DELAY
9774 player->move_delay = -1; /* set to "uninitialized" value */
9777 /* store if player is automatically moved to next field */
9778 player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9780 /* remove the last programmed player action */
9781 player->programmed_action = 0;
9785 /* should only happen if pre-1.2 tape recordings are played */
9786 /* this is only for backward compatibility */
9788 int original_move_delay_value = player->move_delay_value;
9791 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9795 /* scroll remaining steps with finest movement resolution */
9796 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9798 while (player->MovPos)
9800 ScrollPlayer(player, SCROLL_GO_ON);
9801 ScrollScreen(NULL, SCROLL_GO_ON);
9803 #if USE_NEW_MOVE_DELAY
9804 AdvanceFrameAndPlayerCounters(player->index_nr);
9813 player->move_delay_value = original_move_delay_value;
9816 if (player->last_move_dir & MV_HORIZONTAL)
9818 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9819 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9823 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9824 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9830 if (moved & MF_MOVING && !ScreenMovPos &&
9831 (player == local_player || !options.network))
9833 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9834 int offset = (setup.scroll_delay ? 3 : 0);
9836 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9838 /* actual player has left the screen -- scroll in that direction */
9839 if (jx != old_jx) /* player has moved horizontally */
9840 scroll_x += (jx - old_jx);
9841 else /* player has moved vertically */
9842 scroll_y += (jy - old_jy);
9846 if (jx != old_jx) /* player has moved horizontally */
9848 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9849 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9850 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9852 /* don't scroll over playfield boundaries */
9853 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9854 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9856 /* don't scroll more than one field at a time */
9857 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9859 /* don't scroll against the player's moving direction */
9860 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9861 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9862 scroll_x = old_scroll_x;
9864 else /* player has moved vertically */
9866 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9867 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9868 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9870 /* don't scroll over playfield boundaries */
9871 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9872 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9874 /* don't scroll more than one field at a time */
9875 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9877 /* don't scroll against the player's moving direction */
9878 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9879 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9880 scroll_y = old_scroll_y;
9884 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9886 if (!options.network && !AllPlayersInVisibleScreen())
9888 scroll_x = old_scroll_x;
9889 scroll_y = old_scroll_y;
9893 ScrollScreen(player, SCROLL_INIT);
9894 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9901 InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9903 if (!(moved & MF_MOVING) && !player->is_pushing)
9908 player->StepFrame = 0;
9910 if (moved & MF_MOVING)
9913 printf("::: REALLY moves now\n");
9916 if (old_jx != jx && old_jy == jy)
9917 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9918 else if (old_jx == jx && old_jy != jy)
9919 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9921 DrawLevelField(jx, jy); /* for "crumbled sand" */
9923 player->last_move_dir = player->MovDir;
9924 player->is_moving = TRUE;
9926 player->is_snapping = FALSE;
9930 player->is_switching = FALSE;
9933 player->is_dropping = FALSE;
9937 /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9940 if (game.engine_version < VERSION_IDENT(3,1,0,0))
9943 int move_direction = player->MovDir;
9945 int enter_side = MV_DIR_OPPOSITE(move_direction);
9946 int leave_side = move_direction;
9948 static int trigger_sides[4][2] =
9950 /* enter side leave side */
9951 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
9952 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
9953 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
9954 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
9956 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9957 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9959 int old_element = Feld[old_jx][old_jy];
9960 int new_element = Feld[jx][jy];
9963 /* !!! TEST ONLY !!! */
9964 if (IS_CUSTOM_ELEMENT(old_element))
9965 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9967 player->index_bit, leave_side);
9969 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9971 player->index_bit, leave_side);
9973 if (IS_CUSTOM_ELEMENT(new_element))
9974 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9975 player->index_bit, enter_side);
9977 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9978 CE_OTHER_GETS_ENTERED,
9979 player->index_bit, enter_side);
9989 CheckGravityMovementWhenNotMoving(player);
9992 player->last_move_dir = MV_NO_MOVING;
9994 player->is_moving = FALSE;
9996 #if USE_NEW_MOVE_STYLE
9997 /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9998 /* ensure that the player is also allowed to move in the next frame */
9999 /* (currently, the player is forced to wait eight frames before he can try
10002 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10003 player->move_delay = 0; /* allow direct movement in the next frame */
10007 #if USE_NEW_MOVE_DELAY
10008 if (player->move_delay == -1) /* not yet initialized by DigField() */
10009 player->move_delay = player->move_delay_value;
10012 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10014 TestIfHeroTouchesBadThing(jx, jy);
10015 TestIfPlayerTouchesCustomElement(jx, jy);
10018 if (!player->active)
10019 RemoveHero(player);
10024 void ScrollPlayer(struct PlayerInfo *player, int mode)
10026 int jx = player->jx, jy = player->jy;
10027 int last_jx = player->last_jx, last_jy = player->last_jy;
10028 int move_stepsize = TILEX / player->move_delay_value;
10030 if (!player->active || !player->MovPos)
10033 if (mode == SCROLL_INIT)
10035 player->actual_frame_counter = FrameCounter;
10036 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10039 printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10041 last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10042 player->block_delay);
10045 #if USE_NEW_BLOCK_STYLE
10048 if (player->block_delay <= 0)
10049 printf("::: ALERT! block_delay == %d\n", player->block_delay);
10052 if (player->block_delay > 0 &&
10053 Feld[last_jx][last_jy] == EL_EMPTY)
10055 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10056 MovDelay[last_jx][last_jy] = player->block_delay + 1;
10059 #if USE_NEW_MOVE_STYLE
10060 if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10061 player->block_last_field) &&
10062 Feld[last_jx][last_jy] == EL_EMPTY)
10063 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10065 if (Feld[last_jx][last_jy] == EL_EMPTY)
10066 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10071 DrawPlayer(player);
10076 else if (!FrameReached(&player->actual_frame_counter, 1))
10079 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10080 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10082 #if USE_NEW_BLOCK_STYLE
10084 if (!player->block_last_field &&
10085 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10087 RemoveField(last_jx, last_jy);
10089 Feld[last_jx][last_jy] = EL_EMPTY;
10093 /* before DrawPlayer() to draw correct player graphic for this case */
10094 if (player->MovPos == 0)
10095 CheckGravityMovement(player);
10098 DrawPlayer(player); /* needed here only to cleanup last field */
10101 if (player->MovPos == 0) /* player reached destination field */
10104 if (player->move_delay_reset_counter > 0)
10106 player->move_delay_reset_counter--;
10108 if (player->move_delay_reset_counter == 0)
10110 /* continue with normal speed after quickly moving through gate */
10111 HALVE_PLAYER_SPEED(player);
10113 /* be able to make the next move without delay */
10114 player->move_delay = 0;
10118 if (IS_PASSABLE(Feld[last_jx][last_jy]))
10120 /* continue with normal speed after quickly moving through gate */
10121 HALVE_PLAYER_SPEED(player);
10123 /* be able to make the next move without delay */
10124 player->move_delay = 0;
10128 #if USE_NEW_BLOCK_STYLE
10130 if (player->block_last_field &&
10131 Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10133 RemoveField(last_jx, last_jy);
10135 Feld[last_jx][last_jy] = EL_EMPTY;
10139 player->last_jx = jx;
10140 player->last_jy = jy;
10142 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10143 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10144 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10146 DrawPlayer(player); /* needed here only to cleanup last field */
10147 RemoveHero(player);
10149 if (local_player->friends_still_needed == 0 ||
10150 IS_SP_ELEMENT(Feld[jx][jy]))
10151 player->LevelSolved = player->GameOver = TRUE;
10155 /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10156 /* this breaks one level: "machine", level 000 */
10158 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10161 int move_direction = player->MovDir;
10163 int enter_side = MV_DIR_OPPOSITE(move_direction);
10164 int leave_side = move_direction;
10166 static int trigger_sides[4][2] =
10168 /* enter side leave side */
10169 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
10170 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
10171 { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
10172 { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
10174 int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10175 int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10177 int old_jx = last_jx;
10178 int old_jy = last_jy;
10179 int old_element = Feld[old_jx][old_jy];
10180 int new_element = Feld[jx][jy];
10183 /* !!! TEST ONLY !!! */
10184 if (IS_CUSTOM_ELEMENT(old_element))
10185 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10187 player->index_bit, leave_side);
10189 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10190 CE_OTHER_GETS_LEFT,
10191 player->index_bit, leave_side);
10193 if (IS_CUSTOM_ELEMENT(new_element))
10194 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10195 player->index_bit, enter_side);
10197 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10198 CE_OTHER_GETS_ENTERED,
10199 player->index_bit, enter_side);
10205 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10207 TestIfHeroTouchesBadThing(jx, jy);
10208 TestIfPlayerTouchesCustomElement(jx, jy);
10211 /* needed because pushed element has not yet reached its destination,
10212 so it would trigger a change event at its previous field location */
10213 if (!player->is_pushing)
10215 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10218 if (!player->active)
10219 RemoveHero(player);
10222 if (level.use_step_counter)
10232 if (TimeLeft <= 10 && setup.time_limit)
10233 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10235 DrawGameValue_Time(TimeLeft);
10237 if (!TimeLeft && setup.time_limit)
10238 for (i = 0; i < MAX_PLAYERS; i++)
10239 KillHero(&stored_player[i]);
10241 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10242 DrawGameValue_Time(TimePlayed);
10245 if (tape.single_step && tape.recording && !tape.pausing &&
10246 !player->programmed_action)
10247 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10251 void ScrollScreen(struct PlayerInfo *player, int mode)
10253 static unsigned long screen_frame_counter = 0;
10255 if (mode == SCROLL_INIT)
10257 /* set scrolling step size according to actual player's moving speed */
10258 ScrollStepSize = TILEX / player->move_delay_value;
10260 screen_frame_counter = FrameCounter;
10261 ScreenMovDir = player->MovDir;
10262 ScreenMovPos = player->MovPos;
10263 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10266 else if (!FrameReached(&screen_frame_counter, 1))
10271 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10272 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10273 redraw_mask |= REDRAW_FIELD;
10276 ScreenMovDir = MV_NO_MOVING;
10279 void TestIfPlayerTouchesCustomElement(int x, int y)
10281 static int xy[4][2] =
10288 static int trigger_sides[4][2] =
10290 /* center side border side */
10291 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10292 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10293 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10294 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10296 static int touch_dir[4] =
10298 MV_LEFT | MV_RIGHT,
10303 int center_element = Feld[x][y]; /* should always be non-moving! */
10306 for (i = 0; i < NUM_DIRECTIONS; i++)
10308 int xx = x + xy[i][0];
10309 int yy = y + xy[i][1];
10310 int center_side = trigger_sides[i][0];
10311 int border_side = trigger_sides[i][1];
10312 int border_element;
10314 if (!IN_LEV_FIELD(xx, yy))
10317 if (IS_PLAYER(x, y))
10319 struct PlayerInfo *player = PLAYERINFO(x, y);
10321 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10322 border_element = Feld[xx][yy]; /* may be moving! */
10323 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10324 border_element = Feld[xx][yy];
10325 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10326 border_element = MovingOrBlocked2Element(xx, yy);
10328 continue; /* center and border element do not touch */
10331 /* !!! TEST ONLY !!! */
10332 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10333 player->index_bit, border_side);
10334 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10335 CE_OTHER_GETS_TOUCHED,
10336 player->index_bit, border_side);
10338 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10339 CE_OTHER_GETS_TOUCHED,
10340 player->index_bit, border_side);
10341 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10342 player->index_bit, border_side);
10345 else if (IS_PLAYER(xx, yy))
10347 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10349 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10351 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10352 continue; /* center and border element do not touch */
10356 /* !!! TEST ONLY !!! */
10357 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10358 player->index_bit, center_side);
10359 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10360 CE_OTHER_GETS_TOUCHED,
10361 player->index_bit, center_side);
10363 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10364 CE_OTHER_GETS_TOUCHED,
10365 player->index_bit, center_side);
10366 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10367 player->index_bit, center_side);
10375 void TestIfElementTouchesCustomElement(int x, int y)
10377 static int xy[4][2] =
10384 static int trigger_sides[4][2] =
10386 /* center side border side */
10387 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10388 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10389 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10390 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10392 static int touch_dir[4] =
10394 MV_LEFT | MV_RIGHT,
10399 boolean change_center_element = FALSE;
10400 int center_element_change_page = 0;
10401 int center_element = Feld[x][y]; /* should always be non-moving! */
10402 int border_trigger_element = EL_UNDEFINED;
10405 for (i = 0; i < NUM_DIRECTIONS; i++)
10407 int xx = x + xy[i][0];
10408 int yy = y + xy[i][1];
10409 int center_side = trigger_sides[i][0];
10410 int border_side = trigger_sides[i][1];
10411 int border_element;
10413 if (!IN_LEV_FIELD(xx, yy))
10416 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10417 border_element = Feld[xx][yy]; /* may be moving! */
10418 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10419 border_element = Feld[xx][yy];
10420 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10421 border_element = MovingOrBlocked2Element(xx, yy);
10423 continue; /* center and border element do not touch */
10425 /* check for change of center element (but change it only once) */
10426 if (IS_CUSTOM_ELEMENT(center_element) &&
10427 HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10428 !change_center_element)
10430 for (j = 0; j < element_info[center_element].num_change_pages; j++)
10432 struct ElementChangeInfo *change =
10433 &element_info[center_element].change_page[j];
10435 if (change->can_change &&
10436 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10437 change->trigger_side & border_side &&
10439 IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10441 change->trigger_element == border_element
10445 change_center_element = TRUE;
10446 center_element_change_page = j;
10447 border_trigger_element = border_element;
10454 /* check for change of border element */
10455 if (IS_CUSTOM_ELEMENT(border_element) &&
10456 HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10458 for (j = 0; j < element_info[border_element].num_change_pages; j++)
10460 struct ElementChangeInfo *change =
10461 &element_info[border_element].change_page[j];
10463 if (change->can_change &&
10464 change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10465 change->trigger_side & center_side &&
10467 IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10469 change->trigger_element == center_element
10474 printf("::: border_element %d, %d\n", x, y);
10477 CheckElementChangeByPage(xx, yy, border_element, center_element,
10478 CE_OTHER_IS_TOUCHING, j);
10485 if (change_center_element)
10488 printf("::: center_element %d, %d\n", x, y);
10491 CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10492 CE_OTHER_IS_TOUCHING, center_element_change_page);
10496 void TestIfElementHitsCustomElement(int x, int y, int direction)
10498 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10499 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10500 int hitx = x + dx, hity = y + dy;
10501 int hitting_element = Feld[x][y];
10502 int touched_element;
10504 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10505 !IS_FREE(hitx, hity) &&
10506 (!IS_MOVING(hitx, hity) ||
10507 MovDir[hitx][hity] != direction ||
10508 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10511 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10515 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10519 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10520 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10522 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10523 CE_HITTING_SOMETHING, direction);
10525 if (IN_LEV_FIELD(hitx, hity))
10527 int opposite_direction = MV_DIR_OPPOSITE(direction);
10528 int hitting_side = direction;
10529 int touched_side = opposite_direction;
10531 int touched_element = MovingOrBlocked2Element(hitx, hity);
10534 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10535 MovDir[hitx][hity] != direction ||
10536 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10545 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10546 CE_HIT_BY_SOMETHING, opposite_direction);
10548 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10549 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10551 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10553 struct ElementChangeInfo *change =
10554 &element_info[hitting_element].change_page[i];
10556 if (change->can_change &&
10557 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10558 change->trigger_side & touched_side &&
10561 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10563 change->trigger_element == touched_element
10567 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10568 CE_OTHER_IS_HITTING, i);
10574 if (IS_CUSTOM_ELEMENT(touched_element) &&
10575 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10577 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10579 struct ElementChangeInfo *change =
10580 &element_info[touched_element].change_page[i];
10582 if (change->can_change &&
10583 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10584 change->trigger_side & hitting_side &&
10586 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10588 change->trigger_element == hitting_element
10592 CheckElementChangeByPage(hitx, hity, touched_element,
10593 hitting_element, CE_OTHER_GETS_HIT, i);
10603 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10605 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10606 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10607 int hitx = x + dx, hity = y + dy;
10608 int hitting_element = Feld[x][y];
10609 int touched_element;
10611 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10612 !IS_FREE(hitx, hity) &&
10613 (!IS_MOVING(hitx, hity) ||
10614 MovDir[hitx][hity] != direction ||
10615 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10618 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10622 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10626 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10627 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10629 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10630 EP_CAN_SMASH_EVERYTHING, direction);
10632 if (IN_LEV_FIELD(hitx, hity))
10634 int opposite_direction = MV_DIR_OPPOSITE(direction);
10635 int hitting_side = direction;
10636 int touched_side = opposite_direction;
10638 int touched_element = MovingOrBlocked2Element(hitx, hity);
10641 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10642 MovDir[hitx][hity] != direction ||
10643 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10652 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10653 CE_SMASHED_BY_SOMETHING, opposite_direction);
10655 if (IS_CUSTOM_ELEMENT(hitting_element) &&
10656 HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10658 for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10660 struct ElementChangeInfo *change =
10661 &element_info[hitting_element].change_page[i];
10663 if (change->can_change &&
10664 change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10665 change->trigger_side & touched_side &&
10668 IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10670 change->trigger_element == touched_element
10674 CheckElementChangeByPage(x, y, hitting_element, touched_element,
10675 CE_OTHER_IS_SMASHING, i);
10681 if (IS_CUSTOM_ELEMENT(touched_element) &&
10682 HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10684 for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10686 struct ElementChangeInfo *change =
10687 &element_info[touched_element].change_page[i];
10689 if (change->can_change &&
10690 change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10691 change->trigger_side & hitting_side &&
10693 IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10695 change->trigger_element == hitting_element
10699 CheckElementChangeByPage(hitx, hity, touched_element,
10700 hitting_element, CE_OTHER_GETS_SMASHED,i);
10710 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10712 int i, kill_x = -1, kill_y = -1;
10713 int bad_element = -1;
10714 static int test_xy[4][2] =
10721 static int test_dir[4] =
10729 for (i = 0; i < NUM_DIRECTIONS; i++)
10731 int test_x, test_y, test_move_dir, test_element;
10733 test_x = good_x + test_xy[i][0];
10734 test_y = good_y + test_xy[i][1];
10736 if (!IN_LEV_FIELD(test_x, test_y))
10740 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10743 test_element = Feld[test_x][test_y];
10745 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10748 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10749 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10751 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10752 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10756 bad_element = test_element;
10762 if (kill_x != -1 || kill_y != -1)
10764 if (IS_PLAYER(good_x, good_y))
10766 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10769 if (player->shield_deadly_time_left > 0 &&
10770 !IS_INDESTRUCTIBLE(bad_element))
10771 Bang(kill_x, kill_y);
10772 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10775 if (player->shield_deadly_time_left > 0)
10776 Bang(kill_x, kill_y);
10777 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10782 Bang(good_x, good_y);
10786 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10788 int i, kill_x = -1, kill_y = -1;
10789 int bad_element = Feld[bad_x][bad_y];
10790 static int test_xy[4][2] =
10797 static int touch_dir[4] =
10799 MV_LEFT | MV_RIGHT,
10804 static int test_dir[4] =
10812 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10815 for (i = 0; i < NUM_DIRECTIONS; i++)
10817 int test_x, test_y, test_move_dir, test_element;
10819 test_x = bad_x + test_xy[i][0];
10820 test_y = bad_y + test_xy[i][1];
10821 if (!IN_LEV_FIELD(test_x, test_y))
10825 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10827 test_element = Feld[test_x][test_y];
10829 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10830 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10832 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10833 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10835 /* good thing is player or penguin that does not move away */
10836 if (IS_PLAYER(test_x, test_y))
10838 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10840 if (bad_element == EL_ROBOT && player->is_moving)
10841 continue; /* robot does not kill player if he is moving */
10843 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10845 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10846 continue; /* center and border element do not touch */
10853 else if (test_element == EL_PENGUIN)
10862 if (kill_x != -1 || kill_y != -1)
10864 if (IS_PLAYER(kill_x, kill_y))
10866 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10869 if (player->shield_deadly_time_left > 0 &&
10870 !IS_INDESTRUCTIBLE(bad_element))
10871 Bang(bad_x, bad_y);
10872 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10875 if (player->shield_deadly_time_left > 0)
10876 Bang(bad_x, bad_y);
10877 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10882 Bang(kill_x, kill_y);
10886 void TestIfHeroTouchesBadThing(int x, int y)
10888 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10891 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10893 TestIfGoodThingHitsBadThing(x, y, move_dir);
10896 void TestIfBadThingTouchesHero(int x, int y)
10898 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10901 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10903 TestIfBadThingHitsGoodThing(x, y, move_dir);
10906 void TestIfFriendTouchesBadThing(int x, int y)
10908 TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10911 void TestIfBadThingTouchesFriend(int x, int y)
10913 TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10916 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10918 int i, kill_x = bad_x, kill_y = bad_y;
10919 static int xy[4][2] =
10927 for (i = 0; i < NUM_DIRECTIONS; i++)
10931 x = bad_x + xy[i][0];
10932 y = bad_y + xy[i][1];
10933 if (!IN_LEV_FIELD(x, y))
10936 element = Feld[x][y];
10937 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10938 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10946 if (kill_x != bad_x || kill_y != bad_y)
10947 Bang(bad_x, bad_y);
10950 void KillHero(struct PlayerInfo *player)
10952 int jx = player->jx, jy = player->jy;
10954 if (!player->active)
10957 /* remove accessible field at the player's position */
10958 Feld[jx][jy] = EL_EMPTY;
10960 /* deactivate shield (else Bang()/Explode() would not work right) */
10961 player->shield_normal_time_left = 0;
10962 player->shield_deadly_time_left = 0;
10968 static void KillHeroUnlessEnemyProtected(int x, int y)
10970 if (!PLAYER_ENEMY_PROTECTED(x, y))
10971 KillHero(PLAYERINFO(x, y));
10974 static void KillHeroUnlessExplosionProtected(int x, int y)
10976 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10977 KillHero(PLAYERINFO(x, y));
10980 void BuryHero(struct PlayerInfo *player)
10982 int jx = player->jx, jy = player->jy;
10984 if (!player->active)
10988 PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10990 PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10992 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10994 player->GameOver = TRUE;
10995 RemoveHero(player);
10998 void RemoveHero(struct PlayerInfo *player)
11000 int jx = player->jx, jy = player->jy;
11001 int i, found = FALSE;
11003 player->present = FALSE;
11004 player->active = FALSE;
11006 if (!ExplodeField[jx][jy])
11007 StorePlayer[jx][jy] = 0;
11009 for (i = 0; i < MAX_PLAYERS; i++)
11010 if (stored_player[i].active)
11014 AllPlayersGone = TRUE;
11021 =============================================================================
11022 checkDiagonalPushing()
11023 -----------------------------------------------------------------------------
11024 check if diagonal input device direction results in pushing of object
11025 (by checking if the alternative direction is walkable, diggable, ...)
11026 =============================================================================
11029 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11030 int x, int y, int real_dx, int real_dy)
11032 int jx, jy, dx, dy, xx, yy;
11034 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11037 /* diagonal direction: check alternative direction */
11042 xx = jx + (dx == 0 ? real_dx : 0);
11043 yy = jy + (dy == 0 ? real_dy : 0);
11045 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11049 =============================================================================
11051 -----------------------------------------------------------------------------
11052 x, y: field next to player (non-diagonal) to try to dig to
11053 real_dx, real_dy: direction as read from input device (can be diagonal)
11054 =============================================================================
11057 int DigField(struct PlayerInfo *player,
11058 int oldx, int oldy, int x, int y,
11059 int real_dx, int real_dy, int mode)
11062 boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11064 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11065 boolean player_was_pushing = player->is_pushing;
11066 int jx = oldx, jy = oldy;
11067 int dx = x - jx, dy = y - jy;
11068 int nextx = x + dx, nexty = y + dy;
11069 int move_direction = (dx == -1 ? MV_LEFT :
11070 dx == +1 ? MV_RIGHT :
11072 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11073 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11075 int dig_side = MV_DIR_OPPOSITE(move_direction);
11077 static int trigger_sides[4] =
11079 CH_SIDE_RIGHT, /* moving left */
11080 CH_SIDE_LEFT, /* moving right */
11081 CH_SIDE_BOTTOM, /* moving up */
11082 CH_SIDE_TOP, /* moving down */
11084 int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11086 int old_element = Feld[jx][jy];
11089 if (is_player) /* function can also be called by EL_PENGUIN */
11091 if (player->MovPos == 0)
11093 player->is_digging = FALSE;
11094 player->is_collecting = FALSE;
11097 if (player->MovPos == 0) /* last pushing move finished */
11098 player->is_pushing = FALSE;
11100 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11102 player->is_switching = FALSE;
11103 #if USE_NEW_PUSH_DELAY
11104 player->push_delay = -1;
11106 player->push_delay = 0;
11109 return MF_NO_ACTION;
11113 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11114 return MF_NO_ACTION;
11119 if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11121 if (IS_TUBE(Feld[jx][jy]) ||
11122 (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11126 int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11127 int tube_leave_directions[][2] =
11129 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11130 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11131 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11132 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
11133 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
11134 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
11135 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
11136 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
11137 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
11138 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
11139 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
11140 { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11143 while (tube_leave_directions[i][0] != tube_element)
11146 if (tube_leave_directions[i][0] == -1) /* should not happen */
11150 if (!(tube_leave_directions[i][1] & move_direction))
11151 return MF_NO_ACTION; /* tube has no opening in this direction */
11156 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11157 old_element = Back[jx][jy];
11161 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11162 return MF_NO_ACTION; /* field has no opening in this direction */
11164 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11165 return MF_NO_ACTION; /* field has no opening in this direction */
11167 element = Feld[x][y];
11169 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11170 return MF_NO_ACTION;
11172 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11173 game.engine_version >= VERSION_IDENT(2,2,0,0))
11174 return MF_NO_ACTION;
11177 if (game.gravity && is_player && !player->is_auto_moving &&
11178 canFallDown(player) && move_direction != MV_DOWN &&
11179 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11180 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11184 if (element == EL_EMPTY_SPACE &&
11185 game.gravity && !player->is_auto_moving &&
11186 canFallDown(player) && move_direction != MV_DOWN)
11187 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11193 case EL_SP_PORT_LEFT:
11194 case EL_SP_PORT_RIGHT:
11195 case EL_SP_PORT_UP:
11196 case EL_SP_PORT_DOWN:
11197 case EL_SP_PORT_HORIZONTAL:
11198 case EL_SP_PORT_VERTICAL:
11199 case EL_SP_PORT_ANY:
11200 case EL_SP_GRAVITY_PORT_LEFT:
11201 case EL_SP_GRAVITY_PORT_RIGHT:
11202 case EL_SP_GRAVITY_PORT_UP:
11203 case EL_SP_GRAVITY_PORT_DOWN:
11205 if (!canEnterSupaplexPort(x, y, dx, dy))
11206 return MF_NO_ACTION;
11209 element != EL_SP_PORT_LEFT &&
11210 element != EL_SP_GRAVITY_PORT_LEFT &&
11211 element != EL_SP_PORT_HORIZONTAL &&
11212 element != EL_SP_PORT_ANY) ||
11214 element != EL_SP_PORT_RIGHT &&
11215 element != EL_SP_GRAVITY_PORT_RIGHT &&
11216 element != EL_SP_PORT_HORIZONTAL &&
11217 element != EL_SP_PORT_ANY) ||
11219 element != EL_SP_PORT_UP &&
11220 element != EL_SP_GRAVITY_PORT_UP &&
11221 element != EL_SP_PORT_VERTICAL &&
11222 element != EL_SP_PORT_ANY) ||
11224 element != EL_SP_PORT_DOWN &&
11225 element != EL_SP_GRAVITY_PORT_DOWN &&
11226 element != EL_SP_PORT_VERTICAL &&
11227 element != EL_SP_PORT_ANY) ||
11228 !IN_LEV_FIELD(nextx, nexty) ||
11229 !IS_FREE(nextx, nexty))
11230 return MF_NO_ACTION;
11233 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11234 element == EL_SP_GRAVITY_PORT_RIGHT ||
11235 element == EL_SP_GRAVITY_PORT_UP ||
11236 element == EL_SP_GRAVITY_PORT_DOWN)
11237 game.gravity = !game.gravity;
11239 /* automatically move to the next field with double speed */
11240 player->programmed_action = move_direction;
11242 if (player->move_delay_reset_counter == 0)
11244 player->move_delay_reset_counter = 2; /* two double speed steps */
11246 DOUBLE_PLAYER_SPEED(player);
11249 player->move_delay_reset_counter = 2;
11251 DOUBLE_PLAYER_SPEED(player);
11255 printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11258 PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11264 case EL_TUBE_VERTICAL:
11265 case EL_TUBE_HORIZONTAL:
11266 case EL_TUBE_VERTICAL_LEFT:
11267 case EL_TUBE_VERTICAL_RIGHT:
11268 case EL_TUBE_HORIZONTAL_UP:
11269 case EL_TUBE_HORIZONTAL_DOWN:
11270 case EL_TUBE_LEFT_UP:
11271 case EL_TUBE_LEFT_DOWN:
11272 case EL_TUBE_RIGHT_UP:
11273 case EL_TUBE_RIGHT_DOWN:
11276 int tube_enter_directions[][2] =
11278 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11279 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
11280 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
11281 { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
11282 { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
11283 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
11284 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
11285 { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
11286 { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
11287 { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
11288 { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
11289 { -1, MV_NO_MOVING }
11292 while (tube_enter_directions[i][0] != element)
11295 if (tube_enter_directions[i][0] == -1) /* should not happen */
11299 if (!(tube_enter_directions[i][1] & move_direction))
11300 return MF_NO_ACTION; /* tube has no opening in this direction */
11302 PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11310 if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11312 if (IS_WALKABLE(element))
11315 int sound_element = SND_ELEMENT(element);
11316 int sound_action = ACTION_WALKING;
11319 if (!ACCESS_FROM(element, opposite_direction))
11320 return MF_NO_ACTION; /* field not accessible from this direction */
11324 if (element == EL_EMPTY_SPACE &&
11325 game.gravity && !player->is_auto_moving &&
11326 canFallDown(player) && move_direction != MV_DOWN)
11327 return MF_NO_ACTION; /* player cannot walk here due to gravity */
11330 if (IS_GATE(element))
11332 if (!player->key[element - EL_GATE_1])
11333 return MF_NO_ACTION;
11335 else if (IS_GATE_GRAY(element))
11337 if (!player->key[element - EL_GATE_1_GRAY])
11338 return MF_NO_ACTION;
11340 else if (element == EL_EXIT_OPEN ||
11341 element == EL_SP_EXIT_OPEN ||
11342 element == EL_SP_EXIT_OPENING)
11344 sound_action = ACTION_PASSING; /* player is passing exit */
11346 else if (element == EL_EMPTY)
11348 sound_action = ACTION_MOVING; /* nothing to walk on */
11351 /* play sound from background or player, whatever is available */
11352 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11353 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11355 PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11360 else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11362 else if (IS_PASSABLE(element))
11366 if (!canPassField(x, y, move_direction))
11367 return MF_NO_ACTION;
11372 if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11373 !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11374 (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11375 return MF_NO_ACTION;
11377 if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11378 return MF_NO_ACTION;
11383 if (!ACCESS_FROM(element, opposite_direction))
11384 return MF_NO_ACTION; /* field not accessible from this direction */
11386 if (IS_CUSTOM_ELEMENT(element) &&
11387 !ACCESS_FROM(element, opposite_direction))
11388 return MF_NO_ACTION; /* field not accessible from this direction */
11392 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11393 return MF_NO_ACTION;
11398 if (IS_EM_GATE(element))
11400 if (!player->key[element - EL_EM_GATE_1])
11401 return MF_NO_ACTION;
11403 else if (IS_EM_GATE_GRAY(element))
11405 if (!player->key[element - EL_EM_GATE_1_GRAY])
11406 return MF_NO_ACTION;
11408 else if (IS_SP_PORT(element))
11410 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11411 element == EL_SP_GRAVITY_PORT_RIGHT ||
11412 element == EL_SP_GRAVITY_PORT_UP ||
11413 element == EL_SP_GRAVITY_PORT_DOWN)
11414 game.gravity = !game.gravity;
11415 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11416 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11417 element == EL_SP_GRAVITY_ON_PORT_UP ||
11418 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11419 game.gravity = TRUE;
11420 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11421 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11422 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11423 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11424 game.gravity = FALSE;
11427 /* automatically move to the next field with double speed */
11428 player->programmed_action = move_direction;
11430 if (player->move_delay_reset_counter == 0)
11432 player->move_delay_reset_counter = 2; /* two double speed steps */
11434 DOUBLE_PLAYER_SPEED(player);
11437 player->move_delay_reset_counter = 2;
11439 DOUBLE_PLAYER_SPEED(player);
11442 PlayLevelSoundAction(x, y, ACTION_PASSING);
11446 else if (IS_DIGGABLE(element))
11450 if (mode != DF_SNAP)
11453 GfxElement[x][y] = GFX_ELEMENT(element);
11456 (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11458 player->is_digging = TRUE;
11461 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11463 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11464 player->index_bit, dig_side);
11467 if (mode == DF_SNAP)
11468 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11473 else if (IS_COLLECTIBLE(element))
11477 if (is_player && mode != DF_SNAP)
11479 GfxElement[x][y] = element;
11480 player->is_collecting = TRUE;
11483 if (element == EL_SPEED_PILL)
11484 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11485 else if (element == EL_EXTRA_TIME && level.time > 0)
11488 DrawGameValue_Time(TimeLeft);
11490 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11492 player->shield_normal_time_left += 10;
11493 if (element == EL_SHIELD_DEADLY)
11494 player->shield_deadly_time_left += 10;
11496 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11498 if (player->inventory_size < MAX_INVENTORY_SIZE)
11499 player->inventory_element[player->inventory_size++] = element;
11501 DrawGameValue_Dynamite(local_player->inventory_size);
11503 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11505 player->dynabomb_count++;
11506 player->dynabombs_left++;
11508 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11510 player->dynabomb_size++;
11512 else if (element == EL_DYNABOMB_INCREASE_POWER)
11514 player->dynabomb_xl = TRUE;
11516 else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11517 (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11519 int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11520 element - EL_KEY_1 : element - EL_EM_KEY_1);
11522 player->key[key_nr] = TRUE;
11524 DrawGameValue_Keys(player);
11526 redraw_mask |= REDRAW_DOOR_1;
11528 else if (IS_ENVELOPE(element))
11531 player->show_envelope = element;
11533 ShowEnvelope(element - EL_ENVELOPE_1);
11536 else if (IS_DROPPABLE(element) ||
11537 IS_THROWABLE(element)) /* can be collected and dropped */
11541 if (element_info[element].collect_count == 0)
11542 player->inventory_infinite_element = element;
11544 for (i = 0; i < element_info[element].collect_count; i++)
11545 if (player->inventory_size < MAX_INVENTORY_SIZE)
11546 player->inventory_element[player->inventory_size++] = element;
11548 DrawGameValue_Dynamite(local_player->inventory_size);
11550 else if (element_info[element].collect_count > 0)
11552 local_player->gems_still_needed -=
11553 element_info[element].collect_count;
11554 if (local_player->gems_still_needed < 0)
11555 local_player->gems_still_needed = 0;
11557 DrawGameValue_Emeralds(local_player->gems_still_needed);
11560 RaiseScoreElement(element);
11561 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11564 CheckTriggeredElementChangeByPlayer(x, y, element,
11565 CE_OTHER_GETS_COLLECTED,
11566 player->index_bit, dig_side);
11569 if (mode == DF_SNAP)
11570 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11575 else if (IS_PUSHABLE(element))
11577 if (mode == DF_SNAP && element != EL_BD_ROCK)
11578 return MF_NO_ACTION;
11580 if (CAN_FALL(element) && dy)
11581 return MF_NO_ACTION;
11583 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11584 !(element == EL_SPRING && level.use_spring_bug))
11585 return MF_NO_ACTION;
11588 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11589 ((move_direction & MV_VERTICAL &&
11590 ((element_info[element].move_pattern & MV_LEFT &&
11591 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11592 (element_info[element].move_pattern & MV_RIGHT &&
11593 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11594 (move_direction & MV_HORIZONTAL &&
11595 ((element_info[element].move_pattern & MV_UP &&
11596 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11597 (element_info[element].move_pattern & MV_DOWN &&
11598 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11599 return MF_NO_ACTION;
11603 /* do not push elements already moving away faster than player */
11604 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11605 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11606 return MF_NO_ACTION;
11608 if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11609 return MF_NO_ACTION;
11615 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11617 if (player->push_delay_value == -1 || !player_was_pushing)
11618 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11620 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11622 if (player->push_delay_value == -1)
11623 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11626 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11628 if (player->push_delay_value == -1 || !player_was_pushing)
11629 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11632 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11634 if (!player->is_pushing)
11635 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11639 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11640 (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11641 !player_is_pushing))
11642 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11645 if (!player->is_pushing &&
11646 game.engine_version >= VERSION_IDENT(2,2,0,7))
11647 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11651 printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11652 player->push_delay, player->push_delay_value,
11653 FrameCounter, game.engine_version,
11654 player_was_pushing, player->is_pushing,
11655 element, element_info[element].token_name,
11656 GET_NEW_PUSH_DELAY(element));
11659 player->is_pushing = TRUE;
11661 if (!(IN_LEV_FIELD(nextx, nexty) &&
11662 (IS_FREE(nextx, nexty) ||
11663 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11664 IS_SB_ELEMENT(element)))))
11665 return MF_NO_ACTION;
11667 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11668 return MF_NO_ACTION;
11670 #if USE_NEW_PUSH_DELAY
11673 if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11674 printf("::: ALERT: %d, %d [%d / %d]\n",
11675 player->push_delay, player->push_delay2,
11676 FrameCounter, FrameCounter / 50);
11679 if (player->push_delay == -1) /* new pushing; restart delay */
11680 player->push_delay = 0;
11682 if (player->push_delay == 0) /* new pushing; restart delay */
11683 player->push_delay = FrameCounter;
11686 #if USE_NEW_PUSH_DELAY
11688 if ( (player->push_delay > 0) != (!xxx_fr) )
11689 printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11690 player->push_delay,
11691 xxx_pdv2, player->push_delay2, player->push_delay_value,
11692 FrameCounter, FrameCounter / 50);
11696 if (player->push_delay > 0 &&
11697 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11698 element != EL_SPRING && element != EL_BALLOON)
11701 if (player->push_delay < player->push_delay_value &&
11702 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11703 element != EL_SPRING && element != EL_BALLOON)
11707 if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11708 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11709 element != EL_SPRING && element != EL_BALLOON)
11712 /* make sure that there is no move delay before next try to push */
11713 #if USE_NEW_MOVE_DELAY
11714 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11715 player->move_delay = 0;
11717 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11718 player->move_delay = INITIAL_MOVE_DELAY_OFF;
11721 return MF_NO_ACTION;
11725 printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11728 if (IS_SB_ELEMENT(element))
11730 if (element == EL_SOKOBAN_FIELD_FULL)
11732 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11733 local_player->sokobanfields_still_needed++;
11736 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11738 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11739 local_player->sokobanfields_still_needed--;
11742 Feld[x][y] = EL_SOKOBAN_OBJECT;
11744 if (Back[x][y] == Back[nextx][nexty])
11745 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11746 else if (Back[x][y] != 0)
11747 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11750 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11753 if (local_player->sokobanfields_still_needed == 0 &&
11754 game.emulation == EMU_SOKOBAN)
11756 player->LevelSolved = player->GameOver = TRUE;
11757 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11761 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11763 InitMovingField(x, y, move_direction);
11764 GfxAction[x][y] = ACTION_PUSHING;
11766 if (mode == DF_SNAP)
11767 ContinueMoving(x, y);
11769 MovPos[x][y] = (dx != 0 ? dx : dy);
11771 Pushed[x][y] = TRUE;
11772 Pushed[nextx][nexty] = TRUE;
11774 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11775 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11777 player->push_delay_value = -1; /* get new value later */
11779 #if USE_PUSH_BUGFIX
11780 /* now: check for element change _after_ element has been pushed! */
11782 if (game.use_bug_change_when_pushing)
11784 if (game.engine_version < VERSION_IDENT(3,1,0,0))
11787 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11788 player->index_bit, dig_side);
11789 CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11790 player->index_bit, dig_side);
11796 /* check for element change _after_ element has been pushed! */
11800 /* !!! TEST ONLY !!! */
11801 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11802 player->index_bit, dig_side);
11803 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11804 player->index_bit, dig_side);
11806 CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11807 player->index_bit, dig_side);
11808 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11809 player->index_bit, dig_side);
11817 else if (IS_SWITCHABLE(element))
11819 if (PLAYER_SWITCHING(player, x, y))
11821 CheckTriggeredElementChangeByPlayer(x,y, element,
11822 CE_OTHER_GETS_PRESSED,
11823 player->index_bit, dig_side);
11828 player->is_switching = TRUE;
11829 player->switch_x = x;
11830 player->switch_y = y;
11832 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11834 if (element == EL_ROBOT_WHEEL)
11836 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11840 DrawLevelField(x, y);
11842 else if (element == EL_SP_TERMINAL)
11846 for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11848 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11850 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11851 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11854 else if (IS_BELT_SWITCH(element))
11856 ToggleBeltSwitch(x, y);
11858 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11859 element == EL_SWITCHGATE_SWITCH_DOWN)
11861 ToggleSwitchgateSwitch(x, y);
11863 else if (element == EL_LIGHT_SWITCH ||
11864 element == EL_LIGHT_SWITCH_ACTIVE)
11866 ToggleLightSwitch(x, y);
11869 PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11870 SND_LIGHT_SWITCH_ACTIVATING :
11871 SND_LIGHT_SWITCH_DEACTIVATING);
11874 else if (element == EL_TIMEGATE_SWITCH)
11876 ActivateTimegateSwitch(x, y);
11878 else if (element == EL_BALLOON_SWITCH_LEFT ||
11879 element == EL_BALLOON_SWITCH_RIGHT ||
11880 element == EL_BALLOON_SWITCH_UP ||
11881 element == EL_BALLOON_SWITCH_DOWN ||
11882 element == EL_BALLOON_SWITCH_ANY)
11884 if (element == EL_BALLOON_SWITCH_ANY)
11885 game.balloon_dir = move_direction;
11887 game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11888 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11889 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11890 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11893 else if (element == EL_LAMP)
11895 Feld[x][y] = EL_LAMP_ACTIVE;
11896 local_player->lights_still_needed--;
11898 DrawLevelField(x, y);
11900 else if (element == EL_TIME_ORB_FULL)
11902 Feld[x][y] = EL_TIME_ORB_EMPTY;
11904 DrawGameValue_Time(TimeLeft);
11906 DrawLevelField(x, y);
11909 PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11913 CheckTriggeredElementChangeByPlayer(x, y, element,
11914 CE_OTHER_IS_SWITCHING,
11915 player->index_bit, dig_side);
11917 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11918 player->index_bit, dig_side);
11924 if (!PLAYER_SWITCHING(player, x, y))
11926 player->is_switching = TRUE;
11927 player->switch_x = x;
11928 player->switch_y = y;
11931 /* !!! TEST ONLY !!! */
11932 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11933 player->index_bit, dig_side);
11934 CheckTriggeredElementChangeByPlayer(x, y, element,
11935 CE_OTHER_IS_SWITCHING,
11936 player->index_bit, dig_side);
11938 CheckTriggeredElementChangeByPlayer(x, y, element,
11939 CE_OTHER_IS_SWITCHING,
11940 player->index_bit, dig_side);
11941 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11942 player->index_bit, dig_side);
11947 /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11948 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11949 player->index_bit, dig_side);
11950 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11951 player->index_bit, dig_side);
11953 CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11954 player->index_bit, dig_side);
11955 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11956 player->index_bit, dig_side);
11960 return MF_NO_ACTION;
11963 #if USE_NEW_PUSH_DELAY
11964 player->push_delay = -1;
11966 player->push_delay = 0;
11969 if (Feld[x][y] != element) /* really digged/collected something */
11970 player->is_collecting = !player->is_digging;
11975 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11977 int jx = player->jx, jy = player->jy;
11978 int x = jx + dx, y = jy + dy;
11979 int snap_direction = (dx == -1 ? MV_LEFT :
11980 dx == +1 ? MV_RIGHT :
11982 dy == +1 ? MV_DOWN : MV_NO_MOVING);
11985 if (player->MovPos != 0)
11988 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11992 if (!player->active || !IN_LEV_FIELD(x, y))
12000 if (player->MovPos == 0)
12001 player->is_pushing = FALSE;
12003 player->is_snapping = FALSE;
12005 if (player->MovPos == 0)
12007 player->is_moving = FALSE;
12008 player->is_digging = FALSE;
12009 player->is_collecting = FALSE;
12015 if (player->is_snapping)
12018 player->MovDir = snap_direction;
12021 if (player->MovPos == 0)
12024 player->is_moving = FALSE;
12025 player->is_digging = FALSE;
12026 player->is_collecting = FALSE;
12029 player->is_dropping = FALSE;
12031 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12034 player->is_snapping = TRUE;
12037 if (player->MovPos == 0)
12040 player->is_moving = FALSE;
12041 player->is_digging = FALSE;
12042 player->is_collecting = FALSE;
12046 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12047 DrawLevelField(player->last_jx, player->last_jy);
12050 DrawLevelField(x, y);
12059 boolean DropElement(struct PlayerInfo *player)
12061 int old_element, new_element;
12062 int dropx = player->jx, dropy = player->jy;
12063 int drop_direction = player->MovDir;
12065 int drop_side = drop_direction;
12067 static int trigger_sides[4] =
12069 CH_SIDE_LEFT, /* dropping left */
12070 CH_SIDE_RIGHT, /* dropping right */
12071 CH_SIDE_TOP, /* dropping up */
12072 CH_SIDE_BOTTOM, /* dropping down */
12074 int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12076 int drop_element = (player->inventory_size > 0 ?
12077 player->inventory_element[player->inventory_size - 1] :
12078 player->inventory_infinite_element != EL_UNDEFINED ?
12079 player->inventory_infinite_element :
12080 player->dynabombs_left > 0 ?
12081 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12084 if (IS_THROWABLE(drop_element))
12086 dropx += GET_DX_FROM_DIR(drop_direction);
12087 dropy += GET_DY_FROM_DIR(drop_direction);
12089 if (!IN_LEV_FIELD(dropx, dropy))
12093 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12094 new_element = drop_element; /* default: no change when dropping */
12096 /* check if player is active, not moving and ready to drop */
12097 if (!player->active || player->MovPos || player->drop_delay > 0)
12100 /* check if player has anything that can be dropped */
12102 if (new_element == EL_UNDEFINED)
12105 if (player->inventory_size == 0 &&
12106 player->inventory_infinite_element == EL_UNDEFINED &&
12107 player->dynabombs_left == 0)
12111 /* check if anything can be dropped at the current position */
12112 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12115 /* collected custom elements can only be dropped on empty fields */
12117 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12120 if (player->inventory_size > 0 &&
12121 IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12122 && old_element != EL_EMPTY)
12126 if (old_element != EL_EMPTY)
12127 Back[dropx][dropy] = old_element; /* store old element on this field */
12129 ResetGfxAnimation(dropx, dropy);
12130 ResetRandomAnimationValue(dropx, dropy);
12132 if (player->inventory_size > 0 ||
12133 player->inventory_infinite_element != EL_UNDEFINED)
12135 if (player->inventory_size > 0)
12137 player->inventory_size--;
12140 new_element = player->inventory_element[player->inventory_size];
12143 DrawGameValue_Dynamite(local_player->inventory_size);
12145 if (new_element == EL_DYNAMITE)
12146 new_element = EL_DYNAMITE_ACTIVE;
12147 else if (new_element == EL_SP_DISK_RED)
12148 new_element = EL_SP_DISK_RED_ACTIVE;
12151 Feld[dropx][dropy] = new_element;
12153 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12154 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12155 el2img(Feld[dropx][dropy]), 0);
12157 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12160 /* needed if previous element just changed to "empty" in the last frame */
12161 Changed[dropx][dropy] = 0; /* allow another change */
12165 /* !!! TEST ONLY !!! */
12166 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12167 player->index_bit, drop_side);
12168 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12169 CE_OTHER_GETS_DROPPED,
12170 player->index_bit, drop_side);
12172 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12173 CE_OTHER_GETS_DROPPED,
12174 player->index_bit, drop_side);
12175 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12176 player->index_bit, drop_side);
12179 TestIfElementTouchesCustomElement(dropx, dropy);
12181 else /* player is dropping a dyna bomb */
12183 player->dynabombs_left--;
12186 new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12189 Feld[dropx][dropy] = new_element;
12191 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12192 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12193 el2img(Feld[dropx][dropy]), 0);
12195 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12202 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12205 InitField_WithBug1(dropx, dropy, FALSE);
12207 InitField(dropx, dropy, FALSE);
12208 if (CAN_MOVE(Feld[dropx][dropy]))
12209 InitMovDir(dropx, dropy);
12213 new_element = Feld[dropx][dropy]; /* element might have changed */
12215 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12216 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12219 int move_stepsize = element_info[new_element].move_stepsize;
12221 int move_direction, nextx, nexty;
12223 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12224 MovDir[dropx][dropy] = drop_direction;
12226 move_direction = MovDir[dropx][dropy];
12227 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12228 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12231 Changed[dropx][dropy] = 0; /* allow another change */
12232 CheckCollision[dropx][dropy] = 2;
12235 if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12238 WasJustMoving[dropx][dropy] = 3;
12241 InitMovingField(dropx, dropy, move_direction);
12242 ContinueMoving(dropx, dropy);
12247 /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12250 Changed[dropx][dropy] = 0; /* allow another change */
12253 TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12255 CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12256 CE_HITTING_SOMETHING, move_direction);
12264 player->drop_delay = 2 * TILEX / move_stepsize + 1;
12269 player->drop_delay = 8 + 8 + 8;
12273 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12278 player->is_dropping = TRUE;
12284 /* ------------------------------------------------------------------------- */
12285 /* game sound playing functions */
12286 /* ------------------------------------------------------------------------- */
12288 static int *loop_sound_frame = NULL;
12289 static int *loop_sound_volume = NULL;
12291 void InitPlayLevelSound()
12293 int num_sounds = getSoundListSize();
12295 checked_free(loop_sound_frame);
12296 checked_free(loop_sound_volume);
12298 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12299 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12302 static void PlayLevelSound(int x, int y, int nr)
12304 int sx = SCREENX(x), sy = SCREENY(y);
12305 int volume, stereo_position;
12306 int max_distance = 8;
12307 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12309 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12310 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12313 if (!IN_LEV_FIELD(x, y) ||
12314 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12315 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12318 volume = SOUND_MAX_VOLUME;
12320 if (!IN_SCR_FIELD(sx, sy))
12322 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12323 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12325 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12328 stereo_position = (SOUND_MAX_LEFT +
12329 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12330 (SCR_FIELDX + 2 * max_distance));
12332 if (IS_LOOP_SOUND(nr))
12334 /* This assures that quieter loop sounds do not overwrite louder ones,
12335 while restarting sound volume comparison with each new game frame. */
12337 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12340 loop_sound_volume[nr] = volume;
12341 loop_sound_frame[nr] = FrameCounter;
12344 PlaySoundExt(nr, volume, stereo_position, type);
12347 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12349 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12350 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12351 y < LEVELY(BY1) ? LEVELY(BY1) :
12352 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12356 static void PlayLevelSoundAction(int x, int y, int action)
12358 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12361 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12363 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12365 if (sound_effect != SND_UNDEFINED)
12366 PlayLevelSound(x, y, sound_effect);
12369 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12372 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12374 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12375 PlayLevelSound(x, y, sound_effect);
12378 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12380 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12382 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12383 PlayLevelSound(x, y, sound_effect);
12386 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12388 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12390 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12391 StopSound(sound_effect);
12394 static void PlayLevelMusic()
12396 if (levelset.music[level_nr] != MUS_UNDEFINED)
12397 PlayMusic(levelset.music[level_nr]); /* from config file */
12399 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12402 void RaiseScore(int value)
12404 local_player->score += value;
12406 DrawGameValue_Score(local_player->score);
12409 void RaiseScoreElement(int element)
12414 case EL_BD_DIAMOND:
12415 case EL_EMERALD_YELLOW:
12416 case EL_EMERALD_RED:
12417 case EL_EMERALD_PURPLE:
12418 case EL_SP_INFOTRON:
12419 RaiseScore(level.score[SC_EMERALD]);
12422 RaiseScore(level.score[SC_DIAMOND]);
12425 RaiseScore(level.score[SC_CRYSTAL]);
12428 RaiseScore(level.score[SC_PEARL]);
12431 case EL_BD_BUTTERFLY:
12432 case EL_SP_ELECTRON:
12433 RaiseScore(level.score[SC_BUG]);
12436 case EL_BD_FIREFLY:
12437 case EL_SP_SNIKSNAK:
12438 RaiseScore(level.score[SC_SPACESHIP]);
12441 case EL_DARK_YAMYAM:
12442 RaiseScore(level.score[SC_YAMYAM]);
12445 RaiseScore(level.score[SC_ROBOT]);
12448 RaiseScore(level.score[SC_PACMAN]);
12451 RaiseScore(level.score[SC_NUT]);
12454 case EL_SP_DISK_RED:
12455 case EL_DYNABOMB_INCREASE_NUMBER:
12456 case EL_DYNABOMB_INCREASE_SIZE:
12457 case EL_DYNABOMB_INCREASE_POWER:
12458 RaiseScore(level.score[SC_DYNAMITE]);
12460 case EL_SHIELD_NORMAL:
12461 case EL_SHIELD_DEADLY:
12462 RaiseScore(level.score[SC_SHIELD]);
12464 case EL_EXTRA_TIME:
12465 RaiseScore(level.score[SC_TIME_BONUS]);
12471 RaiseScore(level.score[SC_KEY]);
12474 RaiseScore(element_info[element].collect_score);
12479 void RequestQuitGame(boolean ask_if_really_quit)
12481 if (AllPlayersGone ||
12482 !ask_if_really_quit ||
12483 level_editor_test_game ||
12484 Request("Do you really want to quit the game ?",
12485 REQ_ASK | REQ_STAY_CLOSED))
12487 #if defined(NETWORK_AVALIABLE)
12488 if (options.network)
12489 SendToServer_StopPlaying();
12493 game_status = GAME_MODE_MAIN;
12501 if (tape.playing && tape.deactivate_display)
12502 TapeDeactivateDisplayOff(TRUE);
12505 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12508 if (tape.playing && tape.deactivate_display)
12509 TapeDeactivateDisplayOn();
12516 /* ---------- new game button stuff ---------------------------------------- */
12518 /* graphic position values for game buttons */
12519 #define GAME_BUTTON_XSIZE 30
12520 #define GAME_BUTTON_YSIZE 30
12521 #define GAME_BUTTON_XPOS 5
12522 #define GAME_BUTTON_YPOS 215
12523 #define SOUND_BUTTON_XPOS 5
12524 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12526 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12527 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12528 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12529 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12530 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12531 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12538 } gamebutton_info[NUM_GAME_BUTTONS] =
12541 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12546 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12547 GAME_CTRL_ID_PAUSE,
12551 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12556 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12557 SOUND_CTRL_ID_MUSIC,
12558 "background music on/off"
12561 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12562 SOUND_CTRL_ID_LOOPS,
12563 "sound loops on/off"
12566 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12567 SOUND_CTRL_ID_SIMPLE,
12568 "normal sounds on/off"
12572 void CreateGameButtons()
12576 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12578 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12579 struct GadgetInfo *gi;
12582 unsigned long event_mask;
12583 int gd_xoffset, gd_yoffset;
12584 int gd_x1, gd_x2, gd_y1, gd_y2;
12587 gd_xoffset = gamebutton_info[i].x;
12588 gd_yoffset = gamebutton_info[i].y;
12589 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12590 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12592 if (id == GAME_CTRL_ID_STOP ||
12593 id == GAME_CTRL_ID_PAUSE ||
12594 id == GAME_CTRL_ID_PLAY)
12596 button_type = GD_TYPE_NORMAL_BUTTON;
12598 event_mask = GD_EVENT_RELEASED;
12599 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12600 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12604 button_type = GD_TYPE_CHECK_BUTTON;
12606 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12607 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12608 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12609 event_mask = GD_EVENT_PRESSED;
12610 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12611 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12614 gi = CreateGadget(GDI_CUSTOM_ID, id,
12615 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12616 GDI_X, DX + gd_xoffset,
12617 GDI_Y, DY + gd_yoffset,
12618 GDI_WIDTH, GAME_BUTTON_XSIZE,
12619 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12620 GDI_TYPE, button_type,
12621 GDI_STATE, GD_BUTTON_UNPRESSED,
12622 GDI_CHECKED, checked,
12623 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12624 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12625 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12626 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12627 GDI_EVENT_MASK, event_mask,
12628 GDI_CALLBACK_ACTION, HandleGameButtons,
12632 Error(ERR_EXIT, "cannot create gadget");
12634 game_gadget[id] = gi;
12638 void FreeGameButtons()
12642 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12643 FreeGadget(game_gadget[i]);
12646 static void MapGameButtons()
12650 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12651 MapGadget(game_gadget[i]);
12654 void UnmapGameButtons()
12658 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12659 UnmapGadget(game_gadget[i]);
12662 static void HandleGameButtons(struct GadgetInfo *gi)
12664 int id = gi->custom_id;
12666 if (game_status != GAME_MODE_PLAYING)
12671 case GAME_CTRL_ID_STOP:
12672 RequestQuitGame(TRUE);
12675 case GAME_CTRL_ID_PAUSE:
12676 if (options.network)
12678 #if defined(NETWORK_AVALIABLE)
12680 SendToServer_ContinuePlaying();
12682 SendToServer_PausePlaying();
12686 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12689 case GAME_CTRL_ID_PLAY:
12692 #if defined(NETWORK_AVALIABLE)
12693 if (options.network)
12694 SendToServer_ContinuePlaying();
12698 tape.pausing = FALSE;
12699 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12704 case SOUND_CTRL_ID_MUSIC:
12705 if (setup.sound_music)
12707 setup.sound_music = FALSE;
12710 else if (audio.music_available)
12712 setup.sound = setup.sound_music = TRUE;
12714 SetAudioMode(setup.sound);
12720 case SOUND_CTRL_ID_LOOPS:
12721 if (setup.sound_loops)
12722 setup.sound_loops = FALSE;
12723 else if (audio.loops_available)
12725 setup.sound = setup.sound_loops = TRUE;
12726 SetAudioMode(setup.sound);
12730 case SOUND_CTRL_ID_SIMPLE:
12731 if (setup.sound_simple)
12732 setup.sound_simple = FALSE;
12733 else if (audio.sound_available)
12735 setup.sound = setup.sound_simple = TRUE;
12736 SetAudioMode(setup.sound);